1. netfilter简介
netfilter是在Linux内核中一组钩子,这些钩子允许在网络协议栈中使用内核模块来注册回调函数,可以处理协议栈中经过钩子的每一个报文。因此我们可以利用它来实现很多功能,如过滤报文,修改报文等。
和netfilter常常出现在一起的还有iptable,它是在netfilter基础上在Linux内核中内置的防火墙架构,它工作在用户空间,根据我们配置的规则集工作,每一个规则都由一些匹配(match)和动作(target)组成。
2. netfilter编程
2.1 netfilter钩子
如图1所示,netfiter提供的钩子分两部分,一部分是接收网络中的数据,另一部分是发送数据到网络中去。
图1
-
NF_IP_PRE_ROUTING (0)
从网络中接收到的,进行完正确性检查的数据报文。经过这个钩子后,对报文进行路由检查,判断是进入本机还是转发。 -
NF_IP_LOCAL_IN (1)
对目的地是本机的报文,会经过这个钩子。 -
NF_IP_FORWARD (2)
而对需要转发的数据报文,则会经过这个钩子。(组播报文最终也会经过这个钩子?) -
NF_IP_LOCAL_OUT (3)
本地发出的报文做发送路由之前经过的钩子。 -
NF_IP_POST_ROUTING (4)
所有将要发出的报文都会通过此钩子。
2.2 netfilter对数据包处理方式
netfilter的钩子函数返回值有5种,分别如下:
- NF_ACCEPT:继续处理数据包;
- NF_DROP:丢弃此包,不再继续处理;
- NF_STOLEN:表示此包已经被处理过,不需继续处理;
- NF_QUERE:队列化数据包(通常是为用户空间处理做准备);
- NF_REPEAT:再一次调用这个钩子函数。
做屏蔽数据包处理时常常会使用NF_ACCEPT,NF_DROP,而拷贝数据包时常常会用到NF_STOLEN。
2.3 注册和注销钩子
2.3.1 NF_HOOK宏
netfilter在协议栈处理过程中在注册的钩子处调用NF_HOOK()宏来插入回调函数的。
在3.2.0-26内核中:
如果配置了netfilter则调用nf_hook_thresh,并在返回NF_ACCEPT的情况下,调用okfn。
static inline int
NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb,
struct net_device *in, struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN);
}
否则,直接调用okfn,
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
okfn是一个函数指针,是从钩子函数返回协议栈时继续要执行的函数。在ip_rcv()函数中调用时设置的ip_rcv_finish。
2.3.2 nf_hook_ops
在内核中操作网络数据的模块的注册与注销。
nf_hook_ops结构体:
struct nf_hook_ops {
/* 用户从这里开始向下填 */
struct list_head list;
/* 用户从这里开始向下填 */
/* hook为钩子处理函数*/
nf_hookfn *hook;
/* 模块所有者 */
struct module *owner;
/* 协议簇 */
u_int8_t pf;
/* 2.1中5个挂载点之一 */
unsigned int hooknum;
/* 优先级,依据升序来排列钩子 */
int priority;
};
注册和注销函数:
int nf_register_hook(struct nf_hook_ops *reg);
void nf_unregister_hook(struct nf_hook_ops *reg);
int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n);
void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n);
例如:
/*Initialize the hook*/
static struct nf_hook_ops nf_out_modify =
{
.hook = ip4_multi_modify,
.hooknum = NF_IP_POST_ROUTING,
.pf = PF_INET,
.priority = NF_IP_PRI_FIRST,
};
// static
static int __init hello_init(void)
{
nf_register_hook(&nf_out_modify);
return 0;
}
static void __exit hello_exit(void)
{
nf_unregister_hook(&nf_out_modify);
}
/*module initial and exit*/
module_init(hello_init);
module_exit(hello_exit);
2.3.3 nf_sockopt_ops
socket选项控制的钩子函数的注册与注销。(TODO)
3. IPv4与IPv6的区别
1. nf_hook_ops里面的协议簇不同,IPv4的是PF_INET,而IPv6的是PF_INET6.
2. nf_hook_ops中的priority和hooknum针对IPv6的在netfilter_ipv6.h里也有定义。
3. 取得报文header处理不同。
IPv4:ip_hdr(skb)
IPv6:ipv6_hdr(skb)
参考:
[2] 宋敬彬,孙海滨等,《Linux网络编程》
[3] http://blog.csdn.net/efan_linux/article/details/4605073