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 调用优先级