赞
踩
[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_tpf;
unsigned inthooknum;
/* Hooks are ordered in ascending priority. */
intpriority;
};
```
`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(®[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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。