1、简介
Netfilter是Linux 2.4.x引入的一个子系统,它做为一个通用的、抽象的框架,提供一整套的hook函数的管理机制,使得诸如数据包过滤、网络地址转换(NAT)和基于协议类型的链接跟踪成为了可能。Netfilter在内核中位置以下图所示:linux
Netfilter在netfilter_ipv4.h中命名5个节点,这5个节点是netfilter发挥做用的地方。网络
在数据包流经内核协议栈的整个过程当中,在一些已预约义的关键点上PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT和POST_ROUTING会根据数据包的协议簇PF_INET到这些关键点去查找是否注册有钩子函数。若是没有,则直接返回okfn函数指针所指向的函数继续走协议栈;若是有,则调用nf_hook_slow函数,从而进入到Netfilter框架中去进一步调用已注册在该过滤点下的钩子函数,再根据其返回值来肯定是否继续执行由函数指针okfn所指向的函数。
2、钩子介绍
Netfilter使用NF_HOOK(include/linux/netfilter.h)宏在协议栈内部切入到Netfilter框架中。
一、钩子函数
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)
关于宏NF_HOOK各个参数的解释说明:
1) pf:协议族名,Netfilter架构一样能够用于IP层以外,所以这个变量还能够有诸如PF_INET6,PF_DECnet等名字。
2) hook:HOOK点的名字,对于IP层,就是取上面的五个值;
3) skb:不解释;
4) indev:数据包进来的设备,以struct net_device结构表示;
5) outdev:数据包出去的设备,以struct net_device结构表示;
(后面能够看到,以上五个参数将传递给nf_register_hook中注册的处理函数。)
6) okfn:是个函数指针,当全部的该HOOK点的全部登记函数调用完后,转而走此流程
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \
({int __ret; \
if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh, 1)) == 1)\
__ret = (okfn)(skb); \
__ret;})
NF_HOOK_THRESH宏增长的最后一个参数thresh用来指定经过该宏去遍历钩子函数时的优先级。
static inline int nf_hook_thresh(int pf, unsigned int hook,
struct sk_buff **pskb,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *), int thresh,
int cond)
{
if (!cond)
return 1;
#ifndef CONFIG_NETFILTER_DEBUG
if (list_empty(&nf_hooks[pf][hook]))
return 1;
#endif
return nf_hook_slow(pf, hook, pskb, indev, outdev, okfn, thresh);
}
这个函数又只增长了一个参数cond,该参数为0则放弃遍历,而且也不执行okfn函数;为1则执行nf_hook_slow去完成钩子函数okfn的顺序遍历(优先级从小到大依次执行)。其核心就是nf_hook_slow()函数。该函数本质上作的事情很简单,根据优先级查找双向链表nf_hooks[][],找到对应的回调函数来处理数据包,而后根据其返回值判断是否须要执行okfn函数。
二、HOOK点介绍
从协议栈正常的流程切入到Netfilter框架中,而后顺序、依次去调用每一个HOOK点全部的钩子函数的相关操做有以下几处:
1)net/ipv4/ip_input.c里的ip_rcv函数。该函数主要用来处理网络层的IP报文的入口函数,它到Netfilter框架的切入点为:
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish)
2)net/ipv4/ip_forward.c中的ip_forward函数,它的切入点为:
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,ip_forward_finish);
3)net/ipv4/ip_output.c中的ip_output函数,它切入Netfilter框架的形式为:
NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED));
这里咱们看到切入点从无条件宏NF_HOOK改为了有条件宏NF_HOOK_COND,调用该宏的条件是:若是协议栈当前所处理的数据包skb中没有从新路由的标记,数据包才会进入Netfilter框架。不然直接调用ip_finish_output函数走协议栈去处理。除此以外,有条件宏和无条件宏再无其余任何差别。
4)仍是在net/ipv4/ip_input.c中的ip_local_deliver函数。该函数处理全部目的地址是本机的数据包,其切入函数为:
NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish);
5)net/ipv4/ip_output.c中的ip_push_pending_frames函数。该函数是将IP分片重组成完整的IP报文,而后发送出去。进入Netfilter框架的切入点为:
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);架构