Netfilter/IPTables
Linux 2.4 版本以上新加入的防火墙机制
Netfilter与IP协议栈是无缝契合的,工具模块IPTables从用户态的iptables连接到内核态的Netfilter的架构中
主要功能
1.数据报过滤
2.链接跟踪 (一条tcp会话)
3.网络地址转换(NAT)
4.数据报修改(mangle)
可以这么说,Netfilter是表的容器,表是链的容器,链是规则的容器,最终形成对数据报处理规则的实现。
以下的绿底图,都是复制的别人的
每个链挂有一系列规则,链称为钩子点
链的实际拓扑是下面这样的,
他说明了三种 数据流向
1.pre -> local_in -> 上层
2.pre -> forward -> post
3.上层 -> local_out ->post
理解这个流向很重要,因为刚开始接触的时候很可能以为
pre -> local_in ->上层 ->local_out ->post 是一条 ,这个认知会为理解nf带来很大阻碍
对于数据的实际处理,是在链上的规则(钩子函数nf_hook_ops)内进行的
经过规则的判断,会返回一个值,告诉ntfilter 应该 丢掉这个包还是放行,或者其他操作
具体的返回值,及动作可以再去查看
上面的链 拓扑看起来是 一个平面的, 实际上, netfilter 是一个三维的结构,更高一维是协议
,每选择一种协议, 对应的二维平面,都是像上面的图展示的一样,只不过,里面的规则会有差别
NF_HOOK 是一个宏,指明了 要调用哪一个协议层的哪个钩子点,
使用 NF_HOOK 可以将流程切入到netfilter之中 在头文件(include/linux/netfilter.h)
#define NF_HOOK(pf, hook, skb, indev, outdev,okfn)
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)
调用
NF_HOOK(刚才所说的协议, 钩子,skb(数据),输入设备, 输出设备,处理走完钩子状态的回调函数)
最后一个参数指定通过该宏去遍历钩子函数时的优先级,越小越优先
NF_HOOK_THRESH宏只增加了一个thresh参数,这个参数就是用来指定通过该宏去遍历钩子函数时的优先级,同时,该宏内部又调用了nf_hook_thresh函数:
static inline int nf_hook_thresh(int pf, unsignedint 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[][] 上
pre钩子 在ip_rcv()调用
(优先级顺序):Conntrack(-200)、mangle(-150)、DNAT(-100)
主要是对数据报作报头检测处理,以捕获异常情况。
local_in 钩子 ,在ip_local_deliver()调用
(优先级顺序):mangle(-150)、filter(0)、SNAT(100)、Conntrack(INT_MAX-1)
防火墙一般建立在这个HOOK上。
forward钩子 ,在ip_forward()调用
(优先级顺序)mangle(-150)、filter(0)
local_out 钩子,在 ip_build_and_send_pkt()调用
(优先级顺序) Conntrack(-200)、mangle(-150)、DNAT(-100)、filter(0)
post钩子 在ip_finish_output() 中调用
mangle(-150)、SNAT(100)、Conntrack(INT_MAX)
可以发现, 钩子函数的优先顺序 (相对于一条流来说)
都是 先 跟踪链接(ct), 然后 mangle nat 、filter ,最后再 链接跟踪(ct)
要注册一个钩子函数 需要调用 nf_register_hook() 函数,并且准备一个数据 结构nf_hook_ops
nf_register_hook(&nf_hook_ops) 能完成对钩子函数的注册
nf_unregister_hook(&nf_hook_ops) 能完成对钩子函数的注销
钩子函数对应的结构就是下面这个
struct nf_hook_ops {
struct list_head list;
/* 此下的值由用户填充 */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hook以升序的优先级排序 */
int priority;
};
- list 即 netfilter 的链表结构,链接各个钩子函数
- nf_hookfn *hook 真正的 处理函数,具体执行的内容
nf_hookfn 函数指针
okfn 即用来处理
- pf 是 协议 ,
- hooknum 即 表明是在哪个钩子点上的 pre post foward ,由宏定义
- priority 调用优先级
如何调用钩子函数
netfilter 有5条链: pre,forword,post,in,out
实际上,就是在内的5个位置上调用 NF_HOOK ,(正常代码逻辑,通过NF_HOOK,切入到netfileter之中)
重要结构
钩子函数保存在全局二维数组nf_hooks中。
nf hooks
struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;
NFPROTO_NUMPROTO,是说在哪个协议层(模式)
NF_MAX_HOOKS , 是说在哪个钩子点
每个成员都是一个链表,表示某个协议下的钩子点的钩子函数链
上述二维数组成员的具体结构
struct nf_hook_ops
{
struct list_head list; //该成员将会把这个结构添加在nf_hooks数组引导的队列头中。
/* User fills in from here down. */
nf_hookfn *hook; //钩子处理函数:用于判断报文是否需要经过本钩子到检测过滤等
struct module *owner; //可以使用动态加载的内核模块
u_int8_t pf; //协议族,对应于nf_hooks的第一维
unsigned int hooknum; //钩子点,对应于nf_hooks的第二维
/* Hooks are ordered in ascending priority. */
int priority;//这里不是一个先来先服务队列,而是一个优先级队列。
};
钩子处理函数的 回调函数接口,nf_hook_ops 结构的hook成员的类型。
//return: NF_DROP,NF_ACCEPT...
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 *));
作为回调函数,他的参数由NF_HOOK 传递到netfilter框架中去
重要函数
切入到Netfilter 框架的入口NF_HOOK
当nf_hook_ops 数据填充完毕后,调用nf_register_hook(& ops),将钩子函数点注册到框架上