分类: LINUX
2010-01-22 17:00:19
本章分析target。
所有表最后会有一个错误目标的规则;
自定义链会有一个错误目标的链头(规则);
所有链都有一个标准目标的链尾(规则),自定义链的verdict是RETURN,内建链的verdict是ACCEPT和DROP。
关于链的排列方式:
1) 在用户空间的链表形态:
TC_CREATE_CHAIN()可以看出,新链总是加在最后。
list_add_tail(&c->list, &(*handle)->chains);
但是如果新内建链来了呢?
2) 在内核空间的连续内存形态:
内建链在前面;自定义链在后面;
另外的说明参考上面。
/* Convenience structures */
struct ipt_error_target
{
STRUCT_ENTRY_TARGET t;
char error[TABLE_MAXNAMELEN];
};
#define STRUCT_STANDARD_TARGET struct ipt_standard_target
#define ipt_standard_target xt_standard_target // 内核里面定义的ipt_standard_target
struct xt_standard_target
{
struct xt_entry_target target;
int verdict;
};
/* Convenience structures */
struct iptcb_chain_start{
STRUCT_ENTRY e;
struct ipt_error_target name;
};
#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \
ALIGN(sizeof(struct ipt_error_target)))
链头,这个是只有自定义链太会有的。target是个错误目标。此rule不应该被用到。因为所有跳转到此链的规则,都会直接跳到此链的第二条规则。而不是这条错误的规则,参考iptcc_compile_rule()。
struct iptcb_chain_foot {
STRUCT_ENTRY e;
STRUCT_STANDARD_TARGET target;
};
#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \
ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
链尾,标准链和自定义链都会有的。它包含一个标准目标。标准目标在内核定义,包含一个xt_entry_target和一个verdict。
verdict对标准链来说是ACCEPT, DROP等;对自定义链来说是RETURN。
struct iptcb_chain_error {
STRUCT_ENTRY entry;
struct ipt_error_target target;
};
#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \
ALIGN(sizeof(struct ipt_error_target)))
只在iptcc_compile_table()里面用到了,在表的末尾加一个struct iptcb_chain_error。
/* Convenience structures */
struct iptcb_chain_start{
STRUCT_ENTRY e;
struct ipt_error_target name;
};
#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \
ALIGN(sizeof(struct ipt_error_target)))
链头,这个是只有自定义链太会有的。target是个错误目标。此rule不应该被用到。因为所有跳转到此链的规则,都会直接跳到此链的第二条规则。而不是这条错误的规则。
/* compile chain from cache into blob */
static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
{
int ret;
struct rule_head *r;
struct iptcb_chain_start *head;
struct iptcb_chain_foot *foot;
/* only user-defined chains have heaer */
if (!iptcc_is_builtin(c)) { // 只有自定义链才有此链头
/* put chain header in place */
head = (void *)repl->entries + c->head_offset;
head->e.target_offset = sizeof(STRUCT_ENTRY);
head->e.next_offset = IPTCB_CHAIN_START_SIZE;
strcpy(head->name.t.u.user.name, ERROR_TARGET); // 错误目标
head->name.t.u.target_size =
ALIGN(sizeof(struct ipt_error_target));
strcpy(head->name.error, c->name);
} else {
repl->hook_entry[c->hooknum-1] = c->head_offset;
repl->underflow[c->hooknum-1] = c->foot_offset;
}
/* iterate over rules */
list_for_each_entry(r, &c->rules, list) {
ret = iptcc_compile_rule(h, repl, r);
if (ret < 0)
return ret;
}
/* put chain footer in place */
foot = (void *)repl->entries + c->foot_offset;
foot->e.target_offset = sizeof(STRUCT_ENTRY);
foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
foot->target.target.u.target_size =
ALIGN(sizeof(STRUCT_STANDARD_TARGET));
/* builtin targets have verdict, others return */
if (iptcc_is_builtin(c))
foot->target.verdict = c->verdict;
else
foot->target.verdict = RETURN;
/* set policy-counters */
memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
return 0;
}
/* compile rule from cache into blob */
static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
{
/* handle jumps */
if (r->type == IPTCC_R_JUMP) { // 是JUMP。也就是说目标自定义的链
STRUCT_STANDARD_TARGET *t;
t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
/* memset for memcmp convenience on delete/replace */
memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
strcpy(t->target.u.user.name, STANDARD_TARGET);
/* Jumps can only happen to builtin chains, so we
* can safely assume that they always have a header */
t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE; // 设置verdict为第二条规则
} else if (r->type == IPTCC_R_FALLTHROUGH) {
STRUCT_STANDARD_TARGET *t;
t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
t->verdict = r->offset + r->size;
}
/* copy entry from cache to blob */
memcpy((char *)repl->entries+r->offset, r->entry, r->size);
return 1;
}
struct iptcb_chain_foot {
STRUCT_ENTRY e;
STRUCT_STANDARD_TARGET target;
};
#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \
ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
链尾,标准链和自定义链都会有的。它包含一个标准目标。标准目标在内核定义,包含一个xt_entry_target和一个verdict。
verdict对标准链来说是ACCEPT, DROP等;对自定义链来说是RETURN。
/* compile chain from cache into blob */
static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
{
int ret;
struct rule_head *r;
struct iptcb_chain_start *head;
struct iptcb_chain_foot *foot;
/* only user-defined chains have heaer */
if (!iptcc_is_builtin(c)) {
/* put chain header in place */
head = (void *)repl->entries + c->head_offset;
head->e.target_offset = sizeof(STRUCT_ENTRY);
head->e.next_offset = IPTCB_CHAIN_START_SIZE;
strcpy(head->name.t.u.user.name, ERROR_TARGET);
head->name.t.u.target_size =
ALIGN(sizeof(struct ipt_error_target));
strcpy(head->name.error, c->name);
} else {
repl->hook_entry[c->hooknum-1] = c->head_offset;
repl->underflow[c->hooknum-1] = c->foot_offset;
}
/* iterate over rules */
list_for_each_entry(r, &c->rules, list) {
ret = iptcc_compile_rule(h, repl, r);
if (ret < 0)
return ret;
}
/* put chain footer in place */
foot = (void *)repl->entries + c->foot_offset;
foot->e.target_offset = sizeof(STRUCT_ENTRY);
foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
foot->target.target.u.target_size =
ALIGN(sizeof(STRUCT_STANDARD_TARGET));
/* builtin targets have verdict, others return */
if (iptcc_is_builtin(c))
foot->target.verdict = c->verdict; // 标准链,这个值是在那里设置的?
如果链是表的默认链,那么表模板里面会给出这个值。比如packet_filter表;
/* LOCAL_IN */
{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
0,
sizeof(struct ipt_entry),
sizeof(struct ipt_standard),
0, { 0, 0 }, { } },
{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-NF_ACCEPT - 1 } },
如果不是表的默认链呢?
首先是可以通过case 'P':来调用TC_SET_POLICY()来设置链的默认处理结果的。
if (strcmp(policy, LABEL_ACCEPT) == 0)
c->verdict = -NF_ACCEPT - 1;
else if (strcmp(policy, LABEL_DROP) == 0)
c->verdict = -NF_DROP - 1;
else {
errno = EINVAL;
return 0;
}
规则插入的时候似乎会有设置:
TC_INSERT_ENTRY()
-> iptcc_map_target()
-> iptcc_standard_map()
-> t->verdict = verdict;
else
foot->target.verdict = RETURN; // 自定义链
/* set policy-counters */
memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
return 0;
}
struct iptcb_chain_error {
STRUCT_ENTRY entry;
struct ipt_error_target target;
};
#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \
ALIGN(sizeof(struct ipt_error_target)))
只在iptcc_compile_table()里面用到了,在表的末尾加一个struct iptcb_chain_error。
static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
{
struct chain_head *c;
struct iptcb_chain_error *error;
/* Second pass: copy from cache to offsets, fill in jumps */
list_for_each_entry(c, &h->chains, list) {
int ret = iptcc_compile_chain(h, repl, c);
if (ret < 0)
return ret;
}
/* Append error rule at end of chain */
error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
error->entry.target_offset = sizeof(STRUCT_ENTRY);
error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
error->target.t.u.user.target_size =
ALIGN(sizeof(struct ipt_error_target));
strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
strcpy((char *)&error->target.error, "ERROR");
return 1;
}
ipt_standard_target:
do_command():
case 'j':
set_option(&options, OPT_JUMP, &fw.ip.invflags,
invert);
jumpto = parse_target(optarg);
/* TRY_LOAD (may be chain name) */
target = find_target(jumpto, TRY_LOAD);
struct iptables_target *
find_target(const char *name, enum ipt_tryload tryload)
{
struct iptables_target *ptr;
/* Standard target? */
if (strcmp(name, "") == 0
|| strcmp(name, IPTC_LABEL_ACCEPT) == 0
|| strcmp(name, IPTC_LABEL_DROP) == 0
|| strcmp(name, IPTC_LABEL_QUEUE) == 0
|| strcmp(name, IPTC_LABEL_RETURN) == 0)
name = "standard"; // 内建规则
libipt_standard.c:
static
struct iptables_target standard = {
.next = NULL,
.name = "standard",
.version = IPTABLES_VERSION,
.size = IPT_ALIGN(sizeof(int)),
.userspacesize = IPT_ALIGN(sizeof(int)),
.help = &help,
.init = &init,
.parse = &parse,
.final_check = &final_check,
.print = NULL,
.save = &save,
.extra_opts = opts
};