linux nf_conntrack 連接跟蹤機制 2


連接跟蹤初始化

基礎參數的初始化:nf_conntrack_standalone_init 會調用nf_conntrack_init_start 完成連接跟蹤基礎參數的初始化, hash slab 擴展項 等;

nf_conntrack_l3proto_ipv4_init 函數初始化了協議和tuple操作函數的相關初始化;

 

 
         
static int ipv4_net_init(struct net *net)
{
    int ret = 0;
/*
注冊了和 IPv4 相關的幾個 4 層 TCP、UDP、ICMP等協議
3個l4proto與1個l3proto在pernet的初始化
*/
    ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4);
    if (ret < 0) {
        pr_err("nf_conntrack_tcp4: pernet registration failed\n");
        goto out_tcp;
    }
    ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4);
    if (ret < 0) {
        pr_err("nf_conntrack_udp4: pernet registration failed\n");
        goto out_udp;
    }
    ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp);
    if (ret < 0) {
        pr_err("nf_conntrack_icmp4: pernet registration failed\n");
        goto out_icmp;
    }
    ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4);
    if (ret < 0) {
        pr_err("nf_conntrack_ipv4: pernet registration failed\n");
        goto out_ipv4;
    }
    return 0;
out_ipv4:
    nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
out_icmp:
    nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
out_udp:
    nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
out_tcp:
    return ret;
}
 
         

 


static
int __init nf_conntrack_l3proto_ipv4_init(void) { int ret = 0; need_conntrack(); nf_defrag_ipv4_enable(); /* 用戶態與內核態交互通信的方法sockopt,寫法也簡單. 缺點就是使用 copy_from_user()/copy_to_user()完成內核和用戶的通信, 效率其實不高, 多用在傳遞控制 選項 信息,不適合做大量的數據傳輸 */ ret = nf_register_sockopt(&so_getorigdst); if (ret < 0) { pr_err("Unable to register netfilter socket option\n"); return ret; } /**調用ipv4_net_init 完成相關初始化*/ ret = register_pernet_subsys(&ipv4_net_ops); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register pernet ops\n"); goto cleanup_sockopt; } /*注冊hook */ ret = nf_register_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register hooks.\n"); goto cleanup_pernet; } /* nf_conntrack_l4proto tcp相關初始化 */ ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n"); goto cleanup_hooks; } /* nf_conntrack_l4proto udp相關初始化 */ ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n"); goto cleanup_tcp4; } /* nf_conntrack_l4proto icmp相關初始化 */ ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n"); goto cleanup_udp4; } /* nf_conntrack_l3proto ip相關初始化 */ ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n"); goto cleanup_icmpv4; } return ret; }

nf_conntrack_l3proto_ipv4  結構中包含了基礎信息 tuple 鈎子回調函數;

struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
    .l3proto     = PF_INET,
    .name         = "ipv4",
    .pkt_to_tuple     = ipv4_pkt_to_tuple,
    .invert_tuple     = ipv4_invert_tuple,
    .print_tuple     = ipv4_print_tuple,
    .get_l4proto     = ipv4_get_l4proto,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
    .tuple_to_nlattr = ipv4_tuple_to_nlattr,
    .nlattr_tuple_size = ipv4_nlattr_tuple_size,
    .nlattr_to_tuple = ipv4_nlattr_to_tuple,
    .nla_policy     = ipv4_nla_policy,
#endif
#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
    .ctl_table_path  = "net/ipv4/netfilter",
#endif
    .init_net     = ipv4_init_net,
    .me         = THIS_MODULE,
};
static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
                  struct nf_conntrack_tuple *tuple)
{/* 從ip頭中獲取源目的地址,存入tuple */
    const __be32 *ap;
    __be32 _addrs[2];
    ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr),
                sizeof(u_int32_t) * 2, _addrs);
    if (ap == NULL)
        return false;

    tuple->src.u3.ip = ap[0];
    tuple->dst.u3.ip = ap[1];

    return true;
}

static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
                  const struct nf_conntrack_tuple *orig)
{
    tuple->src.u3.ip = orig->dst.u3.ip;
    tuple->dst.u3.ip = orig->src.u3.ip;
/* 根據原tuple地址設置新tuple,源目的地址均相反 */
    return true;
}

