前面提到过,在内核中,handler指针指向了从内核中返回的对应的表的信息,handler对应的结构中,涉及到链的结构成员主要有两个:
struct chain_cache *cache_chain_heads;
struct chain_cache *cache_chain_iteration;
前者用于指向第一个链,后者指向当前链。而struct chain_cache的定义如下:
struct chain_cache
{
char name[TABLE_MAXNAMELEN]; /*链名*/
STRUCT_ENTRY *start; /*该链的第一条规则*/
STRUCT_ENTRY *end; /*该链的最后一条规则*/
};
理解了这两个成员,和结构struct chain_cache,再来理解链的遍历函数就不难了。所谓链的遍历,就是将handler对应成员的值取出来。
#define TC_FIRST_CHAIN iptc_first_chain
#define TC_NEXT_CHAIN iptc_next_chain
函数TC_FIRST_CHAIN用于返回第一个链:
/* Iterator functions to run through the chains. */
const char *
TC_FIRST_CHAIN(TC_HANDLE_T *handle)
{
/*链首为空,则返回NULL*/
if ((*handle)->cache_chain_heads == NULL
&& !populate_cache(*handle))
return NULL;
/*当前链的指针指向链表首部*/
(*handle)->cache_chain_iteration
= &(*handle)->cache_chain_heads[0];
/*返回链的名称*/
return (*handle)->cache_chain_iteration->name;
}
/* Iterator functions to run through the chains. Returns NULL at end. */
const char *
TC_NEXT_CHAIN(TC_HANDLE_T *handle)
{
/*很简单,用heads开始,用++就可以实现遍历了*/
(*handle)->cache_chain_iteration++;
if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
== (*handle)->cache_num_chains)
return NULL;
return (*handle)->cache_chain_iteration->name;
}
规则的遍历
当遍历到某个链的时候,接下来,就需要遍历当前链下的所有规则了,输出之了。前面叙述了链的遍历,那么规则的遍历,应该就是根据链的名称,找到对应的成员结构struct chain_cache ,这里面包含了当前链的第一条规则与最后一条规则的指针:
#define TC_FIRST_RULE iptc_first_rule
#define TC_NEXT_RULE iptc_next_rule
/* Get first rule in the given chain: NULL for empty chain. */
const STRUCT_ENTRY *
TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
{
struct chain_cache *c;
c = find_label(chain, *handle); /*根据链名,返回对应的struct chain_cache结构*/
if (!c) { /*没有找到,返回NULL*/
errno = ENOENT;
return NULL;
}
/* Empty chain: single return/policy rule */
if (c->start == c->end) /*如果是空链*/
return NULL;
(*handle)->cache_rule_end = c->end;
return c->start; /*返回链的首条规则*/
}
/* Returns NULL when rules run out. */
const STRUCT_ENTRY *
TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
{
if ((void *)prev + prev->next_offset
== (void *)(*handle)->cache_rule_end)
return NULL;
return (void *)prev + prev->next_offset;
}
要更解TC_NEXT_RULE函数是如何实现查找下一条规则的,需要首先理解STRUCT_ENTRY结构:
#define STRUCT_ENTRY struct ipt_entry
ipt_entry结构用于存储链的规则,每一个包过滤规则可以分成两部份:条件和动作。前者在Netfilter中,称为match,后者称之为target。Match又分为两部份,一部份为一些基本的元素,如来源/目的地址,进/出网口,协议等,对应了struct ipt_ip,我们常常将其称为标准的match,另一部份match则以插件的形式存在,是动态可选择,也允许第三方开发的,常常称为扩展的match,如字符串匹配,p2p匹配等。同样,规则的target也是可扩展的。这样,一条规则占用的空间,可以分为:struct ipt_ip+n*match+n*target,(n表示了其个数,这里的match指的是可扩展的match部份)。基于此,规则对应的结构如下:
/* 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; /*标准的match部份*/
/* Mark with fields that we care about. */
unsigned int nfcache;
/* Size of ipt_entry + matches */
u_int16_t target_offset; /*target的开始位置,是sizeof(ipt_entry+n*match)*/
/* Size of ipt_entry + matches + target */
u_int16_t next_offset; /*下一条规则相对于本条规则的位置,是sizeof(ipt_entry)加上所有的match,以及所有的target*/
/* Back pointer */
unsigned int comefrom;
/* Packet and byte counters. */
struct ipt_counters counters;
/* The matches (if any), then the target. */
unsigned char elems[0];
};
有了这样的基础,就不难理解遍历规则中,寻找下一条规则语句:
return (void *)prev + prev->next_offset;
即是本条规则加上下一条规则的偏移值。
输出规则
print_firewall 函数用于规则的输出:
print_firewall(i, iptc_get_target(i, handle), num++,format,*handle);
i:当前的规则;
iptc_get_target(i, handle):用于规则的target部份的处理;
num:规则序号;
format:输出格式;
handler:表的信息;
/* e is called `fw' here for hysterical raisins */
static void
print_firewall(const struct ipt_entry *fw,
const char *targname,
unsigned int num,
unsigned int format,
const iptc_handle_t handle)
{
struct iptables_target *target = NULL;
const struct ipt_entry_target *t;
u_int8_t flags;
char buf[BUFSIZ];
if (!iptc_is_chain(targname, handle))
target = find_target(targname, TRY_LOAD);
else
target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
t = ipt_get_target((struct ipt_entry *)fw);
flags = fw->ip.flags;
if (format & FMT_LINENUMBERS) /*输出行号*/
printf(FMT("%-4u ", "%u "), num+1);
if (!(format & FMT_NOCOUNTS)) { /*详细模式,列出计数器*/
print_num(fw->counters.pcnt, format); /*匹配当前规则的数据包个数*/
print_num(fw->counters.bcnt, format); /*--------------------大小*/
}
/*输出目标名称*/
if (!(format & FMT_NOTARGET)) /*目标名称,即拦截、通过等动作*/
printf(FMT("%-9s ", "%s "), targname);
/*输出协议名*/
fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
{
char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
if (pname)
printf(FMT("%-5s", "%s "), pname);
else
printf(FMT("%-5hu", "%hu "), fw->ip.proto);
}
/*输出选项字段*/
if (format & FMT_OPTIONS) {
if (format & FMT_NOTABLE)
fputs("opt ", stdout);
fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); //#define IP_FW_INV_FRAG 0x0080 /* Invert the sense of IP_FW_F_FRAG. */
fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); //#define IP_FW_F_FRAG 0x0004 /* Set if rule is a fragment rule */
fputc(' ', stdout);
}
if (format & FMT_VIA) {
char iface[IFNAMSIZ+2];
if (fw->ip.invflags & IPT_INV_VIA_IN) { /*输入端口取反标志*/
iface[0] = '!'; /*设置取反标志符*/
iface[1] = '\0';
}
else iface[0] = '\0';
if (fw->ip.iniface[0] != '\0') {
strcat(iface, fw->ip.iniface);
}
else if (format & FMT_NUMERIC) strcat(iface, "*");
else strcat(iface, "any");
printf(FMT(" %-6s ","in %s "), iface); /*输出输入端口*/
if (fw->ip.invflags & IPT_INV_VIA_OUT) { /*输出端口取反标志*/
iface[0] = '!'; /*设置取反标志符*/
iface[1] = '\0';
}
else iface[0] = '\0';
if (fw->ip.outiface[0] != '\0') {
strcat(iface, fw->ip.outiface);
}
else if (format & FMT_NUMERIC) strcat(iface, "*");
else strcat(iface, "any");
printf(FMT("%-6s ","out %s "), iface); /*输出输出端口*/
} /*end print in/out interface */
/*输出源地址及掩码*/
fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); /*源地址取反标志*/
if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) /*源地址为任意*/
printf(FMT("%-19s ","%s "), "anywhere");
else {
if (format & FMT_NUMERIC)
sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));