iptables : version 1.2 版本分析
主要是基于ipv4的, ipv6的没有分析。
源代码 路径 git://git.netfilter.org/iptables。
一. extentions
iptable的所有的extenion源文件在 extention 目录中,
每个extention 都有一个.c文件。
extention 可以是一个match 或 一个target,
每个match/target 必须提供init 函数, register函数和一个结构体。
结构体中包含有多个函数指针和一些数据。
struct iptables_match, struct iptables_target 见附录 【1】,【2】
运行iptables 命令时, 第一个操作就是加载所有的extention。
也就是将所有的match/target结构体加入到一个链表中。
加载步骤是:
1. init_extensions 函数
此调用所有extension的初始化函数。
函数的名字很类似都是ipt_extensionname_init();
例如:
ipt_ah_init();
ipt_conntrack_init();
ipt_dscp_init();
2. 各个模块的_init函数被调用。
对于match的extension:
_init函数主要动作就是调用register_match 将当前模块的
iptables_match 注册到系统中全局变量iptables_matches
所指定的链表中。
对于target的extension:
_init函数主要动作就是调用register_target 将当前模块的
iptables_target 注册到系统中全局变量iptables_targets
所指定的链表中。
在后续版本中register_match, register_target 改为了
xtables_register_match, _target. 同时有增加了xtables 模块。
其实只是代码结构上的变化, 功能和实现方式上并没用本质的变化。
这里就不分析了。
二 iptables主流程介绍:
1. 加载extensions
2. 初始化match list 和taget list, 表示没有match和taget 被选中。
3. 循环调用getopt_long 函数 解析出所有option 并加以处理。
option的处理分三类:
1). 大写字母: 视为命令 调用add_command函数将其加入command 变量中。
(后面会对command变量解析(command变量是一个命令集), 处理所有命令)
2). 小写字母: 视为OPtion, 调用set_option函数将其加入options变量中,
并更新 &fw.ip.invflags 变量, 表明是否是一个反转option
(反转option即取反, /bin/iptables -A INPUT ! -i br0 -d 255.255.255.254 -j DROP
!表示取反操作, 表示INPUT链 如果不是br0发出的包且, 目的地址是
255.255.255.254
则丢掉)
3). 特殊字母: 对于各个特殊字母有特殊处理。
a. 'h' 表示帮助, 显示帮助后直接退出
b. 'p' 表示protocol, 找到protocol , 赋值到 fw.ip.proto ,fw.nfcache |= NFC_IP_PROTO;
(fw 是一个ipt_entry 变量, 表示一条iptable rule)
c. 'm' 表示启用extension 模块中的类型为match的模块, 找到指定的match 记录下来, 留待将来使用。
d. '1' no option , 显示错误信息 并退出
e. 'M' 表示需要加载内核模块, 记录下模块名, 后面会主动加载
(iptables_insmod("ip_tables", modprobe);)
这一点对在内核中加入新的netfilter 模块应该非常有用。
4). default:
会遍历所有match, 找到指定的match(以前标记过的, 可以是一个也可以是多个)。
调用match的parse 函数来分析相关信息
如果这条命令没有使用match 并且这条命令使用了protocol, 则加载相应的protocol 模块。
4. 循环调用getopt_long 函数 结束。
5. 对所有match 遍历做final_check. (大多数的match 对这个函数只是“空实现”。所谓的"空实现"是自定义的名词, 即函数中do nothing, 直接返回)
6. 出错处理
7. 对解析出来的部分数据进行分析和校验
a. 如果 参数(options)中含有 -s, 即 [!] --source -s address[/mask][...]
调用函数parse_hostnetworkmask 解析到的networkmask 输出到 fw.ip.smsk ,saddrs, nsaddrs
b. 如果 参数(options)中含有 -d, 即 [!] --destination -d address[/mask][...]
调用函数parse_hostnetworkmask 解析到的networkmask 输出到 fw.ip.dmsk, daddrs, ndaddrs
c. 对解析出来的地址进行错误校验。
d. 对所有的command option 组合进行校验。 command 和option 的解释见附录【3】
8. 从内核中得到当前所有的iptable表项
(如果还没有得到, 一般情况下是没有得到,需要在这初始化, 如果在main函数中得到的,可以通过
do_command的参数handle传到这)
执行iptc_init(即函数TC_INIT)
struct ipt_get_entries
{
/* Which table: user fills this in. */
char name[IPT_TABLE_MAXNAMELEN];
/* User fills this in: total entry size. */
unsigned int size;
/* The entries. */ 所有的entries都存到这。
struct ipt_entry entrytable[0];
};
9. 处理chain 和target信息
找到相应的target 和chain,如果没有target 则使用standard target。
在这个过程中有一个小的问题,
ibt_is_chain->find_label->populate_cache(handle))->addchain()
为什么之前不直接populate_cache呢, 反正ibt_is_chain总是要被执行的,可能需要仔细分析代码。
10. 生成iptentry
根据fw 信息, match 信息, target 信息 生成一个entry
e = generate_entry(&fw, iptables_matches, target->t);
10. 处理command,
根据command 类型调用相应的iptc函数做相应的操作。
参数主要有 option 信息, 生成的entry, handle(所有现有的kernel 中的iptable entries)
在每个iptc 函数最后, 都会调用TC_COMMIT将数据同步到内核中。
11
附录 【1】
/* Include file for additions: new matches and targets. */
struct iptables_match
{
struct iptables_match *next;
ipt_chainlabel name;
const char *version;
/* Size of match data. */
size_t size;
/* Size of match data relevent for userspace comparison purposes */
size_t userspacesize;
/* Function which prints out usage message. */
void (*help)(void);
/* Initialize the match. */
void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);
/* Function which parses command options; returns true if it
ate an option */
int (*parse)(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match);
/* Final check; exit if not ok. */
void (*final_check)(unsigned int flags);
/* Prints out the match iff non-NULL: put space at end */
void (*print)(const struct ipt_ip *ip,
const struct ipt_entry_match *match, int numeric);
/* Saves the match info in parsable form to stdout. */
void (*save)(const struct ipt_ip *ip,
const struct ipt_entry_match *match);
/* Pointer to list of extra command-line options */
const struct option *extra_opts;
/* Ignore these men behind the curtain: */
unsigned int option_offset;
struct ipt_entry_match *m;
unsigned int mflags;
unsigned int used;
#ifdef NO_SHARED_LIBS
unsigned int loaded; /* simulate loading so options are merged properly */
#endif
};
附录【2】
struct iptables_target
{
struct iptables_target *next;
ipt_chainlabel name;
const char *version;
/* Size of target data. */
size_t size;
/* Size of target data relevent for userspace comparison purposes */
size_t userspacesize;
/* Function which prints out usage message. */
void (*help)(void);
/* Initialize the target. */
void (*init)(struct ipt_entry_target *t, unsigned int *nfcache);
/* Function which parses command options; returns true if it
ate an option */
int (*parse)(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
struct ipt_entry_target **target);
/* Final check; exit if not ok. */
void (*final_check)(unsigned int flags);
/* Prints out the target iff non-NULL: put space at end */
void (*print)(const struct ipt_ip *ip,
const struct ipt_entry_target *target, int numeric);
/* Saves the targinfo in parsable form to stdout. */
void (*save)(const struct ipt_ip *ip,
const struct ipt_entry_target *target);
/* Pointer to list of extra command-line options */
struct option *extra_opts;
/* Ignore these men behind the curtain: */
unsigned int option_offset;
struct ipt_entry_target *t;
unsigned int tflags;
unsigned int used;
#ifdef NO_SHARED_LIBS
unsigned int loaded; /* simulate loading so options are merged properly */
#endif
};
附录【3】
在iptables 命令中, 参数分为两部分Command Option
command : 是一个action , 包含append, check, delete insert 等动作(后面加chain )
option : 是依附于 command的 选项, 表示这个操作具体要做些什么, 或完成这个action后要做些什么。
哪个action 可以有那些option, 近下面的数组定义。
/* Table of legal combinations of commands and options. If any of the
* given commands make an option legal, that option is legal (applies to
* CMD_LIST and CMD_ZERO only).
* Key:
* + compulsory 必须的, 相当于MUST or MANDATE
* x illegal 非法的, 相当于 MUST NOT or ERROR
* optional 可选的。
*/
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
/* -n -s -d -p -j -v -x -i -o -f --line */
/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*LIST*/ {' ','x','x','x','x',' ',' ','x','x','x',' '},
/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
/*CHECK*/ {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'}
};
iptables --help
iptables v1.4.21
Usage: iptables -[ACD] chain rule-specification [options]
iptables -I chain [rulenum] rule-specification [options]
iptables -R chain rulenum rule-specification [options]
iptables -D chain rulenum [options]
iptables -[LS] [chain [rulenum]] [options]
iptables -[FZ] [chain] [options]
iptables -[NX] chain
iptables -E old-chain-name new-chain-name
iptables -P chain target [options]
iptables -h (print this help information)
Commands:
Either long or short options are allowed.
--append -A chain Append to chain
--check -C chain Check for the existence of a rule
--delete -D chain Delete matching rule from chain
--delete -D chain rulenum
Delete rule rulenum (1 = first) from chain
--insert -I chain [rulenum]
Insert in chain as rulenum (default 1=first)
--replace -R chain rulenum
Replace rule rulenum (1 = first) in chain
--list -L [chain [rulenum]]
List the rules in a chain or all chains
--list-rules -S [chain [rulenum]]
Print the rules in a chain or all chains
--flush -F [chain] Delete all rules in chain or all chains
--zero -Z [chain [rulenum]]
Zero counters in chain or all chains
--new -N chain Create a new user-defined chain
--delete-chain
-X [chain] Delete a user-defined chain
--policy -P chain target
Change policy on chain to target
--rename-chain
-E old-chain new-chain
Change chain name, (moving any references)
Options:
--ipv4 -4 Nothing (line is ignored by ip6tables-restore)
--ipv6 -6 Error (line is ignored by iptables-restore)
[!] --protocol -p proto protocol: by number or name, eg. `tcp'
[!] --source -s address[/mask][...]
source specification
[!] --destination -d address[/mask][...]
destination specification
[!] --in-interface -i input name[+]
network interface name ([+] for wildcard)
--jump -j target
target for rule (may load target extension)
--goto -g chain
jump to chain with no return
--match -m match
extended match (may load extension)
--numeric -n numeric output of addresses and ports
[!] --out-interface -o output name[+]
network interface name ([+] for wildcard)
--table -t table table to manipulate (default: `filter')
--verbose -v verbose mode
--wait -w wait for the xtables lock
--line-numbers print line numbers when listing
--exact -x expand numbers (display exact values)
[!] --fragment -f match second or further fragments only
--modprobe=
try to insert modules using this command
--set-counters PKTS BYTES set the counter during insert/append
[!] --version -V print package version.