netfilter 源码分析
1, 定义
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
// NF_HOOK()函数宏定义
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)
// NF_HOOK_THRESH()函数宏定义
#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;})
2, 调用关系
2.1 函数调用
//netfilter共定义了5个挂载点,每个挂载点用在数据包接收的不同的阶段,这5个挂载点的调用关系如下:
NF_IP_PRE_ROUTING(ip_rcv)-------->NF_IP_FORWARD(ip_forward)--------->NF_IP_POST_ROUTING(ip_output)
| ^
| |
V |
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
(ip_local_deliver) (ip_local_out)
NF_INET_PRE_ROUTING : 用于在sk_buff成功接收,并对sk的内容进行成功检查后调用:
//ip_rcv()函数中调用:
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
NF_INET_LOCAL_OUT : 在ip包接收到本地后调用:
//ip_local_deliver()函数中的调用:
int ip_local_deliver(struct sk_buff *skb)
return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
//NF_INET_FORWARD: 在对接收的sk_buff包完成路由分类判断是需要进行转发的包进行处理后调用。
//在ip_forward()函数中的调用:
int ip_forward(struct sk_buff *skb)
return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,
ip_forward_finish);
//在对自身发出的包进行处理调用。
// _ip_local_out()函数中的调用:
调用关系:ip_build_and_send_pkt()->ip_local_out()->_ip_local_out()
//int __ip_local_out(struct sk_buff *skb)
return nf_hook(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev,
dst_output);
//在IP栈成功接收sk_buff包后处理调用。
// ip_output()函数中的调用:
int ip_output(struct sk_buff *skb)
return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
3, netfilter 数据结构
3.1 nf_hooks链表
netfilter定义了一个二维的链表头数组struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]来表示所有协议族的各个挂接点,NPROTO值为32,可表示linux所支持所有32个协议族(include/linux/socket.h文件中定义),也就是使用socket(2)函数的第一个参数的值,如互联网的TCP/IP协议族PF_INET(2)。每个协议族有NF_MAX_HOOKS(8)个挂接点,但实际只用了如上所述的5个,数组中每个元素表示一个协议族在一个挂接点的处理链表头,其定义如下:
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS] __read_mostly;
3.2 netfilter 挂载函数注册结构
//netfilter 操作结构
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook; //挂载函数
struct module *owner; //模块
int pf; //pf与hooknum一起索引到特定协议特定编号的挂载函数队列,用于索引nf_hooks
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority; //priority决定在同一队列(pf与hooknum相同)的顺序,priority越小则排列越靠前。
};
//挂载函数的原型
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
4, netfilter的注册
4.1 注册和注销
函数调用关系:
iptable_filter_init()->nf_register_hooks()->nf_register_hook()
//对挂载函数的注册是在nf_register_hook函数中完成的。
int nf_register_hook(struct nf_hook_ops *reg)
{
struct nf_hook_ops *elem;
int err;
err = mutex_lock_interruptible(&nf_hook_mutex);
if (err < 0)
return err;
//从指定pf和hooknum的nf_hooks队列遍历,按priority从小到大顺序,将reg插入相应位置。
list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
if (reg->priority < elem->priority) //要注册的reg要比存在的优先级数字小,放到前面
break;
}
list_add_rcu(®->list, elem->list.prev);
mutex_unlock(&nf_hook_mutex);
return 0;
}
//注销挂载函数
void nf_unregister_hook(struct nf_hook_ops *reg)
{
mutex_lock(&nf_hook_mutex);
list_del_rcu(®->list); //把reg从对应的链表中删除
mutex_unlock(&nf_hook_mutex);
synchronize_net();
}
//向链表中注册n次
int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)
//注销n次
void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n)
4.2 调用netfilter函数
4.2.1 函数调用关系
ip_rcv | NF_HOOK()->NF_HOOK_THRESH()
ip_forward | ---> ->nf_hook_thresh()
... | ->nf_hook_slow()
->nf_iterate()
4.2.2 数据结构
// 函数nf_iterate()函数返回值
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP
NF_ACCEPT: 表示勾子函数允许报文继续向下处理,此时应该继续执行队列上的下一个勾子函数,因为这些勾子函数都是对同一类报文在相同位置的过滤,前一个通后,并不能返回,而要所有函数都执行完,结果仍为NF_ACCEPT时,则可返回它;
NF_REPEAT: 表示要重复执行勾子函数一次;所以勾子函数要编写得当,否则报文会一直执行一个返回NF_REPEAET的勾子函数,当返回值为NF_REPEAT时,不会返回;
NF_STOP : 表示停止执行队列上的勾子函数,直接返回;
NF_DROP: 表示丢弃掉报文;
NF_STOLEN: 表示报文不再往上传递,与NF_DROP不同的是,它没有调用kfree_skb()释放掉skb;
NF_QUEUE: 检查给定协议(pf)是否有队列处理函数,有则进行处理,否则丢掉。
4.2.3 实现
/* Returns 1 if okfn() needs to be executed by the caller,
* -EPERM for NF_DROP, 0 otherwise. */
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
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;
#ifdef CONFIG_NET_NS
struct net *net;
net = indev == NULL ? dev_net(outdev) : dev_net(indev);
if (net != &init_net)
return 1;
#endif
/* We may already have this, but read-locks nest anyway */
rcu_read_lock();
//通过pf协议类型,和hook类型,找到挂载函数结构的链表头
elem = &nf_hooks[pf][hook];
next_hook:
verdict = nf_iterate(&nf_hooks[pf][hook], skb, 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(skb);
ret = -EPERM;
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { //检查是否有队列处理函数,若有调用之
if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
verdict >> NF_VERDICT_BITS))
goto next_hook;
}
unlock:
rcu_read_unlock();
return ret;
}
unsigned int nf_iterate(struct list_head *head,
struct sk_buff *skb,
int hook,
const struct net_device *indev,
const struct net_device *outdev,
struct list_head **i,
int (*okfn)(struct sk_buff *),
int hook_thresh)
{
unsigned int verdict;
/*
* The caller must not block between calls to this
* function because of risk of continuing from deleted element.
*/
list_for_each_continue_rcu(*i, head) {
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
if (hook_thresh > elem->priority)
continue;
/* Optimization: we don't need to hold module
reference here, since function can't sleep. --RR */
verdict = elem->hook(hook, skb, indev, outdev, okfn);
if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
if (unlikely((verdict & NF_VERDICT_MASK)
> NF_MAX_VERDICT)) {
NFDEBUG("Evil return from %p(%u).\n",
elem->hook, hook);
continue;
}
#endif
if (verdict != NF_REPEAT)
return verdict;
*i = (*i)->prev;
}
}
return NF_ACCEPT;
}
#说明
kernel版本2.6.27
阅读(1105) | 评论(0) | 转发(0) |