//负责整个用户输入的命令处理
int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) { //初始化变量
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 xtables_match *m;//扩展匹配操作函数指针
struct iptables_rule_match *matches = NULL; struct iptables_rule_match *matchp; struct xtables_target *target = NULL; struct xtables_target *t;//扩展目标操作函数指针
const char *jumpto = ""; char *protocol = NULL; int proto_used = 0; unsigned long long cnt;
memset(&fw, 0, sizeof(fw)); /*extern char *optarg; //选项的参数指针 extern int optind; //下一次调用getopt时,从optind存储的位置处重新开始检查选择项. extern int opterr; //当为0时,不向stderr输出错误信息. */ optind = 0;
/* 清除标识mflags,以免do_command 被第二次调用,为了安全,清除了所有匹 配的链表*/ for (m = xtables_matches; m; m = m->next) m->mflags = 0;
for (t = xtables_targets; t; t = t->next) { t->tflags = 0; t->used = 0; }
/* Suppress error messages: we may add new options if we demand-load a protocol. */ opterr = 0; //禁止错误信息显示
//分析命令行选项,返回短选项字母,错误返回-1
while ((c = getopt_long(argc, argv, "-A:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:", //例ho:v::a,表示有效选项是-h,-o与-v并且第二个参数-o后面需要一个参数,v后接参数不能空格.
opts, NULL)) != -1) { switch (c) { case 'A'://追加一条规则到规则链,如:iptables -A INPUT -j ACCEPT
add_command(&command, CMD_APPEND, CMD_NONE, invert); //该函数用来添加命令宏
chain = optarg; //得到-A 后参数(链名)
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); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); break;
case 'S': add_command(&command, CMD_LIST_RULES, CMD_ZERO, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(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|CMD_LIST_RULES, 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)) exit_error(PARAMETER_PROBLEM, "chain name may not clash " "with target name\n"); add_command(&command, CMD_NEW_CHAIN, CMD_NONE, invert); 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': //为规则链(INPUT、OUTPUT 和FORWARD)定义一默认策略
add_command(&command, CMD_SET_POLICY, CMD_NONE, invert); chain = optarg; //链名
if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') policy = argv[optind++];//得到策略,如iptables -P INPUT ACCEPT
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);
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, 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': // -j ACCEPT
//将j 选项对应的宏定义位OPT_JUMP 加到options 上,invert 表示非逻辑操作
set_option(&options, OPT_JUMP, &fw.ip.invflags, invert); jumpto = parse_target(optarg); //解析目标名是否正确
target = find_target(jumpto, TRY_LOAD); //正确,从全局的目标链表xptables_targets 中查找这个目标,设置尝试加载目标标识
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); //保存目标名到xptables_targets
set_revision(target->t->u.user.name, target->revision); //设置版本
if (target->init != NULL) target->init(target->t); //初始化xptables_targets
opts = merge_options(opts, target->extra_opts, &target->option_offset);//将target 的参数选项与旧的参数选项连接在一起由opts 返回,这样下一个循环可以分析target 的参数选项,一般在“default:”中进行分析
if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } 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': {//可扩展选项s
size_t size;
if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --match");
m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);//从全局链表xptables_match 中查找匹配模块
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); //初始化
if (m != m->next) { /* Merge options for non-cloned matches */ opts = merge_options(opts, m->extra_opts, &m->option_offset);//将扩展匹配的选项加入到全局选项表opts,下一个循环就可以解析它的选项了
if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } } break;
case 'n': set_option(&options, OPT_NUMERIC, &fw.ip.invflags, invert); break;
case 't': if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --table"); *table = argv[optind-1]; //取t 后面的表名
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_program = optarg; break;
case 'c':
set_option(&options, OPT_COUNTERS, &fw.ip.invflags, invert); pcnt = optarg; bcnt = strchr(pcnt + 1, ','); if (bcnt) bcnt++; if (!bcnt && optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') bcnt = argv[optind++]; if (!bcnt) exit_error(PARAMETER_PROBLEM, "-%c requires packet and byte counter", opt2char(OPT_COUNTERS));
if (sscanf(pcnt, "%llu", &cnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); fw.counters.pcnt = cnt;
if (sscanf(bcnt, "%llu", &cnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); fw.counters.bcnt = cnt; 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; } fprintf(stderr, "Bad argument `%s'\n", optarg); exit_tryhelp(2);
default://分析扩展目标和扩展匹配的选项
if (!target || !(target->parse(c - target->option_offset, //调用扩展目标的选项分析函数
argv, invert, &target->tflags, &fw, &target->t))) { for (matchp = matches; matchp; matchp = matchp->next) { if (matchp->completed) continue; if (matchp->match->parse(c - matchp->match->option_offset, argv, invert, &matchp->match->mflags, &fw, &matchp->match->m)) break; } m = matchp ? matchp->match : NULL;
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);
opts = merge_options(opts, m->extra_opts, &m->option_offset); if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!");
optind--; continue; } if (!m) exit_error(PARAMETER_PROBLEM, "Unknown arg `%s'", argv[optind-1]); } } invert = FALSE; }
//final_check成员函数的作用是作最终的标志检查,如果检测失则,则退出
for (matchp = matches; matchp; matchp = matchp->next) if (matchp->match->final_check != NULL)//使用扩展匹配的函数检查标识
matchp->match->final_check(matchp->match->mflags);
if (target != NULL && target->final_check != NULL) 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 = "0.0.0.0/0"; if (!(options & OPT_SOURCE)) //源
shostnetworkmask = "0.0.0.0/0"; }
/*对来源/目的地址及掩码进行拆分,它们总是以 addr/mask的形式来出现的,根据’/’前面的字符串取得地址值,根据’/’后面的掩码位数,求得正确的掩码值,值得注意的是,同时要处理主机地址和网络地址的情况*/ if (shostnetworkmask) ipparse_hostnetworkmask(shostnetworkmask, &saddrs, &fw.ip.smsk, &nsaddrs);
if (dhostnetworkmask) ipparse_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);
/*handle,是一个指向了具体表,如filter、nat表的句柄,这里判断,如果handle为空,则调用iptc_init,根据table的名称,让handle指针指向相应的表的地址空间,也就是把对应表的所有信息从内核中取出来*/ if (!*handle) *handle = iptc_init(*table); //调用 iptc_init获取表的规则信息,调用list_entries函数显示规则
/* try to insmod the module if iptc_init failed */ if (!*handle && load_xtables_ko(modprobe_program, 0) != -1) *handle = iptc_init(*table);
if (!*handle) exit_error(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)) { //jumpto链名
fprintf(stderr, "Warning: using chain %s, not extension\n", jumpto);
if (target->t) free(target->t);
target = NULL; } 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); }
if (!target) {//扩展目标不存在
#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, 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_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: case CMD_LIST|CMD_ZERO: //list_entries是规则显示的主要处理函数
ret = list_entries(chain, rulenum, options&OPT_VERBOSE, options&OPT_NUMERIC, options&OPT_EXPANDED, options&OPT_LINENUMBERS, handle); if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_LIST_RULES: case CMD_LIST_RULES|CMD_ZERO: ret = list_rules(chain, rulenum, options&OPT_VERBOSE, handle); if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_NEW_CHAIN: ret = iptc_create_chain(chain, handle); 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, options&OPT_COUNTERS ? &fw.counters : 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; }
|