基于linux2.6.21
上一节分析了hook机制,本节简单介绍下三层防火墙借助hook机制,实现的总体框架
1 三层netfilter hook点的注册与注销
我们知道使用iptables,添加规则时需要指定表,在iptables中有filter、nat、mangle三张表,因为我们接下来几节分析的内容也是从这三个table表加上连接跟踪,此处我们也已这四个模块来将,而不是按HOOK点的分类来分析
1.1 filter机制相关的hook注册注册
Filter表主要在以下几个hook点上其作用:
NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT
即只在数据包的接收、数据包的发送及数据包的转发时进行过滤,filter表相关的hook点的注册是在iptables filter模块初始化时注册的,定义的hook_ops结构体变量如下:
static struct nf_hook_ops ipt_ops[] = {
{
.hook= ipt_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_IN,
.priority= NF_IP_PRI_FILTER,
},
{
.hook= ipt_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_FORWARD,
.priority= NF_IP_PRI_FILTER,
},
{
.hook= ipt_local_out_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_OUT,
.priority= NF_IP_PRI_FILTER,
},
};
通过上面的定义我们知道,相应的hook函数分别为ipt_hook、ipt_local_out_hook(关于这两个函数的具体执行过程暂且不分析,后面分析)。
然后通过调用nf_register_hook进行注册,调用nf_unregister_hook进行注销
1.2 nat机制相关的hook注册注册
nat表主要在以下几个hook点上其作用:
NF_IP_PRE_ROUTING、NF_IP_POST_ROUTING、NF_IP_LOCAL_OUT、NF_IP_LOCAL_IN
其中NF_IP_PRE_ROUTING、NF_IP_LOCAL_OUT为目的地址转发,而NF_IP_POST_ROUTING、NF_IP_LOCAL_IN为源地址转换。
/* Before packet filtering, change destination */
static struct nf_hook_ops ip_nat_in_ops = {
.hook= ip_nat_in,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_PRE_ROUTING,
.priority= NF_IP_PRI_NAT_DST,
};
/* After packet filtering, change source */
static struct nf_hook_ops ip_nat_out_ops = {
.hook= ip_nat_out,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_POST_ROUTING,
.priority= NF_IP_PRI_NAT_SRC,
};
/* After conntrack, adjust sequence number */
static struct nf_hook_ops ip_nat_adjust_out_ops = {
.hook= ip_nat_adjust,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_POST_ROUTING,
.priority= NF_IP_PRI_NAT_SEQ_ADJUST,
};
/* Before packet filtering, change destination */
static struct nf_hook_ops ip_nat_local_out_ops = {
.hook= ip_nat_local_fn,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_OUT,
.priority= NF_IP_PRI_NAT_DST,
};
/* After packet filtering, change source for reply packets of LOCAL_OUT DNAT */
static struct nf_hook_ops ip_nat_local_in_ops = {
.hook= ip_nat_fn,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_IN,
.priority= NF_IP_PRI_NAT_SRC,
};
/* After conntrack, adjust sequence number */
static struct nf_hook_ops ip_nat_adjust_in_ops = {
.hook= ip_nat_adjust,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_IN,
.priority= NF_IP_PRI_NAT_SEQ_ADJUST,
};
1.3 mangle机制相关的hook注册与注销
主要定义了以下个hook_ops变量,mangle表主要在NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING等hook点起作用
static struct nf_hook_ops ipt_ops[] = {
{
.hook= ipt_route_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_PRE_ROUTING,
.priority= NF_IP_PRI_MANGLE,
},
{
.hook= ipt_route_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_IN,
.priority= NF_IP_PRI_MANGLE,
},
{
.hook= ipt_route_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_FORWARD,
.priority= NF_IP_PRI_MANGLE,
},
{
.hook= ipt_local_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_OUT,
.priority= NF_IP_PRI_MANGLE,
},
{
.hook= ipt_route_hook,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_POST_ROUTING,
.priority= NF_IP_PRI_MANGLE,
},
};
1.4 连接跟踪模块相关的hook注册与注销
主要定义了以下个hook_ops变量,连接跟踪模块主要在NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING等hook点起作用,连接跟踪模块是实现nat的基础,也是实现ALG功能的基础。
static struct nf_hook_ops ip_conntrack_defrag_ops = {
.hook= ip_conntrack_defrag,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_PRE_ROUTING,
.priority= NF_IP_PRI_CONNTRACK_DEFRAG,
};
static struct nf_hook_ops ip_conntrack_in_ops = {
.hook= ip_conntrack_in,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_PRE_ROUTING,
.priority= NF_IP_PRI_CONNTRACK,
};
static struct nf_hook_ops ip_conntrack_defrag_local_out_ops = {
.hook= ip_conntrack_defrag,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_OUT,
.priority= NF_IP_PRI_CONNTRACK_DEFRAG,
};
static struct nf_hook_ops ip_conntrack_local_out_ops = {
.hook= ip_conntrack_local,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_OUT,
.priority= NF_IP_PRI_CONNTRACK,
};
/* helpers */
static struct nf_hook_ops ip_conntrack_helper_out_ops = {
.hook= ip_conntrack_help,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_POST_ROUTING,
.priority= NF_IP_PRI_CONNTRACK_HELPER,
};
static struct nf_hook_ops ip_conntrack_helper_in_ops = {
.hook= ip_conntrack_help,
.owner= THIS_MODULE,
.pf= PF_INET,
.hooknum= NF_IP_LOCAL_IN,
.priority= NF_IP_PRI_CONNTRACK_HELPER,
};
2 三层netfilter hook点的调用
上面是三层netfilter相关的hook点的注册,下面我们需要知道三层netfilter的hook回调函数是在哪些函数里调用的。我们主要分析ip协议在五个hook点的调用
上图便是五个hook点调用的地方,对应于代码,我们来分析一下。
2.1 PRE_ROUTING
看这个名字,我们知道在这里执行hook回调函数时,数据包还没有经过路由,对于ip报文来说,在ip_rcv函数里,只是对数据包进行了合理性检查,还没有对数据包进行查找路由操作,所以PRE_ROUTINGhook点的回调函数的调用,即是在该函数的末尾通过调用函数NF_HOOK实现
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
2.2 LOCAL_IN
当进入该hook点之前,数据包已经进行了路由操作,通过对协议栈的流程分析我们知道,在 ip_rcv_finish进行了路由选择后,对于属于本地接收的报文会调用函数ip_local_deliver,那很显然,LOCAL_IN HOOK点的回调函数的调用执行,肯定是在这个函数的末尾执行的了。函数片断如下:
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
2.3 FORWARD
在进行了路由后,对于需要转发的数据,通过调用函数dst_input(skb),间接调用函数 ip_forward进行数据转发操作(关于为何会调用到ip_forward及ip_local_deliver,这是通过建立路由缓存时填充dst_entry指针实现的)。所以该HOOK点的hook回调函数的执行也是在该函数的末尾通过调用NF_HOOK实现的。
return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
ip_forward_finish);
2.4 LOCAL_OUT
对于该hook点,是本地发送数据的hook调用,由于本地发送的数据既可以是UDP数据也可以是TCP数据,亦可以是组播数据。所以OUThook点的调用函数不止一处。其代码书写如下:
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
一般是本地数据找到路由之后,且没有调用skb->dst.out准备将数据包发送出去之前调用NF_HOOK
2.5 POST_ROUTING
在函数经过了FORWARD或者OUT节点后,就会通过skb->dst.out,执行到函数ip_output,所以函数
return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
以上就是ip层netfilter涉及的hook_ops的注册以及调用,后面我们分析的xt_table表中的规则匹配、连接跟踪、nat操作、mangle操作,均是由上面五个hook点的NF_HOOK函数的调用而触发的。以下章节则侧重于表匹配、连接跟踪、nat转换的实现分析。本节将总的调用起点分析了一下,后面我们将针对每一个模块的实现进行分析。