Linux 3.13.0 netfilter 学习笔记 之二 IP层netfilter hook点

三层netfilter hook点的注册与注销

我们知道使用iptables,添加规则时需要指定表,在iptables中有filter、nat、mangle三张表,因为我们接下来几节分析的内容也是从这三个table表加上连接跟踪,此处我们按照这四个模块来分析,而不是按HOOK点的分类来分析

filter机制相关的hook注册

Filter表主要在以下几个hook点上其作用:
NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT
即只在数据包的接收、数据包的发送及数据包的转发时进行过滤,filter表相关的hook点的注册是在iptables filter模块初始化时注册的,定义的hook_ops结构体变量如下:

// Linux 2.6.21
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,
    },
};

// Linux 3.13.0
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
			    (1 << NF_INET_FORWARD) | \
			    (1 << NF_INET_LOCAL_OUT))

static const struct xt_table packet_filter = {
	.name		= "filter",
	.valid_hooks	= FILTER_VALID_HOOKS,
	.me		= THIS_MODULE,
	.af		= NFPROTO_IPV4,
	.priority	= NF_IP_PRI_FILTER,
};

通过上面的定义我们知道,相应的hook函数分别为ipt_hook、ipt_local_out_hook(关于这两个函数的具体执行过程暂且不分析,后面分析)。
然后通过调用nf_register_hook进行注册,调用nf_unregister_hook进行注销

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为源地址转换。

static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
	/* Before packet filtering, change destination */
	{
		.hook		= nf_nat_ipv4_in,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_PRE_ROUTING,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	/* After packet filtering, change source */
	{
		.hook		= nf_nat_ipv4_out,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
	/* Before packet filtering, change destination */
	{
		.hook		= nf_nat_ipv4_local_fn,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	/* After packet filtering, change source */
	{
		.hook		= nf_nat_ipv4_fn,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
};

mangle机制相关的hook注册与注销

mangle表主要在NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING等hook点起作用

#define MANGLE_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \
			    (1 << NF_INET_LOCAL_IN) | \
			    (1 << NF_INET_FORWARD) | \
			    (1 << NF_INET_LOCAL_OUT) | \
			    (1 << NF_INET_POST_ROUTING))

static const struct xt_table packet_mangler = {
	.name		= "mangle",
	.valid_hooks	= MANGLE_VALID_HOOKS,
	.me		= THIS_MODULE,
	.af		= NFPROTO_IPV4,
	.priority	= NF_IP_PRI_MANGLE,
};

连接跟踪模块相关的hook注册与注销

主要定义了以下个nf_hook_ops数组,连接跟踪模块主要在NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING等hook点起作用,连接跟踪模块是实现nat的基础,也是实现ALG功能的基础。

/* Connection tracking may drop packets, but never alters them, so
   make it the first hook. */
static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
	{
		.hook		= ipv4_conntrack_in,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_PRE_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK,
	},
	{
		.hook		= ipv4_conntrack_local,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_CONNTRACK,
	},
	{
		.hook		= ipv4_helper,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK_HELPER,
	},
	{
		.hook		= ipv4_confirm,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
	},
	{
		.hook		= ipv4_helper,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_CONNTRACK_HELPER,
	},
	{
		.hook		= ipv4_confirm,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
	},
};

static struct nf_hook_ops ipv4_defrag_ops[] = {
	{
		.hook		= ipv4_conntrack_defrag,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_PRE_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK_DEFRAG,
	},
	{
		.hook           = ipv4_conntrack_defrag,
		.owner          = THIS_MODULE,
		.pf             = NFPROTO_IPV4,
		.hooknum        = NF_INET_LOCAL_OUT,
		.priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
	},
};

static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = {
	{
		.hook		= ipv4_synproxy_hook,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
	},
	{
		.hook		= ipv4_synproxy_hook,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
	},
};

三层netfilter hook点的调用

上面是三层netfilter相关的hook点的注册,下面我们需要知道三层netfilter的hook回调函数是在哪些函数里调用的。我们主要分析ip协议在五个hook点的调用。
上图便是五个hook点调用的地方,对应于代码,我们来分析一下。

PRE_ROUTING

看这个名字,我们知道在这里执行hook回调函数时,数据包还没有经过路由,对于ip报文来说,在ip_rcv函数里,只是对数据包进行了合理性检查,还没有对数据包进行查找路由操作,所以PRE_ROUTING hook点的回调函数的调用,即是在该函数的末尾通过调用函数NF_HOOK实现

	return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
		       ip_rcv_finish);

LOCAL_IN

当进入该hook点之前,数据包已经进行了路由操作,通过对协议栈的流程分析我们知道,在 ip_rcv_finish进行了路由选择后,对于属于本地接收的报文会调用函数ip_local_deliver,那很显然,LOCAL_IN HOOK点的回调函数的调用执行,肯定是在这个函数的末尾执行的了。函数片断如下:

	return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
		       ip_local_deliver_finish);

FORWARD

在进行了路由后,对于需要转发的数据,通过调用函数dst_input(skb),间接调用函数 ip_forward进行数据转发操作(关于为何会调用到ip_forward及ip_local_deliver,这是通过建立路由缓存时填充dst_entry指针实现的)。所以该HOOK点的hook回调函数的执行也是在该函数的末尾通过调用NF_HOOK实现的。

	return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev,
		       rt->dst.dev, ip_forward_finish);

LOCAL_OUT

对于该hook点,是本地发送数据的hook调用,由于本地发送的数据既可以是UDP数据也可以是TCP数据,亦可以是组播数据。所以LOCAL_OUT hook点的调用函数不止一处。其代码书写如下:

NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
		      rt->dst.dev, dst_output);

一般是本地数据找到路由之后,且没有调用skb->dst.out准备将数据包发送出去之前调用NF_HOOK

POST_ROUTING

在函数经过了FORWARD或者OUT节点后,就会通过skb->dst.out,执行到函数ip_output,所以POST_ROUTING hook点的hook回调函数的执行是在该函数的末尾通过调用NF_HOOK_COND实现的。

	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_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转换的实现分析。本节将总的调用起点分析了一下,后面我们将针对每一个模块的实现进行分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值