您的位置:首页 > 运维架构 > Linux

Linux netfilter 学习笔记 之六 ip层netfilter的filter表的创建及其hook函数分析

2014-06-24 20:55 731 查看
基于linux2.6.21

今天主要在前两节的基础上,分析filter表的创建,以及filter表的hook回调函数的分析。

1. Filter模块初始化

在前面分析表的注册时,我们知道要注册一个新的xt_table,需要实例化xt_table与ipt_replace这两个结构体。创建filter表时同样需要这样做。下面是filter表实例化的这两个结构体

1.1 Filter表的初始化

1.1.1 xt_table filter表初始化

主要是设置创建的xt_table的表名,在哪些hook点起作用等

static struct ipt_table packet_filter = {

.name = "filter",

.valid_hooks = FILTER_VALID_HOOKS,/*仅支持NF_LOCAL_IN NF_LOCAL_OUT NF_FORWARD 3个hook点,即该表只包含这个3个内建链*/

.lock = RW_LOCK_UNLOCKED,

.me = THIS_MODULE,

.af = AF_INET,

};

1.1.2 ipt_replace repl

repl的初始化定义如下:

static struct

{

struct ipt_replace repl;

struct ipt_standard entries[3];/*创建了3个rule和一个error*/

struct ipt_error term;

} initial_table __initdata

= {

{ "filter", FILTER_VALID_HOOKS, 4,

sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),

{ [NF_IP_LOCAL_IN] = 0,

[NF_IP_FORWARD] = sizeof(struct ipt_standard),

[NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 },

{ [NF_IP_LOCAL_IN] = 0,

[NF_IP_FORWARD] = sizeof(struct ipt_standard),

[NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 },

0, NULL, { } },

{

/* LOCAL_IN */

{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },

0,

sizeof(struct ipt_entry),

sizeof(struct ipt_standard),

0, { 0, 0 }, { } },

{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },

-NF_ACCEPT - 1 } },

/* FORWARD */

{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },

0,

sizeof(struct ipt_entry),

sizeof(struct ipt_standard),

0, { 0, 0 }, { } },

{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },

-NF_ACCEPT - 1 } },

/* LOCAL_OUT */

{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },

0,

sizeof(struct ipt_entry),

sizeof(struct ipt_standard),

0, { 0, 0 }, { } },

{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },

-NF_ACCEPT - 1 } }

},

/* ERROR */

{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },

0,

sizeof(struct ipt_entry),

sizeof(struct ipt_error),

0, { 0, 0 }, { } },

{ { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } },

{ } },

"ERROR"

}

}

};

在分析这部分代码之前,需要说明一下,struct ipt_standard即为

ipt_entry+ipt_standard_target

下面我们就结合结构体的定义,分析下ipt_replace的初始化与第一条ipt_standard的初始化。

repl的初始化

repl->name "filter"

repl->vaild_hooks = FILTER_VALID_HOOKS

repl->num_entries= 4

repl->size = sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error) 即3个rule和一个error占用的内存大小

repl->hook_entry[] 即设置3个rule与一个error 对应于第一个rule的偏移量

repl->underflow[] 即设置3个rule与一个error 对应于第一个rule的偏移量

repl->num_countes = 0

repl->counters = NULL

repl->entries = {}

entries[3]的初始化中,我们只选取entries[0]进行分析,一个ipt_standard即为

ipt_entry+ipt_standard_target = ipt_entry+ipt_entry_target+verdict。

所以entries[0]的ipt_entry=

{ { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },

0,sizeof(struct ipt_entry),sizeof(struct ipt_standard),0, { 0, 0 }, { } }



ipt_entry.ip = { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }

ipt_entry.nfcache = 0

ipt_entry.target_offset = sizeof(struct ipt_entry)

ipt_entry.next_offset = sizeof(struct ipt_standard),

而entries[0]的ipt_standard_target等于

{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },-NF_ACCEPT - 1 }

即ipt_entry_target = { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }。从而可以得出pt_entry_target.u.user.size = IPT_ALIGN(sizeof(struct ipt_standard_target))

ipt_entry_target.u.user.name = ""

veridct = -NF_ACCEPT - 1 即标准target为ACCEPT。

结合ipt_repleac、ipt_standard、ipt_error结构体的定义,我们知道在filter表初始化时需要创建NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT3条链,且为每条链创建一条target为NF_ACCEPT的默认规则和一条ipt_error规则。

在初始化了以上两个数据结构后,则调用ipt_register_table即可创建一个xt_table,并插入到链表xt_af[AF_INET].tables中

1.2 hook函数的注册

注册了一个xt_table后,只是为iptables提供了一个添加过滤规则的表而已,而我们的最终目的是在执行NF_HOOK时,能够对iptables添加的过滤规则进行过滤检查操作,从而决定数据包的去向,那很明显我们需要在相应的hook点中注册hook函数,这样才能对数据包进行过滤检查。

通过以上的信息,我们可以大致进行如下猜想:

