ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ### **简介** 每个钩子点的钩子函数是以链表的形式进行存储的。链表节点的结构为 ``` struct nf_hook_ops { struct list_head list; /* User fills in from here down. */ nf_hookfn *hook; struct net_device *dev; void *priv; u_int8_t pf; unsigned int hooknum; /* Hooks are ordered in ascending priority. */ int priority; }; ``` `struct list_head`是一个普通的双向链表。`nf_hookfn`用来指向钩子函数的地址。`priority`表示该钩子函数的优先级,该值越小,优先级越高;在这个双向链表中,钩子函数是按照该值进行升序排序的。 注册钩子函数的函数为`nf_register_hooks`,它会将`n`个钩子函数分别注册到对应的钩子处,该函数里面做了一个for循环,调用真正的注册函数`nf_register_hook`。 > net/netfilter/core.c ``` int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n) { ... for (i = 0; i < n; i++) { err = nf_register_hook(&reg[i]); if (err) goto err; } ... } ``` ### **各个模块的钩子函数** 根据上面的分析,注册钩子函数的函数为`nf_register_hooks()`,我们根据这个函数的关键字来搜索conntrack、nat等模块注册的钩子函数。 ##### **Conntrack** 我们在[页面](https://elixir.bootlin.com/linux/v4.4.249/source)上搜索关键字`nf_register_hooks`,会有如下的返回,再根据`conntrack`关键字基本可以定位到conntrack模块注册钩子函数的位置在下面的文件的第472行 ![](https://img.kancloud.cn/fa/83/fa838793adb491fedeaf37be0a012b90_1341x642.png) 我们点开这个文件链接,去到对应位置,发现如下代码 ``` static int __init nf_conntrack_l3proto_ipv4_init(void) { ... ret = nf_register_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); ... } ``` 可以看出,IPv4协议注册的钩子函数在`ipv4_conntrack_ops`这个数组中。我们继续点击这个数组的链接,打开这个数组,如下: ``` /* 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, }, }; ``` 到这里,我们可以看出,Conntrack模块在不同的钩子点注册的钩子函数分别有: | 钩子点 | 钩子函数 | | --- | --- | | NF_INET_PRE_ROUTING | ipv4_conntrack_in() | | NF_INET_LOCAL_OUT | ipv4_conntrack_local() | | NF_INET_POST_ROUTING | ipv4_confirm()、ipv4_helper() | | NF_INET_LOCAL_IN | ipv4_confrim()、ipv4_helper() | ##### **NAT** 根据上面的方法,我们也可以找到nat模块在每个钩子点处注册的钩子函数,如下: > net/ipv4/netfilter/iptable_nat.c ``` static int __init iptable_nat_init(void) { ... err = nf_register_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops)); ... } ``` > net/ipv4/netfilter/iptable_nat.c ``` static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { /* Before packet filtering, change destination */ { .hook = iptable_nat_ipv4_in, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_NAT_DST, }, /* After packet filtering, change source */ { .hook = iptable_nat_ipv4_out, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_NAT_SRC, }, /* Before packet filtering, change destination */ { .hook = iptable_nat_ipv4_local_fn, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_NAT_DST, }, /* After packet filtering, change source */ { .hook = iptable_nat_ipv4_fn, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_NAT_SRC, }, }; ``` | 钩子点 | 钩子函数 | | --- | --- | | NF_INET_PRE_ROUTING | iptable_nat_ipv4_in() | | NF_INET_POST_ROUTING | iptable_nat_ipv4_out() | | NF_INET_LOCAL_OUT | iptable_nat_ipv4_local_fn() | | NF_INET_LOCAL_IN | iptable_nat_ipv4_fn() | ### **参考** http://wiki.dreamrunner.org/public_html/Linux/Networks/netfilter.html https://blog.csdn.net/wenqian1991/article/details/50365689