iptables 命令 源代码分析

来源:互联网 发布:阿里云200m 编辑:程序博客网 时间:2024/06/06 00:40
iptables : version 1.2 版本分析

主要是基于ipv4的, ipv6的没有分析。


源代码 路径 git://git.netfilter.org/iptables。


一. extentions

   iptable的所有的extenion源文件在 extention 目录中,
每个extention 都有一个.c文件。
extention 可以是一个match 或 一个target,

每个match/target 必须提供init 函数, register函数和一个结构体。
结构体中包含有多个函数指针和一些数据。

struct iptables_match, struct iptables_target 见附录 【1】,【2】

运行iptables 命令时, 第一个操作就是加载所有的extention。

也就是将所有的match/target结构体加入到一个链表中。
加载步骤是:

1. init_extensions 函数
   此调用所有extension的初始化函数。
   函数的名字很类似都是ipt_extensionname_init();
   例如:
   ipt_ah_init();
   ipt_conntrack_init();
   ipt_dscp_init();

2. 各个模块的_init函数被调用。

   对于match的extension:
   _init函数主要动作就是调用register_match 将当前模块的
   iptables_match 注册到系统中全局变量iptables_matches
   所指定的链表中。

   对于target的extension:
   _init函数主要动作就是调用register_target 将当前模块的
   iptables_target 注册到系统中全局变量iptables_targets
   所指定的链表中。
   
在后续版本中register_match, register_target 改为了
 xtables_register_match, _target. 同时有增加了xtables 模块。
其实只是代码结构上的变化, 功能和实现方式上并没用本质的变化。
这里就不分析了。


二 iptables主流程介绍:

1. 加载extensions
2. 初始化match list 和taget list, 表示没有match和taget 被选中。
3. 循环调用getopt_long 函数 解析出所有option 并加以处理。
   option的处理分三类:
   1). 大写字母: 视为命令 调用add_command函数将其加入command 变量中。
      (后面会对command变量解析(command变量是一个命令集), 处理所有命令)
   2). 小写字母: 视为OPtion, 调用set_option函数将其加入options变量中,
                 并更新 &fw.ip.invflags 变量, 表明是否是一个反转option
                (反转option即取反, /bin/iptables -A INPUT ! -i br0 -d 255.255.255.254 -j DROP
                 !表示取反操作, 表示INPUT链 如果不是br0发出的包且, 目的地址是       
                 255.255.255.254
 则丢掉)
   3). 特殊字母: 对于各个特殊字母有特殊处理。
      a. 'h' 表示帮助, 显示帮助后直接退出
      b.  'p' 表示protocol, 找到protocol , 赋值到 fw.ip.proto ,fw.nfcache |= NFC_IP_PROTO;
        (fw 是一个ipt_entry 变量, 表示一条iptable rule)
      c. 'm' 表示启用extension 模块中的类型为match的模块, 找到指定的match 记录下来, 留待将来使用。
      d. '1' no option , 显示错误信息 并退出
      e. 'M' 表示需要加载内核模块, 记录下模块名, 后面会主动加载
         (iptables_insmod("ip_tables", modprobe);)
          这一点对在内核中加入新的netfilter 模块应该非常有用。
   4). default:
        会遍历所有match, 找到指定的match(以前标记过的, 可以是一个也可以是多个)。
        调用match的parse 函数来分析相关信息  
        如果这条命令没有使用match 并且这条命令使用了protocol, 则加载相应的protocol 模块。

4. 循环调用getopt_long 函数 结束。
5. 对所有match 遍历做final_check. (大多数的match 对这个函数只是“空实现”。所谓的"空实现"是自定义的名词, 即函数中do nothing, 直接返回)
6. 出错处理
7. 对解析出来的部分数据进行分析和校验
   a. 如果 参数(options)中含有 -s, 即  [!] --source    -s address[/mask][...]
        调用函数parse_hostnetworkmask 解析到的networkmask 输出到 fw.ip.smsk ,saddrs, nsaddrs
   b. 如果 参数(options)中含有 -d, 即  [!] --destination -d address[/mask][...]
        调用函数parse_hostnetworkmask 解析到的networkmask 输出到 fw.ip.dmsk, daddrs, ndaddrs
   c. 对解析出来的地址进行错误校验。
   d. 对所有的command option 组合进行校验。 command 和option 的解释见附录【3】
8. 从内核中得到当前所有的iptable表项
    (如果还没有得到, 一般情况下是没有得到,需要在这初始化, 如果在main函数中得到的,可以通过
     do_command的参数handle传到这)
    执行iptc_init(即函数TC_INIT)
    struct ipt_get_entries
    {
        /* Which table: user fills this in. */
        char name[IPT_TABLE_MAXNAMELEN];

        /* User fills this in: total entry size. */
        unsigned int size;

        /* The entries. */ 所有的entries都存到这。
        struct ipt_entry entrytable[0];
    };
9. 处理chain 和target信息
   找到相应的target 和chain,如果没有target 则使用standard target。
   在这个过程中有一个小的问题,
   ibt_is_chain->find_label->populate_cache(handle))->addchain()
   为什么之前不直接populate_cache呢, 反正ibt_is_chain总是要被执行的,可能需要仔细分析代码。

10. 生成iptentry
    根据fw 信息, match 信息, target 信息 生成一个entry
    e = generate_entry(&fw, iptables_matches, target->t);

