上一章讲解Netfilter的hook点知识及源代码分析
小伙伴了解到报文在内核网络栈会经过5个hook点,每个hook点会安装若干个钩子函数,报文经过每个hook点时会有序的调用钩子函数处理,而钩子函数处理时需要table的rule。问题就来了,钩子函数和rule是怎么联系起来;rule是如何归属于table呢;iptables命令如何下发rule到netfilter模块的;
1.table描述
在内核中代表table的结构体:xt_table,该结构维护了哪些hook点有效,属于哪个协议族(ipv4/ipv6),属于该table的所有规则以及所在有效的hook点上的执行优先级。
Netfilter框架会在每个net ns创建时,创建默认rule关联到table的private属性里,然后把table信息和table的构子函数,记录到net ns里(struct net)。
2.rule描述
2.1 默认rule
在系统启动后,每张table注册的时候,默认生成一条”放行“的rule,保证在某条hook点上所有rule不匹配后都”放行“。
可以通过iptables命令查看某张表的hook点上的默认rule。
命令: iptables -t filter -vnL
Chain INPUT (policy DROP 8036 packets, 606K bytes)
Chain FORWARD (policy ACCEPT 32691 packets, 13M bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 8110 packets, 764K bytes)
pkts bytes target prot opt in out source destination
2.2下发rule
Netfilter采用sockopt方式,是一种内核和用户空间通信手段之一。
socket方式的SET接口约定使用struct ipt_replace结构体下发rule。
/* The argument to IPT_SO_SET_REPLACE. */
struct ipt_replace {
/* Which table. */
char name[XT_TABLE_MAXNAMELEN]; /*要更新的table*/
/* Which hook entry points are valid: bitmask. You can't
change this. */
unsigned int valid_hooks; /*哪些hook点有效*/
/* Number of entries */
unsigned int num_entries; /*rule数量*/
/* Total size of new entries */
unsigned int size; /*rule数量的总大小*/
/* Hook entry points. */
unsigned int hook_entry[NF_INET_NUMHOOKS]; /*记录每个hook的第一条rule的位置*/
/* Underflow points. */
unsigned int underflow[NF_INET_NUMHOOKS]; /*记录每个hook的最后一条rule的位置*/
/* Information about old entries: */
/* Number of counters (must be equal to current number of entries). */
unsigned int num_counters; /*报文数量*/
/* The old entries' counters. */
struct xt_counters *counters; /*报文统计*/
/* The entries (hang off end: not really an array). */
struct ipt_entry entries[0]; /*所有rule的存储位置,每个hook的所有rule位置是连续的*/
};
iptables命令采用该通信方法下发rule,该命令的配置思想:读->更新->写 方式,先获取某个table的所有rule,再对具体操作(删除/插入/…)更新或者新增rule,完成之后所有rule重新下发到Netfilter,整个过程可以理解为 “全量替换“。
以及基于struct ipt_replace结构存储所有rule的内存布局如下:
rule是连续存储的,按照hook定义的编号从小到大排列(pre_routing < input < forward < output < post_routing )。
hook_entry和underflow记录了每个hook的所有rule的起始位置和结束位置。
2.3rule的结构体
无论在内核还是用户空间环境下,一条rule的具体信息是存储在ipt_entry结构体里。
它的elems属性包含0个或者多个ipt_entry_match结构体和一个ipt_entry_target结构体。
/* This structure defines each of the firewall rules. Consists of 3
parts which are 1) general IP header stuff 2) match specific
stuff 3) the target to perform if the rule matches */
struct ipt_entry {
struct ipt_ip ip;
/* Mark with fields that we care about. */
unsigned int nfcache;
/* Size of ipt_entry + matches */
__u16 target_offset;
/* Size of ipt_entry + matches + target */
__u16 next_offset;
/* Back pointer */
unsigned int comefrom;
/* Packet and byte counters. */
struct xt_counters counters;
/* The matches (if any), then the target. */
unsigned char elems[0];
};
ipt_entry具有标准匹配信息,包含网口、ip地址信息等
ipt_entry_match具有扩展匹配信息,包含TCP状态、端口范围信息等
ipt_entry_target代表匹配信息完成后,采用哪种行为,如放行、丢弃或者跳转等。
match、target分为标准和扩展,这些概念自行补补。
3.table和rule最终形态
table从注册到形成,rule从一条默认rule逐渐增多,最后它们会存储在每个net ns里,提供给netfilter使用。
以上源代码以及内容基于内核4.19.1产出。
—越简单,易接受。在折腾路上…