else
sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
printf(FMT("%-19s ","%s "), buf);
}
/*输出目的地址及掩码*/
fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
printf(FMT("%-19s","-> %s"), "anywhere");
else {
if (format & FMT_NUMERIC)
sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
else
sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
printf(FMT("%-19s","-> %s"), buf);
}
if (format & FMT_NOTABLE)
fputs(" ", stdout);
/*输出扩展的MATCH*/
IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
/*输出扩展的TARGET*/
if (target) {
if (target->print)
/* Print the target information. */
target->print(&fw->ip, t, format & FMT_NUMERIC);
} else if (t->u.target_size != sizeof(*t))
printf("[%u bytes of unknown target data] ",
t->u.target_size - sizeof(*t));
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
}
函数分为三部份:
输出标准的match部份;
输出扩展的match部份,调用IPT_MATCH_ITERATE实现;
调用对应的target的print函数输出target部份。
match的输出
IPT_MATCH_ITERATE 宏用于实现扩展match的遍历。这个宏定义在内核include/Linux/Netfilter-ipv4/Ip_tables.h中:
#define IPT_MATCH_ITERATE(e, fn, args...) \
({ \
unsigned int __i; \
int __ret = 0; \
struct ipt_entry_match *__match; \
\
for (__i = sizeof(struct ipt_entry); \
__i < (e)->target_offset; \
__i += __match->u.match_size) { \
__match = (void *)(e) + __i; \
\
__ret = fn(__match , ## args); \ /*每找到一个match,就交由fn函数来处理,在print_firewall中,传递过来的是函数print_match*/
if (__ret != 0) \
break; \
} \
__ret; \
})
要理解这个宏,需要先了解规则的存储,前面提到过,因为match/target都是可变的,所以在内存中,采取了ip_entry+n*match+n*target,即在规则后,是连续的若干个match,而mathc后面,又是若干个target,在结构ip_entry中,成员u_int16_t target_offset;代表了target的偏移地址,即target的开始,match的结束。我们要查到当前规则对应的所有match,需要了解三个要素:
1、match从哪里开始:起始地址应该是 [当前规则地址+sizeof(struct ipt_entry)];
2、match从哪里结束:结束地址,应该是 [当前规则地址+target_offet];
3、每一个match的大小,在内核中,match对应的结构是ipt_entry_match,其成员u.match_size指明了当前match的大小;
这三点,对应了for循环:
for (__i = sizeof(struct ipt_entry); __i < (e)->target_offset; __i += __match->u.match_size)
这样,i就对应了某个match的偏移植,通过:
__match = (void *)(e) + __i;
就得到了match的地址。
再通过
__ret = fn(__match , ## args);
输出之。
fn函数是在print_firewall中,传递过来的是函数print_match。
static int
print_match(const struct ipt_entry_match *m,
const struct ipt_ip *ip,
int numeric)
{
/*根据match名称进行查找,返回一个iptables_match结构,然后调用其中封装的print函数输出该match的信息*/
struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
if (match) {
if (match->print)
match->print(ip, m, numeric);
else
printf("%s ", match->name);
} else {
if (m->u.user.name[0])
printf("UNKNOWN match `%s' ", m->u.user.name);
}
/* Don't stop iterating. */
return 0;
}
这里涉及到两个重要的结构:
struct ipt_entry_match:在内核中用于存储扩展match信息
struct ipt_entry_match
{
union {
struct {
u_int16_t match_size;
/* Used by userspace */
char name[IPT_FUNCTION_MAXNAMELEN];
} user;
struct {
u_int16_t match_size;
/* Used inside the kernel */
struct ipt_match *match;
} kernel;
/* Total length */
u_int16_t match_size;
} u;
unsigned char data[0];
};
struct iptables_match:用于用户级的match存储:
/* Include file for additions: new matches and targets. */
struct iptables_match
{
/* Match链,初始为NULL */
struct iptables_match *next;
/* Match名,和核心模块加载类似,作为动态链接库存在的Iptables Extension的命名规则为libipt_'name'.so */
ipt_chainlabel name;
/*版本信息,一般设为NETFILTER_VERSION */
const char *version;
/* Match数据的大小,必须用IPT_ALIGN()宏指定对界*/
size_t size;
/*由于内核可能修改某些域,因此size可能与确切的用户数据不同,这时就应该把不会被改变的数据放在数据区的前面部分,而这里就应该填写被改变的数据区大小;一般来说,这个值和size相同*/
size_t userspacesize;
/*当iptables要求显示当前match的信息时(比如iptables-m ip_ext -h),就会调用这个函数,输出在iptables程序的通用信息之后. */
void (*help)(void);
/*初始化,在parse之前调用. */
void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);
/*扫描并接收本match的命令行参数,正确接收时返回非0,flags用于保存状态信息*/
int (*parse)(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match);
/* 前面提到过这个函数,当命令行参数全部处理完毕以后调用,如果不正确,应该
退出(exit_error())*/
void (*final_check)(unsigned int flags);
/*当查询当前表中的规则时,显示使用了当前match的规则*/
void (*print)(const struct ipt_ip *ip,
const struct ipt_entry_match *match, int numeric);
/*按照parse允许的格式将本match的命令行参数输出到标准输出,用于iptables-save命令. */
void (*save)(const struct ipt_ip *ip,
const struct ipt_entry_match *match);
/* NULL结尾的参数列表,struct option与getopt(3)使用的结构相同*/
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
};
理解了这两个结构后,再来看find_match函数: