[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(®[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
- 应用层
- HTTP
- Cookie
- Session
- HTTP报文格式
- HTTP的Header字段
- HTTPS
- 简介
- 原理
- RSA加密与解密
- 证书签名与验证
- TLS双向认证
- openssl命令汇总
- DNS
- DNS的记录类型
- DNS的报文格式
- FAQ
- 传输层
- TCP
- CloseWait
- 网络层
- IPv6
- 链路层
- 链接层基础知识
- VLAN
- FAQ
- Linux网络收发包
- 网卡收包
- 网卡发包
- 收发包FAQ
- LVS
- 安装-DR模式
- 基本原理
- Ipvsadm命令
- Netfilter
- Netfilter简介
- 注册钩子函数
- Netfilter中数据包流向
- Iptables的数据结构
- 连接跟踪
- 初识连接跟踪
- 连接跟踪详解
- 连接跟踪数据结构
- 数据包与连接的状态
- NAT
- IPVS
- KubeProxy的IPVS模式
- Linux虚拟网络设备
- 虚拟网络设备简介
- Tap
- VethPair
- Vlan
- Vxlan
- Flannel的VXLAN原理
- Openstack的VXLAN原理
- VXLAN总结
- Bridge
- 给容器设置主机网段IP
- Macvlan
- Ipvlan
- IPIP
- IPIP使用介绍
- IPIP源码分析
- Limdiag网络
- 详细设计
- kubeovn
- IP命令
- Calico
- Calico常见问题
- ARP无响应
- 其他