分类: LINUX
2010-01-22 16:53:24
本章主要是讲iptables命令的整个处理过程,包括:
1. 命令行参数解析;
2. 从kernel取得初始规则集;
3. 显示规则;或如果要设置,那么组装要发送到kernel的数据结构;
4. 发送到kernel;
default:
/* FIXME: This scheme doesn't allow two of the same
matches --RR */
if (!target
|| !(target->parse(c - target->option_offset, // target数据的取得
argv, invert,
&target->tflags,
&fw, &target->t))) {
for (matchp = matches; matchp; matchp = matchp->next) {
if (matchp->match->parse(c - matchp->match->option_offset, // match数据的取得
argv, invert,
&matchp->match->mflags,
&fw,
&fw.nfcache,
&matchp->match->m))
break;
}
m = matchp ? matchp->match : NULL;
/* If you listen carefully, you can
actually hear this code suck. */
/* some explanations (after four different bugs
* in 3 different releases): If we encountere a
* parameter, that has not been parsed yet,
* it's not an option of an explicitly loaded
* match or a target. However, we support
* implicit loading of the protocol match
* extension. '-p tcp' means 'l4 proto 6' and
* at the same time 'load tcp protocol match on
* demand if we specify --dport'.
*
* To make this work, we need to make sure:
* - the parameter has not been parsed by
* a match (m above)
* - a protocol has been specified
* - the protocol extension has not been
* loaded yet, or is loaded and unused
* [think of iptables-restore!]
* - the protocol extension can be successively
* loaded
*/
if (m == NULL
&& protocol
&& (!find_proto(protocol, DONT_LOAD,
options&OPT_NUMERIC, NULL)
|| (find_proto(protocol, DONT_LOAD,
options&OPT_NUMERIC, NULL)
&& (proto_used == 0))
)
&& (m = find_proto(protocol, TRY_LOAD,
options&OPT_NUMERIC, &matches))) {
/* Try loading protocol */
size_t size;
proto_used = 1;
size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+ m->size;
m->m = fw_calloc(1, size);
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
set_revision(m->m->u.user.name,
m->revision);
if (m->init != NULL)
m->init(m->m, &fw.nfcache);
opts = merge_options(opts,
m->extra_opts, &m->option_offset);
optind--;
continue;
}
if (!m)
exit_error(PARAMETER_PROBLEM,
"Unknown arg `%s'",
argv[optind-1]);
}
}
invert = FALSE;
}
for (matchp = matches; matchp; matchp = matchp->next) // 应该是通过动态库,读取了很多match
matchp->match->final_check(matchp->match->mflags); // 检查
if (target) // 有一个target
target->final_check(target->tflags); // 检查
/* Fix me: must put inverse options checking here --MN */
if (optind < argc)
exit_error(PARAMETER_PROBLEM,
"unknown arguments found on commandline");
if (!command)
exit_error(PARAMETER_PROBLEM, "no command specified");
if (invert)
exit_error(PARAMETER_PROBLEM,
"nothing appropriate following !");
if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
if (!(options & OPT_DESTINATION))
dhostnetworkmask = "
if (!(options & OPT_SOURCE))
shostnetworkmask = "
}
if (shostnetworkmask)
parse_hostnetworkmask(shostnetworkmask, &saddrs,
&(fw.ip.smsk), &nsaddrs);
if (dhostnetworkmask)
parse_hostnetworkmask(dhostnetworkmask, &daddrs,
&(fw.ip.dmsk), &ndaddrs);
if ((nsaddrs > 1 || ndaddrs > 1) &&
(fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
" source or destination IP addresses");
if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
"specify a unique address");
generic_opt_check(command, options);
if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
exit_error(PARAMETER_PROBLEM,
"chain name `%s' too long (must be under %i chars)",
chain, IPT_FUNCTION_MAXNAMELEN);
/* only allocate handle if we weren't called with a handle */
if (!*handle)
*handle = iptc_init(*table);
根据table的名称,让handle指针指向相应的表的地址空间,也就是把对应表的所有信息从内核中取出来
#define TC_INIT iptc_init
至于函数的实体TC_INIT(),下面有详细解释
/* try to insmod the module if iptc_init failed */
if (!*handle && iptables_insmod("ip_tables", modprobe) != -1)
*handle = iptc_init(*table);
如果获取换败,将试着插入模块,再次获取
if (!*handle)
exit_error(errno == EPERM ? OTHER_PROBLEM : VERSION_PROBLEM,
"can't initialize iptables table `%s': %s",
*table, iptc_strerror(errno));
if (command == CMD_APPEND
|| command == CMD_DELETE
|| command == CMD_INSERT
|| command == CMD_REPLACE) {
if (strcmp(chain, "PREROUTING") == 0
|| strcmp(chain, "INPUT") == 0) {
/* -o not valid with incoming packets. */
if (options & OPT_VIANAMEOUT)
exit_error(PARAMETER_PROBLEM,
"Can't use -%c with %s\n",
opt2char(OPT_VIANAMEOUT),
chain);
}
if (strcmp(chain, "POSTROUTING") == 0
|| strcmp(chain, "OUTPUT") == 0) {
/* -i not valid with outgoing packets */
if (options & OPT_VIANAMEIN)
exit_error(PARAMETER_PROBLEM,
"Can't use -%c with %s\n",
opt2char(OPT_VIANAMEIN),
chain);
}
if (target && iptc_is_chain(jumpto, *handle)) {
printf("Warning: using chain %s, not extension\n",
jumpto);
if (target->t)
free(target->t);
target = NULL;
}
/* If they didn't specify a target, or it's a chain
name, use standard. */
if (!target
&& (strlen(jumpto) == 0
|| iptc_is_chain(jumpto, *handle))) {
size_t size;
target = find_target(IPT_STANDARD_TARGET,
LOAD_MUST_SUCCEED);
size = sizeof(struct ipt_entry_target)
+ target->size;
target->t = fw_calloc(1, size);
target->t->u.target_size = size;
strcpy(target->t->u.user.name, jumpto);
if (!iptc_is_chain(jumpto, *handle))
set_revision(target->t->u.user.name,
target->revision);
if (target->init != NULL)
target->init(target->t, &fw.nfcache);
}
if (!target) {
/* it is no chain, and we can't load a plugin.
* We cannot know if the plugin is corrupt, non
* existant OR if the user just misspelled a
* chain. */
#ifdef IPT_F_GOTO
if (fw.ip.flags & IPT_F_GOTO)
exit_error(PARAMETER_PROBLEM,
"goto '%s' is not a chain\n", jumpto);
#endif
find_target(jumpto, LOAD_MUST_SUCCEED);
} else {
e = generate_entry(&fw, matches, target->t); // 生成规则
free(target->t);
}
}
switch (command) {
case CMD_APPEND:
ret = append_entry(chain, e, // 添加规则,问题是向哪里添加?
nsaddrs, saddrs, ndaddrs, daddrs, // 解释在下面。感觉只是加在了chain上
options&OPT_VERBOSE,
handle);
break;
case CMD_DELETE:
ret = delete_entry(chain, e,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE,
handle, matches);
break;
case CMD_DELETE_NUM:
ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
break;
case CMD_REPLACE:
ret = replace_entry(chain, e, rulenum - 1,
saddrs, daddrs, options&OPT_VERBOSE,
handle);
break;
case CMD_INSERT:
ret = insert_entry(chain, e, rulenum - 1,
nsaddrs, saddrs, ndaddrs, daddrs,
options&OPT_VERBOSE,
handle);
break;
case CMD_LIST:
ret = list_entries(chain, // 显示某个表的所有规则,下面解释
options&OPT_VERBOSE,
options&OPT_NUMERIC,
options&OPT_EXPANDED,
options&OPT_LINENUMBERS,
handle); // 不需要在指定表了,因为handle就属于一个表
break;
case CMD_FLUSH:
ret = flush_entries(chain, options&OPT_VERBOSE, handle);
break;
case CMD_ZERO:
ret = zero_entries(chain, options&OPT_VERBOSE, handle);
break;
case CMD_LIST|CMD_ZERO:
ret = list_entries(chain, // 下面解释
options&OPT_VERBOSE,
options&OPT_NUMERIC,
options&OPT_EXPANDED,
options&OPT_LINENUMBERS,
handle);
if (ret)
ret = zero_entries(chain,
options&OPT_VERBOSE, handle);
break;
case CMD_NEW_CHAIN:
ret = iptc_create_chain(chain, handle); // 通过自定义链,对command进行举例说明
break;
case CMD_DELETE_CHAIN:
ret = delete_chain(chain, options&OPT_VERBOSE, handle);
break;
case CMD_RENAME_CHAIN:
ret = iptc_rename_chain(chain, newname, handle);
break;
case CMD_SET_POLICY:
ret = iptc_set_policy(chain, policy, NULL, handle);
break;
default:
/* We should never reach this... */
exit_tryhelp(2);
}
if (verbose > 1)
dump_entries(*handle);
clear_rule_matches(&matches);
if (e != NULL) {
free(e);
e = NULL;
}
free(saddrs);
free(daddrs);
free_opts(1);
return ret;
}
static void
add_command(unsigned int *cmd, const int newcmd, const int othercmds,
int invert)
{
if (invert) // 禁止取反
exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
if (*cmd & (~othercmds))
exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
*cmd |= newcmd; // 设置标志位
}
static void
set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
int invert)
{
if (*options & option)
exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
opt2char(option));
*options |= option; // 设置标志位
if (invert) { // 如果取反
unsigned int i;
for (i = 0; 1 << i != option; i++);
if (!inverse_for_options[i]) // 检查是否允许取反
exit_error(PARAMETER_PROBLEM,
"cannot have ! before -%c",
opt2char(option));
*invflg |= inverse_for_options[i]; // 允许的话,取反
}
}
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")
+ strlen(name)];
sprintf(path, "%s/libipt_%s.so", lib_dir, name);
if (dlopen(path, RTLD_NOW)) { // 打开管理match的动态链接库
/* Found library. If it didn't register itself, // 打开库时会调用构造函数
maybe they specified target as match. */
例子:libipt_addrtype.c:
void __attribute((constructor)) my_init(void)
{
register_match(&addrtype);
}
ptr = find_match(name, DONT_LOAD, NULL); // 再次查找,应该能找到了。
if (!ptr)
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;
}