netfiler框架是在内核协议栈实现的基础上完成的,在报文从网口接收,路由等方法实现基础上使用NF_HOOK调用相应的钩子来进入netfiler框架的处理,如

ip_rcv之后会调用NF_HOOK(NF_IP_PRE_ROUTING)

NF_HOOK宏定义如下:

#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN) #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;}) 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);
}

重点介绍下nf_hook_slow方法。

nf_hook_slow针对调用NF_HOOK时的hook点,遍历所有的钩子函数并判断匹配(match)及完成相应处理(target)。

int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *),
int hook_thresh)
{
struct list_head *elem;
unsigned int verdict;
int ret = 0; /* We may already have this, but read-locks nest anyway */
rcu_read_lock(); elem = &nf_hooks[pf][hook];
next_hook:
verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,
outdev, &elem, okfn, hook_thresh);
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
ret = 1;
goto unlock;
} else if (verdict == NF_DROP) {
kfree_skb(*pskb);
ret = -EPERM;
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn,
verdict >> NF_VERDICT_BITS))
goto next_hook;
}
unlock:
rcu_read_unlock();
return ret;
}

选择hook对应的钩子函数时,使用了 nf_hooks(struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS])二维数组,其中行定义了协议簇类型,最大为NPROTO(33),列定义了不同的hook点,具体内容如下所示(参考netfilter_ipv4.h)。

#define NF_IP_PRE_ROUTING 0

/* If the packet is destined for this box. */

#define NF_IP_LOCAL_IN 1

/* If the packet is destined for another interface. */

#define NF_IP_FORWARD 2

/* Packets coming from a local process. */

#define NF_IP_LOCAL_OUT 3

/* Packets about to hit the wire. */

#define NF_IP_POST_ROUTING 4

下面针对每个HOOK点列举下协议栈中的调用

1. ip_rcv中调用NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish)

网卡接收ip报文后经过pre_routing hook点处理。此hook点一般是用来做DNAT。

2. ip_local_deliver中调用NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish)

ip报文被本地网卡接收后,经过local_in hook点处理

3. ip_forward中调用NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev, ip_forward_finish)

路由查询后不是本地数据,需要做转发处理,经过forward hook点处理

4. ip_push_pending_frames中调用NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,dst_output)

此处理是将ip分片报文重组后送出去

5. ip_output中调用NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,ip_finish_output,!(IPCB(skb)->flags & IPSKB_REROUTED))

如果报文中没有设置IPSKB_REROUTED标志,则报文会过post_routing hook点处理。此hook点一般是用来做SNAT的。

NEXT: 后面会介绍netfilter框架如何注册hook钩子。

05-11 19:29