iptables 分析(二)

原文:http://blog.chinaunix.net/uid-24207747-id-2622901.html

do_command()函数分析

//负责整个用户输入的命令处理

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' ", *optarg);
            if (find_target(optarg, TRY_LOAD))
                exit_error(PARAMETER_PROBLEM,
                     "chain name may not clash "
                     "with target name ");
            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 ;-) ", program_version);
            else
                printf("%s v%s ",
                 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] == '') {
                if (invert)
                    exit_error(PARAMETER_PROBLEM,
                         "multiple consecutive ! not"
                         " allowed");
                invert = TRUE;
                optarg[0] = '';
                continue;
            }
            fprintf(stderr, "Bad argument `%s' ", 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(structipt_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 ",
                     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 ",
                     opt2char(OPT_VIANAMEIN),
                     chain);
        }

        if (target && iptc_is_chain(jumpto, *handle)) { //jumpto链名

            fprintf(stderr,
                "Warning: using chain %s, not extension ",
                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 ", 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;
}

原文地址:https://www.cnblogs.com/wangliangblog/p/9799506.html