logo资料库

linux netfilter iptables 源码分析.doc

第1页 / 共40页
第2页 / 共40页
第3页 / 共40页
第4页 / 共40页
第5页 / 共40页
第6页 / 共40页
第7页 / 共40页
第8页 / 共40页
资料共40页,剩余部分请下载后查看
Linux netfilter 源码分析 内容基本上来自两篇文章: 《Netfilter 源码分析》—(独孤九贱 http://www.skynet.org.cn/index.php) 《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) { /* 如果数据报是共享的,则复制一个出来,此时复制而出的已经和 socket 脱离了关系 */ 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: out: } kfree_skb(skb); //丢掉数据报 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 /* 如果 nf_hooks[PF_INET][NF_IP_FORWARD]所指向的链表为空(即该钩子上没有挂处 理函数),则直接调用 okfn;否则,则调用 net/core/netfilter.c::nf_hook_slow()转入 Netfilter 的处理。 */ 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; /*前面提到过,返回 1,则表示装继续调用 okfn 函数指针*/ goto unlock; } else if (verdict == NF_DROP) { kfree_skb(*pskb); /*删除数据包,需要释放 skb*/ 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) { /* * The caller must not block between calls to this * function because of risk of continuing from deleted element. */ /* 依次调用指定 hook 点下的所有 nf_hook_ops->(*hook)函数,这些 nf_hook_ops 里有 filter 表注册的,有 mangle 表注册的,等等。 list_for_each_continue_rcu 函数是一个 for 循环的宏,当调用结点中的 hook 函数后,根 据返回值进行相应处理。如果 hook 函数的返回值是 NF_QUEUE,NF_STOLEN,NF_DROP 时,函数 返回该值;如果返回值是 NF_REPEAT 时,则跳到前一个结点继续处理;如果是其他值,由下 一个结点继续处理。如果整条链表处理完毕,返回值不是上面四个值,则返回 NF_ACCEPT。 */ 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]; /* 表名,如"filter"、"nat"等,为了满足自动模块加载的设计,包含该表的模块应命名为 iptable_'name'.o */ struct ipt_replace *table; /* 表模子,初始为 initial_table.repl */ unsigned int valid_hooks; /* 位向量,标示本表所影响的 HOOK */ rwlock_t lock; /* 读写锁,初始为打开状态 */ struct ipt_table_info *private; /* iptable 的数据区,见下 */ 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]; /* 记录所影响的 HOOK 的规则入口相对于下面的 entries 变量的偏移量 */ unsigned int underflow[NF_IP_NUMHOOKS]; /* 与 hook_entry 相对应的规则表上限偏移量,当无规则录入时,相应的 hook_entry 和 underflow 均为 0 */ 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; /* 所要匹配的报文的 IP 头信息 */ unsigned int nfcache; /* 位向量,标示本规则关心报文的什么部分,暂未使用 */ u_int16_t target_offset; /* target 区的偏移,通常 target 区位于 match 区之后,而 match 区则在 ipt_entry 的末 尾; 初始化为 sizeof(struct ipt_entry),即假定没有 match */ u_int16_t next_offset; /* 下一条规则相对于本规则的偏移,也即本规则所用空间的总和, 初始化为 sizeof(struct ipt_entry)+sizeof(struct ipt_target),即没有 match */ unsigned int comefrom; /* 规则返回点,标记调用本规则的 HOOK 号,可用于检查规则的有效性 */ struct ipt_counters counters; /* 记录该规则处理过的报文数和报文总字节数 */ unsigned char elems[0]; /*target 或者是 match 的起始位置 */ } 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; } /* Entry 1 is the FORWARD hook */
initial_table.entries[1].target.verdict = -forward - 1; /* Register table */ ret = ipt_register_table(&packet_filter); //注册 filter 表 if (ret < 0) return ret; /* Register hooks */ 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; } /* ipt_register_table 函数的参数 packet_filter 包含了待注册表的各个参数 */ static struct ipt_table packet_filter = { .name .table = "filter", = &initial_table.repl, .valid_hooks = FILTER_VALID_HOOKS, .lock = RW_LOCK_UNLOCKED, .me = THIS_MODULE };
/* 上面的&initial_table.repl 是一个 ipt_replace 结构,也就是 ipt_table-〉*table 的 初始值。 下面是 ipt_replace 结构的定义,它和 ipt_table_info 很相似,基本上就是用来初始化 ipt_table 中的 ipt_table_info *private 的,这个结构不同于 ipt_table_info 之处在于, 它还要保存表的旧的规则信息 */ struct ipt_replace { }; char name[IPT_TABLE_MAXNAMELEN]; /* 表名 */ unsigned int valid_hooks; unsigned int num_entries; unsigned int size; /* 影响的 hook */ /* entry 数 */ /* entry 的总大小 */ 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]; /* 规则入口 */ /* 下面是 initial_table.repl 的初始化 */ 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, { } }, { /* LOCAL_IN */ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard),
分享到:
收藏