Linux系统中的Netfilter提供了一个抽象通用化的框架该框架定义的一个子功能的实现就是包过滤子系统框架包含以下五部分
为每种网络协议(IPvIPv等)定义一套钩子函数(IPv定义了个钩子函数)
这些钩子函数在数据报流过协议栈的几个关键点被调用在这几个点中协议栈将把数据报及钩子函数标号作为参数调用Netfilter框架
内核的任何模块可以对每种协议的一个或多个钩子进行注册实现挂接这样当某个数据包被传递给Netfilter框架时内核能检测是否有任何模块对该协议和钩子函数进行了注册若注册了则调用该模块的注册时使用的回调函数这样这些模块就有机会检查(可能还会修改)该数据包丢弃该数据包及指示Netfilter将该数据包传入用户空间的队列
那些排队的数据包是被传递给用户空间的异步地进行处理一个用户进程能检查数据包修改数据包甚至可以重新将该数据包通过离开内核的同一个钩子函数中注入到内核中
任何在IP层要被抛弃的IP数据包在真正抛弃之前都要进行检查例如允许模块检查IPSpoofed包(被路由抛弃)
IP层的五个HOOK点的位置如下所示
()NF_IP_PRE_ROUTING刚刚进入网络层的数据包通过此点(刚刚进行完版本号校验
和等检测)源地址转换在此点进行IP_Inputc中IP_Rcv调用
()NF_IP_LOCAL_IN经路由查找后送往本机的通过此检查点INPUT包过滤在此点进行IP_local_deliver中调用
()NF_IP_FORWARD要转发的包通过此检测点FORWORD包过滤在此点进行
()NF_IP_POST_ROUTING所有马上便要通过网络设备出去的包通过此检测点内置的目的地址转换功能(包括地址伪装)在此点进行
()NF_IP_LOCAL_OUT本机进程发出的包通过此检测点OUTPUT包过滤在此点进行
这些点是已经在内核中定义好的内核模块能够注册在这些HOOK点进行的处理可使用nf_register_hook函数指定在数据报经过这些钩子函数时被调用从而模块可以修改这些数据报并向Netfilter返回如下值
NF_ACCEPT 继续正常传输数据报
NF_DROP 丢弃该数据报不再传输
NF_STOLEN 模块接管该数据报不要继续传输该数据报
NF_QUEUE 对该数据报进行排队(通常用于将数据报给用户空间的进程进行处理)
NF_REPEAT 再次调用该钩子函数
NF_ACCEPT 继续正常传输数据报
NF_DROP 丢弃该数据报不再传输
NF_STOLEN 模块接管该数据报不要继续传输该数据报
NF_QUEUE 对该数据报进行排队(通常用于将数据报给用户空间的进程进行处理)
NF_REPEAT 再次调用该钩子函数
一个基于Netfilter框架的称为IPtables的数据报选择系统在Linux内核中被应用其实它就是IPchains的后继工具但却有更强的可扩展性内核模块可以注册一个新的规则表(table)并要求数据报流经指定的规则表这种数据报选择用于实现数据报过滤(filter表)网络地址转换(Nat表)及数据报处理(Mangle表)
Linux内核提供的这三种数据报处理功能都基于Netfilter的钩子函数和IP表它们是独立的模块相互之间是独立的它们都完美的集成到由Netfileter提供的框架中
包过滤
Filter表格不会对数据报进行修改而只对数据报进行过滤IPtables优于IPchains的一个方面就是它更为小巧和快速它是通过钩子函数NF_IP_LOCAL_INNF_IP_FORWARD及NF_IP_LOCAL_OUT接入Netfilter框架的因此对于任何一个数
报只有一个地方对其进行过滤这相对IPchains来说是一个巨大的改进因为在IPchains中一个被转发的数据报会遍历三条链
NAT
NAT表格监听三个Netfilter钩子函数NF_IP_PRE_ROUTINGNF_IP_POST_ROUTING及NF_IP_LOCAL_OUT
NF_IP_PRE_ROUTING实现对需要转发的数据报的源地址进行地址转换而NF_IP_POST_ROUTING则对需要转发的数据包的目的地址进行地址转换对于本地数据报的目的地址的转换则由NF_IP_LOCAL_OUT来实现NAT表格不同于filter表格因为只有新连接的第一个数据报将遍历表格而随后的数据报将根据第一个数据报的结果进行同样的转换处理NAT表格被用在源NAT目的NAT伪装(其是源NAT的一个特例)及透明代理(其是目的NAT的一个特例)
数据报处理(Packet Mangling)
Mangle表格在NF_IP_PRE_ROUTING和NF_IP_LOCAL_OUT钩子中进行注册使用
mangle表可以实现对数据报的修改或给数据报附上一些带外数据当前mangle表支持修改TOS位及设置skb的nfmard字段
源码分析
如果我们想加入自己的代码便要用nf_register_hook函数其函数原型为
int nf_register_hook(struct nf_hook_ops *reg)
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority */
int priority;
};