a)由上面的表注册,我们知道filter表主要对NF_LOCAL_IN、NF_LOCAL_OUT、NF_FORWAD,那我们也需要在这3个hook点注册相应的hook函数。

b)而我们通过iptables添加的过滤规则又是保存在xt_table->private.entries中,显然要想数据包能够匹配filter表的某条规则,就需要遍历xt_table并对表中的每一条规则都执行match与target操作,即我们编写的hook函数需要满足上述要求,而在第四节的分析中,我们提到函数ipt_do_table也是实现这种功能的,那是不是说我们编写的filter表的hook函数可以调用ipt_do_table呢?

下面就分析filter表的hook函数注册相关的代码,来验证我们的猜想是否正确。

1.2.1 nf_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,

},

};

在iptables_filter.c中,我们可以看到,filter模块初始化了3个nf_hook_ops结构,且这3个nf_hook_ops挂载的hook点分别为 NF_IP_LOCAL_IN、NF_IP_LOCAL_OUT、NF_IP_FORWARD,和我们的猜想的一样。

而3个hook回调函数分别为ipt_hook(NF_IP_LOCAL_IN与NF_IP_FORWARD点上的hook回调函数是相同的)、ipt_local_out_hook。那我们接下来分析下这两个函数,看它们的实现是怎样的额。

1.2.1.1ipt_hook

我们发现这个函数就是直接调用ipt_do_table,实现对数据包的过滤操作,看来我们的分析是正确的(关于ipt_do_table的详细分析,参看上一节的分析)。

主要是调用函数ipt_do_table,遍历该filter表的NF_LOCAL_IN链或者NF_LOCAL_FORWARD链的所有规则,对数据包进行规则检查,根据返回值确定对数据包的后续操作:

i)若是NF_ACCEPT,说明在filter表的hook检查通过了,接着对数据包进行其他的hook
检查。

ii)若是NF_DROP,则说明在filter表的hook检查时,检查的结果是丢掉数据包,则会
释放数据包的缓存,程序返回。

iii)若是NF_STOP,则后续不再进行hook检查,直接放行 。

iiii)若是NF_QUEUE,则发送到应用层进行相应的操作。

static unsigned int

ipt_hook(unsigned int hook,

struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

int (*okfn)(struct sk_buff *))

{

return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);

}

1.2.1.2 ipt_local_out_hook

这个函数相比于ipt_hook,增加了对数据包长度的判断,即若本机发送出去的数据包ip头部长度不全时,则不进行过滤操作,直接返回ACCEPT。否则则调用ipt_do_table对数据包进行过滤操作。

static unsigned int

ipt_local_out_hook(unsigned int hook,

struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

int (*okfn)(struct sk_buff *))

{

/* root is playing with raw sockets. */

if ((*pskb)->len < sizeof(struct iphdr)

|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {

if (net_ratelimit())

printk("ipt_hook: happy cracking.\n");

return NF_ACCEPT;

}

return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);

}

2 filter模块的初始化函数

该函数就是调用ipt_register_table注册filter表,以及调用 nf_register_hook注册上面定义的ipt_ops[]。也就是把我们上面介绍的表的注册于hook函数的注册放在了同一个函数里执行而已。

这个函数里多了一句initial_table.entries[1].target.verdict = -forward - 1;,而forward即为NF_ACCEPT,这句话的意思为设置entries[1]的target为NF_ACCEPT,而entries[1]的target初始化值已经是NF_ACCEPT了,所以我感觉这句代码目前是不需要的,可能以后增加NF_FORWARD时,就有必要了。。。

static int __init init(void)

{

int ret;

if (forward < 0 || forward > NF_MAX_VERDICT) {

printk("iptables forward must be 0 or 1\n");

return -EINVAL;

}

/* Entry 1 is the FORWARD hook */

initial_table.entries[1].target.verdict = -forward - 1;

/* Register table */

ret = ipt_register_table(&packet_filter, &initial_table.repl);

if (ret < 0)

return ret;

/* Register hooks */

ret = nf_register_hook(&ipt_ops[0]);

if (ret < 0)

goto cleanup_table;

ret = nf_register_hook(&ipt_ops[1]);

if (ret < 0)

goto cleanup_hook0;

ret = nf_register_hook(&ipt_ops[2]);

if (ret < 0)

goto cleanup_hook1;

return ret;

cleanup_hook1:

nf_unregister_hook(&ipt_ops[1]);

cleanup_hook0:

nf_unregister_hook(&ipt_ops[0]);

cleanup_table:

ipt_unregister_table(&packet_filter);

return ret;

}

在熟悉了hook机制、数据结构之间的关系(ipt_entry、ipt_standard、ipt_standard_target、ipt_entry_match、ipt_entry_target、xt_table、xt_match、xt_target)、表的注册、表中规则的遍历与规则的匹配等机制后,就很容易理解iptables 的filter表的注册与hook函数注册及hook函数的流程了。

由于nat表牵扯到连接跟踪,比filter表要复杂的多,下一节就开始分析连接跟踪,把连接跟踪分析完以后,再好好分析nat的功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