zoukankan      html  css  js  c++  java
  • Netfilter 之 连接跟踪的helper

    注册helper

    nf_conntrack_ftp_init是连接跟踪ftp模块的初始化函数,可以看到其调用了nf_conntrack_helpers_register来注册helper;

     1 static int __init nf_conntrack_ftp_init(void)
     2 {
     3     int i, ret = 0;
     4 
     5     NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master));
     6 
     7     ftp_buffer = kmalloc(65536, GFP_KERNEL);
     8     if (!ftp_buffer)
     9         return -ENOMEM;
    10 
    11     if (ports_c == 0)
    12         ports[ports_c++] = FTP_PORT;
    13 
    14     /* FIXME should be configurable whether IPv4 and IPv6 FTP connections
    15          are tracked or not - YK */
    16     /* 初始化helper */
    17     for (i = 0; i < ports_c; i++) {
    18         nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_TCP, "ftp",
    19                   FTP_PORT, ports[i], ports[i], &ftp_exp_policy,
    20                   0, help, nf_ct_ftp_from_nlattr, THIS_MODULE);
    21         nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_TCP, "ftp",
    22                   FTP_PORT, ports[i], ports[i], &ftp_exp_policy,
    23                   0, help, nf_ct_ftp_from_nlattr, THIS_MODULE);
    24     }
    25 
    26     /* 注册helper */
    27     ret = nf_conntrack_helpers_register(ftp, ports_c * 2);
    28     if (ret < 0) {
    29         pr_err("failed to register helpers
    ");
    30         kfree(ftp_buffer);
    31         return ret;
    32     }
    33 
    34     return 0;
    35 }

    模块通过调用nf_conntrack_helper_register函数将helper添加到对应的hash中;函数首先会对是否有相同匹配的helper进行检查,不存在才会存放到hash中;

     1 int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
     2 {
     3     struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
     4     unsigned int h = helper_hash(&me->tuple);
     5     struct nf_conntrack_helper *cur;
     6     int ret = 0, i;
     7 
     8     BUG_ON(me->expect_policy == NULL);
     9     BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
    10     BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
    11 
    12     /* 允许的最大期望连接超额 */
    13     if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
    14         return -EINVAL;
    15 
    16     mutex_lock(&nf_ct_helper_mutex);
    17     /* 遍历helper hash,查找是否已存在,条件(名称相同 &&(三层协议为指定||三层协议相同)&&四层协议相同 */
    18     for (i = 0; i < nf_ct_helper_hsize; i++) {
    19         hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
    20             if (!strcmp(cur->name, me->name) && /* 名称相同 */
    21                 (cur->tuple.src.l3num == NFPROTO_UNSPEC || /* 三层协议未指定 */
    22                  cur->tuple.src.l3num == me->tuple.src.l3num) && /* 三层协议相同 */
    23                 cur->tuple.dst.protonum == me->tuple.dst.protonum) { /* 四层协议相同 */
    24                 ret = -EEXIST;
    25                 goto out;
    26             }
    27         }
    28     }
    29 
    30     /* avoid unpredictable behaviour for auto_assign_helper */
    31     /* 不是userspace,遍历hash,进行tuple和掩码的比较,查看是否是已存在 */
    32     if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
    33         hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
    34             if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
    35                              &mask)) {
    36                 ret = -EEXIST;
    37                 goto out;
    38             }
    39         }
    40     }
    41 
    42     /* 将helper加入到helper hash */
    43     refcount_set(&me->refcnt, 1);
    44     hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
    45     nf_ct_helper_count++;
    46 out:
    47     mutex_unlock(&nf_ct_helper_mutex);
    48     return ret;
    49 }
    关联helper到conntrack

    在有新数据包进入的时候,如果没有对应连接跟踪,需要调用init_conntrack新建一个连接跟踪,其中会设置连接跟踪的helper;

     1 static int
     2 resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
     3           struct sk_buff *skb,
     4           unsigned int dataoff,
     5           u_int16_t l3num,
     6           u_int8_t protonum,
     7           struct nf_conntrack_l3proto *l3proto,
     8           struct nf_conntrack_l4proto *l4proto)
     9 {
    10     const struct nf_conntrack_zone *zone;
    11     struct nf_conntrack_tuple tuple;
    12     struct nf_conntrack_tuple_hash *h;
    13     enum ip_conntrack_info ctinfo;
    14     struct nf_conntrack_zone tmp;
    15     struct nf_conn *ct;
    16     u32 hash;
    17 
    18     /* 将源目的地址端口协议方向等字段设置到tuple */
    19     if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
    20                  dataoff, l3num, protonum, net, &tuple, l3proto,
    21                  l4proto)) {
    22         pr_debug("Can't get tuple
    ");
    23         return 0;
    24     }
    25 
    26     /* look for tuple match */
    27     /* 从hash中查找tuple */
    28     zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
    29     hash = hash_conntrack_raw(&tuple, net);
    30     h = __nf_conntrack_find_get(net, zone, &tuple, hash);
    31 
    32     /* 未找到该tuple */
    33     if (!h) {
    34         /* 创建一个节点 */
    35         h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
    36                    skb, dataoff, hash);
    37         if (!h)
    38             return 0;
    39         if (IS_ERR(h))
    40             return PTR_ERR(h);
    41     }
    42 
    43     /* 获取到nf_conn */
    44     ct = nf_ct_tuplehash_to_ctrack(h);
    45 
    46     /* It exists; we have (non-exclusive) reference. */
    47     /* 应答方向,已建立连接应答 */
    48     if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {
    49         ctinfo = IP_CT_ESTABLISHED_REPLY;
    50     } 
    51     /* 原始方向 */
    52     else {
    53         /* Once we've had two way comms, always ESTABLISHED. */
    54         /* 已经见过应答了,那么是已连接状态 */
    55         if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
    56             pr_debug("normal packet for %p
    ", ct);
    57             ctinfo = IP_CT_ESTABLISHED;
    58         } 
    59         /* 有期望连接标记,则设置关联字段 */
    60         else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
    61             pr_debug("related packet for %p
    ", ct);
    62             ctinfo = IP_CT_RELATED;
    63         } 
    64         /* 其他情况,新连接 */
    65         else {
    66             pr_debug("new packet for %p
    ", ct);
    67             ctinfo = IP_CT_NEW;
    68         }
    69     }
    70 
    71     /* skb关联nf_conn */
    72     nf_ct_set(skb, ct, ctinfo);
    73     return 0;
    74 }

    init_conntrack函数调用__nf_ct_try_assign_helper来对helper进行赋值;

     1 static noinline struct nf_conntrack_tuple_hash *
     2 init_conntrack(struct net *net, struct nf_conn *tmpl,
     3            const struct nf_conntrack_tuple *tuple,
     4            struct nf_conntrack_l3proto *l3proto,
     5            struct nf_conntrack_l4proto *l4proto,
     6            struct sk_buff *skb,
     7            unsigned int dataoff, u32 hash)
     8 {
     9         /*...*/
    10 
    11     /* 尝试设置helper */
    12     if (!exp)
    13         __nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);
    14 
    15     /*...*/
    16 
    17     return &ct->tuplehash[IP_CT_DIR_ORIGINAL];
    18 }

    __nf_ct_try_assign_helper函数完成对helper的设置,其中在helper为空的时候调用nf_ct_lookup_helper查找helper;

     1 int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
     2                   gfp_t flags)
     3 {
     4     struct nf_conntrack_helper *helper = NULL;
     5     struct nf_conn_help *help;
     6     struct net *net = nf_ct_net(ct);
     7 
     8     /* We already got a helper explicitly attached. The function
     9      * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
    10      * the helper up again. Since now the user is in full control of
    11      * making consistent helper configurations, skip this automatic
    12      * re-lookup, otherwise we'll lose the helper.
    13      */
    14     if (test_bit(IPS_HELPER_BIT, &ct->status))
    15         return 0;
    16 
    17     /* 原关联存在,记录helper为原关联的helper */
    18     if (tmpl != NULL) {
    19         help = nfct_help(tmpl);
    20         if (help != NULL) {
    21             helper = help->helper;
    22             set_bit(IPS_HELPER_BIT, &ct->status);
    23         }
    24     }
    25 
    26     /* 新连接跟踪的help */
    27     help = nfct_help(ct);
    28 
    29     /* helper为空 */
    30     if (helper == NULL) {
    31         /* 根据tuple和mask查找helper */
    32         helper = nf_ct_lookup_helper(ct, net);
    33 
    34         /* 没找到,赋值为NULL */
    35         if (helper == NULL) {
    36             if (help)
    37                 RCU_INIT_POINTER(help->helper, NULL);
    38             return 0;
    39         }
    40     }
    41 
    42     /* help为空 */
    43     if (help == NULL) {
    44         /* 为连接跟踪添加help扩展 */
    45         help = nf_ct_helper_ext_add(ct, helper, flags);
    46         if (help == NULL)
    47             return -ENOMEM;
    48     } 
    49     /* 扩展不为空 */
    50     else {
    51         /* We only allow helper re-assignment of the same sort since
    52          * we cannot reallocate the helper extension area.
    53          */
    54         struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
    55 
    56         /* 已有的help和新的helper所属的help不是同一个扩展help */
    57         if (tmp && tmp->help != helper->help) {
    58             RCU_INIT_POINTER(help->helper, NULL);
    59             return 0;
    60         }
    61     }
    62 
    63     /* 设置helper */
    64     rcu_assign_pointer(help->helper, helper);
    65 
    66     return 0;
    67 }
     1 static struct nf_conntrack_helper *
     2 nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
     3 {
     4     if (!net->ct.sysctl_auto_assign_helper) {
     5         if (net->ct.auto_assign_helper_warned)
     6             return NULL;
     7         if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
     8             return NULL;
     9         pr_info("nf_conntrack: default automatic helper assignment "
    10             "has been turned off for security reasons and CT-based "
    11             " firewall rule not found. Use the iptables CT target "
    12             "to attach helpers instead.
    ");
    13         net->ct.auto_assign_helper_warned = 1;
    14         return NULL;
    15     }
    16 
    17     /* 根据tuple查找 */
    18     return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
    19 }

    __nf_ct_helper_find会遍历第一部分讲到的保存已注册helper的hash表,通过tuple和mask来查找对应helper;

     1 static struct nf_conntrack_helper *
     2 __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
     3 {
     4     struct nf_conntrack_helper *helper;
     5     struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
     6     unsigned int h;
     7 
     8     if (!nf_ct_helper_count)
     9         return NULL;
    10 
    11     h = helper_hash(tuple);
    12 
    13     /* 遍历对应hash,根据tuple和mask查找helper */
    14     hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
    15         if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
    16             return helper;
    17     }
    18     return NULL;
    19 }

    对比过程,在三层地址,四层端口,协议均相同的情况,认为找到helper;

     1 static inline bool
     2 nf_ct_tuple_src_mask_cmp(const struct nf_conntrack_tuple *t1,
     3              const struct nf_conntrack_tuple *t2,
     4              const struct nf_conntrack_tuple_mask *mask)
     5 {
     6     int count;
     7 
     8     /* 判断三层地址是否相同 */
     9     for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++) {
    10         if ((t1->src.u3.all[count] ^ t2->src.u3.all[count]) &
    11             mask->src.u3.all[count])
    12             return false;
    13     }
    14 
    15     /* 判断四层端口是否相同 */
    16     if ((t1->src.u.all ^ t2->src.u.all) & mask->src.u.all)
    17         return false;
    18 
    19     /* 判断协议是否相同 */
    20     if (t1->src.l3num != t2->src.l3num ||
    21         t1->dst.protonum != t2->dst.protonum)
    22         return false;
    23 
    24 
    25     /* 地址+端口+协议都相同,已存在,返回true */
    26     return true;
    27 }
    调用helper

    在连接跟踪的ipv4_helper钩子函数中,会查找连接跟踪的对应的helper,并执行helper的help函数完成扩展功能;

  • 相关阅读:
    skywalking物理拓扑图
    检测HTTPS证书到期时间
    docker zipkin 链路监控
    定时任务和任务-示例
    小程序列表循环出来的list是不同接口赋的值
    小程序编辑器报-50003错误肿么办?
    uni-app小程序组建
    2019
    春日
    Mac安装软件提示破损
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11755777.html
Copyright © 2011-2022 走看看