netfilter-IPv4实现框架分析(一)
基于Linux-2.6.30版本,具体实现net\ipv4\netfilter目录下,入口文件为net\ipv4\netfilter\iptable_filter.c,入口/出口函数为模块的init函数iptable_filter_init()和uninit函数iptable_filter_fini()
iptable_filter_init()函数流程如下
1、register_pernet_subsys(&iptable_filter_net_ops),其作用初步看是用于注册报文匹配目标规则,暂不分析。
2、nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); 该调用既是将filter类型table的hook这侧到netfilter的核心框架中。其中ipt_ops既是记录了具体hook处理实现内容,如下
static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_FILTER,
},
};
以chain INPUT的实现为例,
hook成员即表示具体的hook处理函数,当报文匹配上本规则后,在报文向上层protocol layer上送之前,会被调用。具体见后面进一步分析。
pf即protocol family,表示处理报文的协议类型。
hooknum其实表示本pf下的hook类型,此处处理入方向的报文,与iptables命令工具中INPUT、FORWARD、OUTPUT基本相对应。
priority表示本条chain的优先级。
需要特别注意一下,后续实际注册chain处理规则时,既是利用pf、hooknum、priority将各个chain保存到nf_hooks中的对应位置。后续在netfilter的核心系统中,即根据pf/hooknum查找与之对应的hook,并按照priority指定的优先级一次调用各个hook函数。
现在看看具体filter hook函数是怎么调用的,对net/ipv4/中的代码进行grep,结果如下
[root@arch ipv4]# grep -n NF_HOOK *.c arp.c:: NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit); arp.c:: return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process); ip_forward.c:: return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev, ip_input.c:: return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL, ip_input.c:: return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_output.c:: NF_HOOK(PF_INET, NF_INET_POST_ROUTING, newskb, ip_output.c:: NF_HOOK(PF_INET, NF_INET_POST_ROUTING, newskb, NULL, ip_output.c:: return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, skb->dev, ip_output.c:: return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev, ipmr.c:: NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev, raw.c:: err = NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev, xfrm4_input.c:: NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL, xfrm4_output.c:: return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, [root@arch ipv4]# pwd /root/linux-2.6./net/ipv4 [root@arch ipv4]#
补充说明:NF_HOOK宏既是netfilter系统在报文处理的地方,插入hook的功能宏,其实现如下
#ifdef CONFIG_NETFILTER #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN) #else #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) #endif
在上面的grep结果中,ip_input.c对INPUT方向中,利用NF_HOOK放置的报文处理hook实现为
/*
* Deliver IP Packets to the higher protocol layers.
*/
int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
*/
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
return ;
}
return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
NF_HOOK_THRESH宏实现如下
#ifdef CONFIG_NETFILTER
#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, )) == )\
__ret = (okfn)(skb); \
__ret;})
#endif
可见,协议栈是在将ip报文向上一层的协议处理层上送报文的时刻,调用netfilter的hook函数的,可见若系统没有配置netfilter,NF_HOOK实际将直接调用协议扎自身的上送函数,反之若配置了netfilter,则先经过netfilter处理之后再根据结果做区分处理。
Nf_hook_thresh()的实现又做了进一步区分实现,如下
#ifdef CONFIG_NETFILTER
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *), int thresh,
int cond)
{
if (!cond)
return ;
#ifndef CONFIG_NETFILTER_DEBUG
if (list_empty(&nf_hooks[pf][hook]))
return ;
#endif
return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
}
#else
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *), int thresh,
int cond)
{
return okfn(skb);
}
#endif
可见最终在netfilter系统中,hook调用的入口是nf_hook_slow,其实现很直观,如下
/* Returns 1 if okfn() needs to be executed by the caller,
* -EPERM for NF_DROP, 0 otherwise. */
int nf_hook_slow(u_int8_t 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 = ;
/* We may already have this, but read-locks nest anyway */
rcu_read_lock();
elem = &nf_hooks[pf][hook]; // pf -> NFPROTO_IPV4, NFPROTO_ARP,NFPROTO_BRIDGE,PF_INET etc
next_hook: // hook -> 0~7
verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
outdev, &elem, okfn, hook_thresh);
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
ret = ;
} 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;
}
rcu_read_unlock();
return ret;
}
基本流程就是利用pf/hook在nf_hooks[][]中找到前期注册的hook,然后遍历调用hook中各个处理函数,根据hook处理函数返回结果,对当前报文做区分处理。其实按优先级进行遍历,不体现在调用的地方,而是在注册的地方,调用既是按照优先级排序好的顺序依次调用各个函数而已。nf_iterate()函数实现如下
unsigned int nf_iterate(struct list_head *head,
struct sk_buff *skb,
unsigned 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;
}
注意红色部分,可以看出,仅当是每一个规则返回结果为NF_ACCEPT时,才继续处理本类型hook中下一个优先级的;若本次的hook函数返回NF_REPEAT,则将当前packet的在本次hook函数上再执行依次;其他情况直接返回hook函数执行结果。
Nf_iterate()返回到nf_hook_slow()函数之后,nf_hook_slow即根据执行结果,做区分处理, 若是ACCEPT或STOP,本packet在netfilter处理过程完毕,后续继续调用packet上报函数
若是DROP,本packet将被释放丢弃,不再调用packet上报函数
若包含QUEUE,则将packet送入当前chain对应的队列中,再做对应的处理,在iptables的命令帮助中,说明如下
QUEUE means to pass the packet to userspace. (How the packet can be received by a userspace process differs by the particular queue handler. 2.4.x and 2.6.x kernels up to 2.6.13 include the ip_queue queue handler. Kernels 2.6.14 and later additionally include the nfnetlink_queue queue handler. Packets with a target of QUEUE will be sent to queue number '0' in this case. Please also see the NFQUEUE target as described later in this man page.)
基本的报文处理流程既是如上面所述,接下来是具体过滤处理实现流程,以决定对应对packet的处理结果,以IPv4报文入方向过滤处理为例,具体既是
static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FILTER,
}
...
};
即ipt_local_in_hook函数,其实现既将报文针对之前添加的规则,与packet做匹配检查,并返回预先设置的结果。
netfilter-IPv4实现框架分析(一)的更多相关文章
- Android/Linux下CGroup框架分析及其使用
1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...
- 几款开源的hybird移动app框架分析
几款开源的Hybrid移动app框架分析 Ionic Onsen UI 与 ionic 相比 jQuery Mobile Mobile Angular UI 结论 很多移动开发者喜欢使用原生代码开发, ...
- 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一
作者:唐老师,华清远见嵌入式学院讲师. 1. Sensor的概念 Sensor即传感器,在当前智能手机上大量存在:G-Sensor.LightsSensor. ProximitySensor.Temp ...
- 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析
作者:唐老师,华清远见嵌入式学院讲师. 1. HAL Stub框架分析 HAL stub的框架比较简单,三个结构体.两个常量.一个函数,简称321架构,它的定义在:@hardware/libhardw ...
- openwrt: Makefile 框架分析
openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...
- Android 核心分析 之六 IPC框架分析 Binder,Service,Service manager
IPC框架分析 Binder,Service,Service manager 我首先从宏观的角度观察Binder,Service,Service Manager,并阐述各自的概念.从Linux的概念空 ...
- VS2010/MFC编程入门之四(MFC应用程序框架分析)
VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html 上一讲鸡啄米讲的是VS2010应用 ...
- Yii PHP 框架分析(二)
Yii PHP 框架分析(二)作者:wdy http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html Yii是基于组件( ...
- Yii PHP 框架分析 (一)
Yii PHP 框架分析 (一)作者:wdy http://hi.baidu.com/delphiss/blog/item/f7da86d787adb72506088b4b.html 基于yii1.0 ...
随机推荐
- Linux 日常命令
命令 介绍说明 pwd 打印出当前所在目录 mkdir 创建一个目录 rmdir 删除一个目录 rm 删除文件或目录 -r:删除目录时必须加上 cp 复制一个文件或目录 -r:复制 ...
- C++中虚析构函数的作用
我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数.可是,为什么要这样做呢?下面用一个小例子来说明: 有下面的两个类: class ClxBase { public: ...
- HTTP中的摘要认证机制
引子: 指定和服务器端交互的HTTP方法,URL地址,即其他请求信息: Method:表示http请求方法,一般使用"GET","POST". url:表示请求 ...
- 如何解决EditText使用时,点击外侧系统键盘不消失的bug
在使用viewPager和EditText一起使用的时候,突然出现了一个bug,在点击EditText(此EditText是在ViewPager的Fragment中) 我在切换ViewPager的时候 ...
- Monkey环境配置
安卓APP想要测试稳定性,monkey是最佳选则. 首先搭建monkey的运行环境 在Windows下基于SDK 1.下载SDK for Windows 解压:android-sdk-windows ...
- 0512 Scrum 项目3.0
SCRUM 流程的步骤2: Spring 计划 1. 确保product backlog井然有序.(参考示例图1) 2. Sprint周期,一个冲刺周期,长度定为两周,本学期还有三个冲刺周期. 3. ...
- C++ 高级语法学习与总结(代码实例)
C++11增加了许多的特性,auto就是一个很明显的例子. 还有就是typedid()获取数据变量的类型 看下面简短的代码: atuo: 很像java中的加强for循环..... //获取一个数据 ...
- JSP显示不完全问题
这个问题出现之后其实有点让我手足无措,因为根本不知道原因出在哪儿. 因为出现这个问题之后修改过一次代码,所以我以为是因为这次修改出现的问题. 但细想之下,这次的修改根本没有涉及到任何有关这方面的东西. ...
- app——分享wap站,数据处理页面展示
无意中接到了一个小的工作任务:配合手机app端的分享功能做一个wap站,简言之:将手机app端分享的文章id传过来,利用此id再进行一系列的操作,由于文章分为纯文本,图文以及图集的三种类型的文章,因此 ...
- CodeForces #367 div2 D Trie
题目链接:Vasiliy's Multiset 题意:这里有一个set容器,有三种操作,+ num, - num, ? num,分别代表往容器里加上num,或者拿走num,或着从容器里找一个数temp ...