10. 处理command,
     根据command 类型调用相应的iptc函数做相应的操作。
     参数主要有 option 信息, 生成的entry, handle(所有现有的kernel 中的iptable entries)
     在每个iptc 函数最后, 都会调用TC_COMMIT将数据同步到内核中。
11

附录 【1】
/* Include file for additions: new matches and targets. */
struct iptables_match
{
    struct iptables_match *next;

    ipt_chainlabel name;

    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;

    /* Ignore these men behind the curtain: */
    unsigned int option_offset;
    struct ipt_entry_match *m;
    unsigned int mflags;
    unsigned int used;
#ifdef NO_SHARED_LIBS
    unsigned int loaded; /* simulate loading so options are merged properly */
#endif
};
附录【2】
struct iptables_target
{
    struct iptables_target *next;

    ipt_chainlabel name;

    const char *version;

    /* Size of target data. */
    size_t size;

    /* Size of target data relevent for userspace comparison purposes */
    size_t userspacesize;

    /* Function which prints out usage message. */
    void (*help)(void);

    /* Initialize the target. */
    void (*init)(struct ipt_entry_target *t, 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,
             struct ipt_entry_target **target);

    /* Final check; exit if not ok. */
    void (*final_check)(unsigned int flags);

    /* Prints out the target iff non-NULL: put space at end */
    void (*print)(const struct ipt_ip *ip,
              const struct ipt_entry_target *target, int numeric);

    /* Saves the targinfo in parsable form to stdout. */
    void (*save)(const struct ipt_ip *ip,
             const struct ipt_entry_target *target);

    /* Pointer to list of extra command-line options */
    struct option *extra_opts;

    /* Ignore these men behind the curtain: */
    unsigned int option_offset;
    struct ipt_entry_target *t;
    unsigned int tflags;
    unsigned int used;
#ifdef NO_SHARED_LIBS
    unsigned int loaded; /* simulate loading so options are merged properly */
#endif
};



附录【3】

在iptables 命令中, 参数分为两部分Command Option
command : 是一个action , 包含append, check, delete insert 等动作(后面加chain )
option : 是依附于 command的 选项, 表示这个操作具体要做些什么, 或完成这个action后要做些什么。

哪个action 可以有那些option, 近下面的数组定义。

/* Table of legal combinations of commands and options.  If any of the
 * given commands make an option legal, that option is legal (applies to
 * CMD_LIST and CMD_ZERO only).
 * Key:
 *  +  compulsory  必须的, 相当于MUST or MANDATE
 *  x  illegal     非法的, 相当于 MUST NOT or ERROR
 *     optional    可选的。
 */
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
    /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' '},
/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
};


iptables --help
iptables v1.4.21

Usage: iptables -[ACD] chain rule-specification [options]
       iptables -I chain [rulenum] rule-specification [options]
       iptables -R chain rulenum rule-specification [options]
       iptables -D chain rulenum [options]
       iptables -[LS] [chain [rulenum]] [options]
       iptables -[FZ] [chain] [options]
       iptables -[NX] chain
       iptables -E old-chain-name new-chain-name
       iptables -P chain target [options]
       iptables -h (print this help information)

Commands:
Either long or short options are allowed.
  --append  -A chain        Append to chain
  --check   -C chain        Check for the existence of a rule
  --delete  -D chain        Delete matching rule from chain
  --delete  -D chain rulenum
                Delete rule rulenum (1 = first) from chain
  --insert  -I chain [rulenum]
                Insert in chain as rulenum (default 1=first)
  --replace -R chain rulenum
                Replace rule rulenum (1 = first) in chain
  --list    -L [chain [rulenum]]
                List the rules in a chain or all chains
  --list-rules -S [chain [rulenum]]
                Print the rules in a chain or all chains
  --flush   -F [chain]        Delete all rules in  chain or all chains
  --zero    -Z [chain [rulenum]]
                Zero counters in chain or all chains
  --new     -N chain        Create a new user-defined chain
  --delete-chain
            -X [chain]        Delete a user-defined chain
  --policy  -P chain target
                Change policy on chain to target
  --rename-chain
            -E old-chain new-chain
                Change chain name, (moving any references)
Options:
    --ipv4    -4        Nothing (line is ignored by ip6tables-restore)
    --ipv6    -6        Error (line is ignored by iptables-restore)
[!] --protocol    -p proto    protocol: by number or name, eg. `tcp'
[!] --source    -s address[/mask][...]
                source specification
[!] --destination -d address[/mask][...]
                destination specification
[!] --in-interface -i input name[+]
                network interface name ([+] for wildcard)
 --jump    -j target
                target for rule (may load target extension)
  --goto      -g chain
                              jump to chain with no return
  --match    -m match
                extended match (may load extension)
  --numeric    -n        numeric output of addresses and ports
[!] --out-interface -o output name[+]
                network interface name ([+] for wildcard)
  --table    -t table    table to manipulate (default: `filter')
  --verbose    -v        verbose mode
  --wait    -w        wait for the xtables lock
  --line-numbers        print line numbers when listing
  --exact    -x        expand numbers (display exact values)
[!] --fragment    -f        match second or further fragments only
  --modprobe=<command>        try to insert modules using this command
  --set-counters PKTS BYTES    set the counter during insert/append
[!] --version    -V        print package version.

0 0
原创粉丝点击