分类: LINUX
2010-01-22 16:59:34
本章主要是讲用户空间match以及match的注册,包括:
1. match结构体形态;
2. 以icmp match来说明match里面的parse函数;
3. match的注册过程;
/* Include file for additions: new matches and targets. */
struct iptables_match
{
struct iptables_match *next;
ipt_chainlabel name;
/* Revision of match (0 by default). */
u_int8_t revision;
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; // 用于parse函数内部
/* Ignore these men behind the curtain: */
unsigned int option_offset;
struct ipt_entry_match *m;
unsigned int mflags; // 用于final_check,作为函数的参数
#ifdef NO_SHARED_LIBS
unsigned int loaded; /* simulate loading so options are merged properly */
#endif
};
例子:
static struct iptables_match icmp = {
.next = NULL,
.name = "icmp", // 库的文件名(也就是libipt_icmp)
.version = IPTABLES_VERSION, // iptables 的版本
.size = IPT_ALIGN(sizeof(struct ipt_icmp)), // 保持用户态程序和核心态
.userspacesize = IPT_ALIGN(sizeof(struct ipt_icmp)), // 共享结构的大小一致性
.help = &help, // 'iptables -m module -h' 时使用
.init = &init, // 初始化一些特定的东西
.parse = &parse, // 输入新规则时,写入我们将共享给kernel的信息
.final_check = &final_check, // 用于验证参数的合法性
.print = &print, // 'iptables -L'显示规则时使用
.save = &save, // 'iptables-save'保存规则中本match时使用的
.extra_opts = opts // 将每个参数映射到一个值,用于struct iptables_match中parse函数
};
注册:
register_match(&icmp); // 注册方法参考下面
函数parse():
/* Function which parses command options; returns true if it
ate an option */
static int
parse(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match)
此函数主要是根据argv来填充(*match)->data;
{
struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
switch (c) {
case '1':
check_inverse(optarg, &invert, &optind, 0);
parse_icmp(argv[optind-1], &icmpinfo->type,
icmpinfo->code);
if (invert)
icmpinfo->invflags |= IPT_ICMP_INV; // 填充match
break;
default:
return 0;
}
return 1;
}
关于上面的case语句,实际根据是iptables_match 结构体里的extra_opts来处理的。例如:
static struct option opts[] = {
{ "icmp-type", 1, 0, '1' }, // 后面那个是被映射的值,参考libipt_dscp.c。
{0}
};
static struct iptables_match icmp = {
.next = NULL,
.name = "icmp",
.version = IPTABLES_VERSION,
.size = IPT_ALIGN(sizeof(struct ipt_icmp)),
.userspacesize = IPT_ALIGN(sizeof(struct ipt_icmp)),
.help = &help,
.init = &init,
.parse = &parse,
.final_check = &final_check,
.print = &print,
.save = &save,
.extra_opts = opts
};
在加载struct iptables_match icmp的时候,做过某些处理。
void
register_match(struct iptables_match *me)
{
struct iptables_match **i, *old;
if (strcmp(me->version, program_version) != 0) { // 如果版本和iptables版本不一致,退出
fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
program_name, me->name, me->version, program_version);
exit(1);
}
/* Revision field stole a char from name. */
if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) { // match名字过长,退出
fprintf(stderr, "%s: target `%s' has invalid name\n",
program_name, me->name);
exit(1);
}
old = find_match(me->name, DURING_LOAD, NULL); // 寻找此match是否已经存在
if (old) { // 参考下面
if (old->revision == me->revision) {
fprintf(stderr,
"%s: match `%s' already registered.\n",
program_name, me->name);
exit(1);
}
/* Now we have two (or more) options, check compatibility. */
if (compatible_match_revision(old->name, old->revision) // 如果已经存在的是适合的
&& old->revision > me->revision) // 并且版本比现在要加载的还新
return;
/* Replace if compatible. */
if (!compatible_match_revision(me->name, me->revision)) // 如果新的不适合
return;
/* Delete old one. */
for (i = &iptables_matches; *i!=old; i = &(*i)->next);
*i = old->next;
}
if (me->size != IPT_ALIGN(me->size)) {
fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
program_name, me->name, (unsigned int)me->size);
exit(1);
}
/* Append to list. */
for (i = &iptables_matches; *i; i = &(*i)->next);
me->next = NULL;
*i = me;
me->m = NULL;
me->mflags = 0;
}
struct iptables_match *
find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches)
{
struct iptables_match *ptr;
for (ptr = iptables_matches; ptr; ptr = ptr->next) { // 从已经加载的列表里面寻找
if (strcmp(name, ptr->name) == 0) // 找到(名字相同)则跳出
break;
}
#ifndef NO_SHARED_LIBS // 如果使用动态库形式
if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) { // 如果已加载列表中不存在,
char path[strlen(lib_dir) + sizeof("/libipt_.so") // 并且标志不为DONT_LOAD
+ strlen(name)]; // 或DURING_LOAD
sprintf(path, "%s/libipt_%s.so", lib_dir, name);
if (dlopen(path, RTLD_NOW)) { // 打开动态库
/* Found library. If it didn't register itself,
maybe they specified target as match. */
ptr = find_match(name, DONT_LOAD, NULL); // 不再次打开动态库的方式寻找
if (!ptr) // 如果还是没找到,那么报告不能加载此match
exit_error(PARAMETER_PROBLEM,
"Couldn't load match `%s'\n",
name);
} else if (tryload == LOAD_MUST_SUCCEED)
exit_error(PARAMETER_PROBLEM,
"Couldn't load match `%s':%s\n",
name, dlerror());
}
#else
if (ptr && !ptr->loaded) {
if (tryload != DONT_LOAD)
ptr->loaded = 1;
else
ptr = NULL;
}
if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
exit_error(PARAMETER_PROBLEM,
"Couldn't find match `%s'\n", name);
}
#endif
if (ptr && matches) {
struct iptables_rule_match **i;
struct iptables_rule_match *newentry;
newentry = fw_malloc(sizeof(struct iptables_rule_match));
for (i = matches; *i; i = &(*i)->next);
newentry->match = ptr;
newentry->next = NULL;
*i = newentry;
}
return ptr;
}