linux netfilter 分析,Linux netfilter源码分析

一、

IP报文的接收到hook函数的调用

1.1

ip_input.c

ip_rcv()函数

以接收到的报文为例,类似的还有ip_forward(ip_forward.c)和ip_output(ip_output.c)

int ip_rcv(struct sk_buff *skb, struct net_device

*dev, struct packet_type *pt, struct net_device *orig_dev)

{

struct iphdr *iph;

//定义一个ip报文的数据报头

u32

len;

if

(skb->pkt_type == PACKET_OTHERHOST)

goto drop; //数据包不是发给我们的

IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES); //收到数据包统计量加1

if

((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)

{

IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

goto out;

}

if

(!pskb_may_pull(skb, sizeof(struct iphdr)))

goto inhdr_error; //对数据报的头长度进行检查,

iph =

skb->nh.iph; //取得数据报的头部位置

if (iph->ihl < 5 ||

iph->version != 4) //版本号或者头长度不对,goto

inhdr_error; //头长度是以4字节为单位的,所以5表示的是20字节

if (!pskb_may_pull(skb,

iph->ihl*4))

goto

inhdr_error;

if (unlikely(ip_fast_csum((u8

*)iph, iph->ihl)))

goto inhdr_error; //检查报文的检验和字段

len =

ntohs(iph->tot_len);

if (skb->len < len || len <

(iph->ihl*4))

goto

inhdr_error; //整个报文长度不可能比报头长度小

if (pskb_trim_rcsum(skb,

len))

{ //对数据报进行裁减,这样可以分片发送过来的数据报不会有重复数据IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

goto drop;

}

return

NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,

ip_rcv_finish); //通过回调函数调用ip_rcv_finish

inhdr_error:

IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);

drop:

kfree_skb(skb); //丢掉数据报out:

return NET_RX_DROP;

}

1.2

include/linux/netfilter.h

NF_HOOK宏

#ifdef

CONFIG_NETFILTER_DEBUG

#define NF_HOOK(pf, hook, skb, indev, outdev,

okfn)

\

nf_hook_slow((pf),

(hook), (skb), (indev), (outdev), (okfn), INT_MIN)

#define NF_HOOK_THRESH nf_hook_slow

#else

#define NF_HOOK(pf, hook, skb, indev, outdev,

okfn)

\

(list_empty(&nf_hooks[(pf)][(hook)])

\

?

(okfn)(skb)

\

: nf_hook_slow((pf), (hook), (skb), (indev), (outdev),

(okfn), INT_MIN))

#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn,

thresh)

\

(list_empty(&nf_hooks[(pf)][(hook)])

\

?

(okfn)(skb)

\

:

nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn),

(thresh)))

#endif

1.3

net/core/netfilter.c

nf_kook_slow()函数

int

nf_hook_slow(int pf, unsigned int hook, struct sk_buff

**pskb,

struct

net_device *indev,

struct

net_device *outdev,

int

(*okfn)(struct sk_buff *),

int

hook_thresh)

{

struct list_head *elem;

unsigned int verdict;

int ret = 0;

rcu_read_lock();

elem = &nf_hooks[pf][hook];

next_hook:

verdict =

nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,

outdev,

&elem, okfn, hook_thresh);

if (verdict == NF_ACCEPT || verdict == NF_STOP) {

ret =

1; goto

unlock;

} else if (verdict ==

NF_DROP) {

kfree_skb(*pskb); ret = -EPERM;

} else if (verdict ==

NF_QUEUE) {

NFDEBUG("nf_hook: Verdict =

QUEUE.\n");

if (!nf_queue(*pskb, elem, pf, hook, indev,

outdev, okfn))

goto

next_hook;

}

unlock:

rcu_read_unlock();

return ret;

}

1.4

net/core/netfilter.c nf_iterate()函数

static unsigned int

nf_iterate(struct list_head *head,

struct sk_buff **skb,

int hook,

const struct net_device *indev,

const struct net_device *outdev,

struct list_head **i,

int (*okfn)(struct sk_buff *),

int hook_thresh)

{

list_for_each_continue_rcu(*i, head) {

struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;

if (hook_thresh > elem->priority)

continue;

switch (elem->hook(hook, skb, indev,

outdev, okfn)) {

case NF_QUEUE:

return NF_QUEUE;

case NF_STOLEN:

return NF_STOLEN;

case NF_DROP:

return NF_DROP;

case NF_REPEAT:

*i = (*i)->prev;

break;

}

}

return NF_ACCEPT;

}

二、ipt_table数据结构和表的初始化

2.1

include/linux/netfilter_ipv4/ip_tables.h struct ipt_table

表结构struct ipt_table{

struct list_head list;

char name[IPT_TABLE_MAXNAMELEN];

struct ipt_replace *table;

unsigned int valid_hooks;

rwlock_t lock;

struct ipt_table_info

*private;

struct module *me;

};

2.2 struct

ipt_table_info是实际描述表的数据结构

ip_tables.c

struct ipt_table_info

{

unsigned int size;

unsigned int number;

unsigned int initial_entries;

unsigned int hook_entry[NF_IP_NUMHOOKS];

unsigned int underflow[NF_IP_NUMHOOKS];

char entries[0] ____cacheline_aligned;

};

2.3

include/linux/netfilter_ipv4 规则用struct ipt_entry结构表示,包含匹配用的IP头部分、一个Target和0个或多个Match。由于Match数不定,所以一条规则实际的占用空间是可变的。结构定义如下

struct ipt_entry

{

struct ipt_ip ip;

unsigned int nfcache;

u_int16_t target_offset;

u_int16_t next_offset;

unsigned int comefrom;

struct ipt_counters counters;

unsigned char elems[0];

}

2.4 iptables的初始化init(void) ,以filter表为例

iptable_filter.c

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;

}

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

ret = ipt_register_table(&packet_filter);

//注册filter表

if (ret < 0)

return ret;

ret = nf_register_hook(&ipt_ops[0]);

//注册三个HOOK

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;

}

static struct

ipt_table packet_filter = {

.name

= "filter",

.table

= &initial_table.repl,

.valid_hooks =

FILTER_VALID_HOOKS,

.lock

= RW_LOCK_UNLOCKED,

.me

= THIS_MODULE

};

struct

ipt_replace

{

char name[IPT_TABLE_MAXNAMELEN];

unsigned int valid_hooks;

unsigned int num_entries;

unsigned int size;

unsigned int hook_entry[NF_IP_NUMHOOKS];

unsigned int underflow[NF_IP_NUMHOOKS];

unsigned int num_counters;

struct ipt_counters __user *counters;

struct ipt_entry entries[0];

};

static

struct

{

struct ipt_replace

repl;

struct ipt_standard entries[3];

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, { } },

{

{ { { { 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 } },

{ { { { 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 } },

{ { { { 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 } }

},

{ { { { 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"

}

}

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值