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

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
原文地址:https://www.cnblogs.com/codestack/p/10852948.html