static void ipv4_print_tuple(struct seq_file *s,
                const struct nf_conntrack_tuple *tuple)
{
    seq_printf(s, "src=%pI4 dst=%pI4 ",
           &tuple->src.u3.ip, &tuple->dst.u3.ip);
}

static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
                unsigned int *dataoff, u_int8_t *protonum)
{
    const struct iphdr *iph;
    struct iphdr _iph;
/* 獲取ip頭中的協議 */
    iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
    if (iph == NULL)
        return -NF_ACCEPT;

    /* Conntrack defragments packets, we might still see fragments
     * inside ICMP packets though. */
    if (iph->frag_off & htons(IP_OFFSET))
        return -NF_ACCEPT;

    *dataoff = nhoff + (iph->ihl << 2);
    *protonum = iph->protocol;

    /* Check bogus IP headers */
    if (*dataoff > skb->len) {
        pr_debug("nf_conntrack_ipv4: bogus IPv4 packet: "
             "nhoff %u, ihl %u, skblen %u\n",
             nhoff, iph->ihl << 2, skb->len);
        return -NF_ACCEPT;
    }

    return NF_ACCEPT;
}

 

 

 

contrack _in helper confirm 等鈎子函數的注冊

* Connection tracking may drop packets, but never alters them, so
   make it the first hook. */
static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
    {
        .hook        = ipv4_conntrack_in,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_PRE_ROUTING,
        .priority    = NF_IP_PRI_CONNTRACK,
    },
    {
        .hook        = ipv4_conntrack_local,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_OUT,
        .priority    = NF_IP_PRI_CONNTRACK,
    },
    {
        .hook        = ipv4_helper,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_POST_ROUTING,
        .priority    = NF_IP_PRI_CONNTRACK_HELPER,
    },
    {
        .hook        = ipv4_confirm,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_POST_ROUTING,
        .priority    = NF_IP_PRI_CONNTRACK_CONFIRM,
    },
    {
        .hook        = ipv4_helper,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_IN,
        .priority    = NF_IP_PRI_CONNTRACK_HELPER,
    },
    {
        .hook        = ipv4_confirm,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_IN,
        .priority    = NF_IP_PRI_CONNTRACK_CONFIRM,
    },
};

 

基於ip層的協議回實現自己的nf_contrack_l4proto,現在以udp為例

das

struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly =
{
    .l3proto        = PF_INET,
    .l4proto        = IPPROTO_UDP,
    .name            = "udp",
    .allow_clash        = true,
    .pkt_to_tuple        = udp_pkt_to_tuple,
    .invert_tuple        = udp_invert_tuple,
    .print_tuple        = udp_print_tuple,
    .packet            = udp_packet,
    .get_timeouts        = udp_get_timeouts,
    .new            = udp_new,
    .error            = udp_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
    .tuple_to_nlattr    = nf_ct_port_tuple_to_nlattr,
    .nlattr_to_tuple    = nf_ct_port_nlattr_to_tuple,
    .nlattr_tuple_size    = nf_ct_port_nlattr_tuple_size,
    .nla_policy        = nf_ct_port_nla_policy,
#endif
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
    .ctnl_timeout        = {
        .nlattr_to_obj    = udp_timeout_nlattr_to_obj,
        .obj_to_nlattr    = udp_timeout_obj_to_nlattr,
        .nlattr_max    = CTA_TIMEOUT_UDP_MAX,
        .obj_size    = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX,
        .nla_policy    = udp_timeout_nla_policy,
    },
#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
    .init_net        = udp_init_net,
    .get_net_proto        = udp_get_net_proto,
};
static bool udp_pkt_to_tuple(const struct sk_buff *skb,
                 unsigned int dataoff,
                 struct net *net,
                 struct nf_conntrack_tuple *tuple)
{
    const struct udphdr *hp;
    struct udphdr _hdr;
/*獲取四冊協議對應元祖信息*/
    /* Actually only need first 8 bytes. */
    hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
    if (hp == NULL)
        return false;

    tuple->src.u.udp.port = hp->source;
    tuple->dst.u.udp.port = hp->dest;

    return true;
}
 
         

 

 

 其注冊L3 L4回調信息 后結構示意圖如下所示:

在netfilter框架中利用nf_register_hook(struct nf_hook_ops *reg)、nf_unregister_hook(struct nf_hook_ops *reg)函數注冊自己的鈎子項;

 

 上圖來自:http://bbs.chinaunix.net/thread-4082396-1-1.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM