1.1 表的查找
再回到iptc_init 函数上来,它根据表名,从内核获取对应的表的相关信息,handle是一个iptc_handle_t类型的指针,在libiptc.c中,有如下定义:
/* Transparent handle type. */
typedef struct iptc_handle *iptc_handle_t;
在Libip4tc中:
#define STRUCT_TC_HANDLE struct iptc_handle
在Libiptc.c中,可以找到STRUCT_TC_HANDLE的定义:
STRUCT_TC_HANDLE
{
/* Have changes been made? */
int changed;
/* Size in here reflects original state. */
STRUCT_GETINFO info;
struct counter_map *counter_map;
/* Array of hook names */
const char **hooknames;
/* Cached position of chain heads (NULL = no cache). */
unsigned int cache_num_chains;
unsigned int cache_num_builtins;
/* Rule iterator: terminal rule */
STRUCT_ENTRY *cache_rule_end;
/* Number in here reflects current state. */
unsigned int new_number;
STRUCT_GET_ENTRIES entries;
};
再来看看iptc_init函数,同样在在Libip4tc中,有如下定义:
#define TC_INIT iptc_init
在Libiptc.c中,可以看到函数的实现,基本上iptables与内核的交互,都是使用setsockopt函数来实现的,对于获取取规是信息来说,标志位是SO_GET_INFO,而从内核返回回来的规则信息是一个STRUCT_GETINFO结构:
TC_HANDLE_T TC_INIT(const char *tablename)
{
TC_HANDLE_T h;
STRUCT_GETINFO info;
unsigned int i;
int tmp;
socklen_t s;
iptc_fn = TC_INIT;
if (sockfd != -1)
close(sockfd);
/*为获取信息打开一个套接字接口*/
sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
if (sockfd < 0)
return NULL;
s = sizeof(info);
if (strlen(tablename) >= TABLE_MAXNAMELEN) {
errno = EINVAL;
return NULL;
}
strcpy(info.name, tablename);
/*获取规则信息*/
if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
return NULL;
if ((h = alloc_handle(info.name, info.size, info.num_entries))
== NULL)
return NULL;
/* Too hard --RR */
#if 0
sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
dynlib = dlopen(pathname, RTLD_NOW);
if (!dynlib) {
errno = ENOENT;
return NULL;
}
h->hooknames = dlsym(dynlib, "hooknames");
if (!h->hooknames) {
errno = ENOENT;
return NULL;
}
#else
h->hooknames = hooknames;
#endif
/* Initialize current state */
h->info = info;
h->new_number = h->info.num_entries;
for (i = 0; i < h->info.num_entries; i++)
h->counter_map[i]
= ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
h->entries.size = h->info.size;
tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
&tmp) < 0) {
free(h);
return NULL;
}
CHECK(h);
return h;
}
函数为h分配空间,然后赋予相应的值。要理解这个函数,还需要了解STRUCT_GETINFO结构和分配内存空间的函数alloc_handle。
#define STRUCT_GETINFO struct ipt_getinfo
/* The argument to IPT_SO_GET_INFO */
struct ipt_getinfo
{
/* Which table: caller fills this in. */
char name[IPT_TABLE_MAXNAMELEN];
/* Kernel fills these in. */
/* Which hook entry points are valid: bitmask */
unsigned int valid_hooks;
/* Hook entry points: one per netfilter hook. */
unsigned int hook_entry[NF_IP_NUMHOOKS];
/* Underflow points. */
unsigned int underflow[NF_IP_NUMHOOKS];
/* Number of entries */
unsigned int num_entries;
/* Size of entries. */
unsigned int size;
};
/* Allocate handle of given size */
static TC_HANDLE_T
alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
{
size_t len;
TC_HANDLE_T h;
len = sizeof(STRUCT_TC_HANDLE)
+ size
+ num_rules * sizeof(struct counter_map);
if ((h = malloc(len)) == NULL) {
errno = ENOMEM;
return NULL;
}
h->changed = 0;
h->cache_num_chains = 0;
h->cache_chain_heads = NULL;
h->counter_map = (void *)h
+ sizeof(STRUCT_TC_HANDLE)
+ size;
strcpy(h->info.name, tablename);
strcpy(h->entries.name, tablename);
return h;
}
函数list_entries用于显示表下边的链:
/*显示某table下的chain*/
static int
list_entries(const ipt_chainlabel chain, int verbose, int numeric,
int expanded, int linenumbers, iptc_handle_t *handle)
{
int found = 0;
unsigned int format;
const char *this;
format = FMT_OPTIONS; /*设置输出格式*/
if (!verbose) /*详细输出模式,,对应-v ,显示匹配的包的数目,包的大小等*/
format |= FMT_NOCOUNTS;
else
format |= FMT_VIA;
if (numeric) /*对应-n,以数字的形式输出地址和端口*/
format |= FMT_NUMERIC;
if (!expanded) /*对应-x,expand numbers (display exact values)*/
format |= FMT_KILOMEGAGIGA;
if (linenumbers) /*输出行的编号*/
format |= FMT_LINENUMBERS;
for (this = iptc_first_chain(handle); /*遍历当前table的所有chain*/
this;
this = iptc_next_chain(handle))
{
const struct ipt_entry *i;
unsigned int num;
if (chain && strcmp(chain, this) != 0) /*匹配指定chain名,这里用chain &&,即若不指定chain,输出所有chain*/
continue;
if (found) printf("/n");
print_header(format, this, handle); /*输出标头*/
i = iptc_first_rule(this, handle); /*移至当前chain的第一条规则*/
num = 0;
while (i) {
print_firewall(i, /*输出当前规则*/
iptc_get_target(i, handle),
num++,
format,
*handle);
i = iptc_next_rule(i, handle); /*移至下一条规则*/
}
found = 1;
}
errno = ENOENT;
return found;
}
可见,在函数中,由iptc_first_chain和 iptc_next_chain实现了遍历,iptc_first_rule和iptc_next_rule实现了链中规是的遍 历,print_firewall函数在遍历到规则的时候,向终端输出防火墙规则,其第二个参数iptc_get_target又用于获取规则的 target。