分类: LINUX
2010-01-22 16:52:20
本章主要是讲iptables命令的整个处理过程,包括:
1. 命令行参数解析;
2. 从kernel取得初始规则集;
3. 显示规则;或如果要设置,那么组装要发送到kernel的数据结构;
4. 发送到kernel;
规则相关说明如下:
1. 规则数据首先从内核读取,存放在handle.entries里面;
2. 将其解析为「チェーン(struct chain_head)---ルール(struct rule_head)」的形式
3. 如果要向内核提交规则变更请求,那么需要将其组装为repl。这里将看到
a) iptcc_compile_table在表尾加ERROR_TARGET
b) iptcc_compile_chain是如何构筑每条链的,包括对自定义链,加链头的error_target以及链尾的RETURN STANDARD_TARGET等。
4. 提交内核
iptables-standalone.c
main()
-> do_command()
-> iptc_commit()
#ifdef IPTABLES_MULTI
int
iptables_main(int argc, char *argv[])
#else
int
main(int argc, char *argv[])
#endif
{
int ret;
char *table = "filter";
iptc_handle_t handle = NULL;
program_name = "iptables";
program_version = IPTABLES_VERSION;
lib_dir = getenv("IPTABLES_LIB_DIR");
if (!lib_dir)
lib_dir = IPT_LIB_DIR;
#ifdef NO_SHARED_LIBS
init_extensions();
#endif
ret = do_command(argc, argv, &table, &handle); // 默认的表是filter,传递一个NULL的
if (ret) // iptc_handle_t handle进去
ret = iptc_commit(&handle); // 要发生的动作都填充在handle里面了
// 向内核发送动作请求,会首先判断if (!(*handle)->changed)
if (!ret)
fprintf(stderr, "iptables: %s\n",
iptc_strerror(errno));
exit(!ret);
}
int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
table是”filter”,后面会通过判断是否有-t选项来修改;
handle为NULL;
如果是显示,则不会改变*handle)->changed;
如果是要设置,则会改变*handle)->changed;那么后面iptc_commit()就会向内核发出setsockopt;
{
struct ipt_entry fw, *e = NULL;
int invert = 0;
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *daddrs = NULL;
int c, verbose = 0;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
unsigned int rulenum = 0, options = 0, command = 0; // 参数位变量;命令位变量
const char *pcnt = NULL, *bcnt = NULL;
int ret = 1;
struct iptables_match *m;
struct iptables_rule_match *matches = NULL; // 下面会在-m选项那边改
struct iptables_rule_match *matchp;
struct iptables_target *target = NULL; // 下面会在-j选项那边改。指定目标(动作)或同一个表内的链
struct iptables_target *t;
const char *jumpto = "";
char *protocol = NULL;
const char *modprobe = NULL;
int proto_used = 0;
memset(&fw, 0, sizeof(fw));
/* re-set optind to
* a second time */
optind = 0;
/* clear mflags in case do_command gets called a second time
* (we clear the global list of all matches for security)*/
for (m = iptables_matches; m; m = m->next) // 全局变量初始化
m->mflags = 0;
for (t = iptables_targets; t; t = t->next) { // 全局变量初始化
t->tflags = 0;
t->used = 0;
}
/* Keeping track of external matches and targets: linked lists. */
struct iptables_match *iptables_matches = NULL;
struct iptables_target *iptables_targets = NULL;
/* Suppress error messages: we may add new options if we
demand-load a protocol. */
opterr = 0;
while ((c = getopt_long(argc, argv,
"-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:",
opts, NULL)) != -1) {
switch (c) {
/*
* Command selection
*/
case 'A':
add_command(&command, CMD_APPEND, CMD_NONE,
invert);
chain = optarg;
break;
case 'D':
add_command(&command, CMD_DELETE, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
break;
case 'R':
add_command(&command, CMD_REPLACE, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
else
exit_error(PARAMETER_PROBLEM,
"-%c requires a rule number",
cmd2char(CMD_REPLACE));
break;
case 'I':
add_command(&command, CMD_INSERT, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
case 'L': // 列出当前存在的规则
add_command(&command, CMD_LIST, CMD_ZERO, // 后面详细讲此函数
invert); // 其实就是在command里面设置CMD_LIST标志位,并检查冲突
if (optarg) chain = optarg; // 取得需要列出的链的名称
else if (optind < argc && argv[optind][0] != '-' // 什么情况下optarg会为NULL?
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'F':
add_command(&command, CMD_FLUSH, CMD_NONE,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'Z':
add_command(&command, CMD_ZERO, CMD_LIST,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'N':
if (optarg && (*optarg == '-' || *optarg == '!')) // 操作数,也就是链名不能为’-’或’!’
exit_error(PARAMETER_PROBLEM,
"chain name not allowed to start "
"with `%c'\n", *optarg);
if (find_target(optarg, TRY_LOAD)) // 也不能和target名字一样。
exit_error(PARAMETER_PROBLEM, // 实际也不能和match的名字一样
"chain name may not clash " // 否则find_target这里通不过
"with target name\n");
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
invert); // #define CMD_NEW_CHAIN 0x0100U
chain = optarg; // 取得链名
break;
case 'X':
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;
case 'E':
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
newname = argv[optind++];
else
exit_error(PARAMETER_PROBLEM,
"-%c requires old-chain-name and "
"new-chain-name",
cmd2char(CMD_RENAME_CHAIN));
break;
case 'P':
add_command(&command, CMD_SET_POLICY, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
policy = argv[optind++];
else
exit_error(PARAMETER_PROBLEM,
"-%c requires a chain and a policy",
cmd2char(CMD_SET_POLICY));
break;
case 'h':
if (!optarg)
optarg = argv[optind];
/* iptables -p icmp -h */
if (!matches && protocol)
find_match(protocol, TRY_LOAD, &matches);
exit_printhelp(matches);
/*
* Option selection
*/
case 'p':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
invert);
/* Canonicalize into lower case */
for (protocol = argv[optind-1]; *protocol; protocol++)
*protocol = tolower(*protocol);
protocol = argv[optind-1];
fw.ip.proto = parse_protocol(protocol);
if (fw.ip.proto == 0
&& (fw.ip.invflags & IPT_INV_PROTO))
exit_error(PARAMETER_PROBLEM,
"rule would never match protocol");
break;
case 's':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_SOURCE, &fw.ip.invflags, // 设置option位变量
invert); // 下面详细讲
shostnetworkmask = argv[optind-1];
break;
case 'd':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
invert);
dhostnetworkmask = argv[optind-1];
break;
#ifdef IPT_F_GOTO
case 'g':
set_option(&options, OPT_JUMP, &fw.ip.invflags,
invert);
fw.ip.flags |= IPT_F_GOTO;
jumpto = parse_target(optarg);
break;
#endif
case 'j': // 设置target
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); // 得到target。find_target函数也要讲
if (target) {
size_t size;
size = IPT_ALIGN(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);
set_revision(target->t->u.user.name,
target->revision);
if (target->init != NULL)
target->init(target->t, &fw.nfcache);
opts = merge_options(opts, target->extra_opts, &target->option_offset);
}
break;
case 'i':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
invert);
parse_interface(argv[optind-1],
fw.ip.iniface,
fw.ip.iniface_mask);
break;
case 'o':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
invert);
parse_interface(argv[optind-1],
fw.ip.outiface,
fw.ip.outiface_mask);
break;
case 'f':
set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
invert);
fw.ip.flags |= IPT_F_FRAG;
break;
case 'v':
if (!verbose)
set_option(&options, OPT_VERBOSE,
&fw.ip.invflags, invert);
verbose++;
break;
case 'm': { // 设置match
size_t size;
if (invert) // 禁止取反
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag before --match");
m = find_match(optarg, LOAD_MUST_SUCCEED, &matches); // 重要的函数,下面讲
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);
}
break;
case 'n':
set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
invert);
break;
case 't':
if (invert) // table不能指定!
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag before --table");
*table = argv[optind-1]; // 修改table名
break;
case 'x':
set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
invert);
break;
case 'V':
if (invert)
printf("Not %s ;-)\n", program_version);
else
printf("%s v%s\n",
program_name, program_version);
exit(0);
case '0':
set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
invert);
break;
case 'M':
modprobe = optarg;
break;
case 'c':
set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
invert);
pcnt = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
bcnt = argv[optind++];
else
exit_error(PARAMETER_PROBLEM,
"-%c requires packet and byte counter",
opt2char(OPT_COUNTERS));
if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1)
exit_error(PARAMETER_PROBLEM,
"-%c packet counter not numeric",
opt2char(OPT_COUNTERS));
if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1)
exit_error(PARAMETER_PROBLEM,
"-%c byte counter not numeric",
opt2char(OPT_COUNTERS));
break;
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {
if (invert)
exit_error(PARAMETER_PROBLEM,
"multiple consecutive ! not"
" allowed");
invert = TRUE;
optarg[0] = '\0';
continue;
}
printf("Bad argument `%s'\n", optarg);
exit_tryhelp(2);