I am definitely no kernel guru and the information provided by this document may be wrong. So don’t expect too much, I’ll always appreciate Your comments and bugfixes.
Netfilter
Netfilter是Linux 2.4内核的一个子系统,Netfiler使得诸如数据包过滤、网络地址转换(NAT)以及网络连接跟踪等技巧成为可能,这些功能仅通过使用内核网络代码提供的各式各样的hook既可以完成。这些hook位于内核代码中,要么是静态链接的,要么是以动态加载的模块的形式存在。可以为指定的网络事件注册相应的回调函数,数据包的接收就是这样一个例子。
Netfilter 中定义了五个关于 IPv4 的 hook,对这些符号的声明可以在 netfilter_ipv4.h 中找到,可用的 IP HOOK 如下:
#define NF_IP_PRE_ROUTING 0 // 在进行完整性检查之后,可以截获接收的所有报文,包括目的地址是自己的报文和需要转发的报文;目的IP地址转换在此点
#define NF_IP_LOCAL_IN 1 // 路由决策后,可以截获目的地址是自己的报文,INPUT 包过滤在这里进行
#define NF_IP_FORWARD 2 // 截获所有转发的报文,FORWARD 在这里进行过滤
#define NF_IP_LOCAL_OUT 3 // 可以截获自身发出的所有报文(不包括转发),OUTPUT 过滤在这里进行
#define NF_IP_POST_ROUTING 4 // 可以截获发送的所有报文,包括自身发出的报文和转发的报文
在 hook 函数完成了对数据包所需的任何的操作之后,它们必须返回下列预定义的 Netfilter 返回值中的一个:
#define NF_DROP 0 // 丢弃数据包,不在继续
#define NF_ACCEPT 1 // 正常传输报文
#define NF_STOLEN 2 // Netfilter 模块接管该报文,不再继续传输
#define NF_QUEUE 3 // 对该数据报进行排队,通常用于将数据报提交给用户空间进程处理
#define NF_REPEAT 4 // 再次调用该钩子函数
#define NF_STOP 5 // 继续正常传输报文
Note
:NF_ACCEPT 和 NF_STOP 都表示报文通过了检查,可以正常向下流通。
NF_ACCEPT
表示报文通过了某个HOOK
函数的处理,下一个HOOK
函数可以接着处理了NF_STOP
表示报文通过了某个HOOK
函数的处理,后面的HOOK
函数你们就不要处理了
注册和注销Netfilter hook
注册一个 hook 函数是围绕 nf_hook_ops 数据结构,数据结构的定义如下:
// https://elixir.bootlin.com/linux/v5.11.2/source/include/linux/netfilter.h#L77
typedef unsigned int nf_hookfn(void *priv, // priv:私有数据
struct sk_buff *skb, // 正在处理的报文
const struct nf_hook_state *state); // 将相关参数都将存储到 state 中
struct nf_hook_ops {
nf_hookfn *hook; // hook 函数
struct net_device *dev; // 设备
void *priv; //
u_int8_t pf; // 协议族
unsigned int hooknum; // hook 触发点的编号
int priority; // 优先级
};
struct nf_hook_state {
unsigned int hook;
u_int8_t pf;
struct net_device *in; // 用于描述数据包到达的接口
struct net_device *out; // 用于描述数据包离开的接口
struct sock *sk;
struct net *net;
int (*okfn)(struct net *, struct sock *, struct sk_buff *);
};
// 参数 in 只用于NF_IP_PRE_ROUTING和NF_IP_LOCAL_IN,
// 参数out只用于NF_IP_LOCAL_OUT和NF_IP_POST_ROUTING
注册一个 Netfilter hook
需要调用 nf_register_net_hook() 函数,以及用到一个 nf_hook_ops
数据结构。nf_register_net_hook()
函数以一个 nf_hook_ops
数据结构的地址作为参数并且返回一个整型的值。 代码如下:
// https://elixir.bootlin.com/linux/v5.11.2/source/net/netfilter/core.c#L557
int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
unsigned int n)
{
...
for (i = 0; i < n; i++) {
err = nf_register_net_hook(net, ®[i]); // 注册 hook 函数
if (err)
goto err; // 注册失败
}
return 0;
err:
if (i > 0) // 注销 hook 函数
nf_unregister_net_hooks(net, reg, i);
return err;
}