您的位置:首页 > 其它

iptables 分析

2012-08-14 22:59 218 查看
http://blog.chinaunix.net/uid-24207747-id-2622900.html
iptables 分析(一) (2010-11-10 12:17)
分类:
iptables

****************

v1.4.1
****************

iptables 是用户空间中用于管理包过滤及NAT 等的工具应用程序。它设置防火墙的过滤规则,并将规则添加到内核空间的特定信息包过滤表内的链中,通过netfilter框架的hook
函数完成对数据包的过滤工作。它还可以设置nat 表的规则实现IP 地址及端口转换,设置mangle 表的规则改变IP 头部信息实现对IP 包更高级的控制.

基本语法:
iptables -t table -Operation chain matching-criteria
-j target

iptables 命令中各参数说明如下: iptables 命令使用选项-t table 选择规则表。
规则表包含处理特定类型信息包的规则和链的信息。规则表选项有:filter、nat 和 mangle.默认是filter。

1)filter(过滤器) 表用于一般的信息包过滤,它包含INPUT、OUTPUT 和 FORWARD 规则链.

2)nat(地址转换) 表用于要转发的信息包,它包含PREROUTING、OUTPUT 和POSTROUTING 规则链。

3)mangle(包处理) 规则表包含PREROUTING 和 OUTPUT 链。每条规则链上有许多规则,通过规则表和hook 点规则链可以找到规则。

-Operation:对规则链的某行规则添加,删除,修改参数(-A:D:C)
chain
:规则链名,有INPUT,OUTPUT,FORWARD.
matching-criteria:匹配,可选项match 部分指定信息包与规则匹配特征,如源和目的地址、协议.
target:动作目标,(-j target)如规则匹配后,执行的相关动作由目标指定,目标基本动作有: ACCEPT 接收该数据包。

DROP 丢弃该数据包。

REJECT 不仅丢弃数据包,还返回给发送者一个可配置的错误信息。

RETURN 让与该规则匹配的信息包停止遍历包含该规则的链,返回到前面调用的链。
如果链是如INPUT 之类的主链,则使用该链的缺省策略处理信息包

要分析源码先学会iptabes工具如何使用,看一下它命令help手册,对要分析源码有帮助.执行iptable -h 有:

iptables v1.4.1.1

Usage: iptables -[AD] chain rule-specification
[options]

       iptables -[RI] chain rulenum rule-specification
[options] //用iptables
- RI 通过规则的顺序指定

       iptables -D chain rulenum 
[options] 
  //删除指定规则

       iptables -[LS]
[chain [rulenum]]
[options] //用iptables
-LFZ 链名 [选项]

       iptables -[FZ]
[chain] 
[options]

       iptables -[NX] chain                       //用
-NX 指定链

       iptables -E old-chain-name new-chain-name  //-E
 用新的链名取代旧的链名

       iptables -P chain target 
[options]         //指定链的默认目标

       iptables -h 
(print this help information)  //帮助

//这些选项指定执行明确的动作:若指令行下没有其他规定,该行只能指定一个选项.对于长格式的命令和选项名,所用字母长度只要保证iptables能从其他选项中区分出该指令就行了

Commands:

Either long or short options are allowed.

  --append 
-A chain        Append to chain //在所选择的链末添加一条或更多规则

  --delete 
-D chain        Delete matching rule from chain //从所选链中删除一条或更多规则

  --delete 
-D chain rulenum   //从所选链中删除一条或更多规则。这条命令可以有两种方法:可以把被删除规则指定为链中的序号(第一条序号为1),或者指定为要匹配的规则

                Delete rule rulenum (1 
= first) from chain

  --insert 
-I chain [rulenum]    //根据给出的规则序号向所选链中插入一条或更多规则。所以,如果规则序号为1,规则会被插入链的头部。这也是不指定规则序号时的默认方式。

                Insert in chain as rulenum 
(default 1=first)

  --replace
-R chain rulenum                                                             //从选中的链中取代一条规则,如果源(地址)或者/与 目的(地址)被转换为多地址,该命令会失败。规则序号从1开始

                Replace rule rulenum (1 
= first) in chain

  --list 
-L [chain [rulenum]]  //显示所选链的所有规则。如果没有选择链,所有链将被显示。也可以和z选项一起使用,这时链会被自动列出和归零。精确输出受其它所给参数影响

                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]        Zero counters
in chain or all chains //把所有链的包及字节的计数器清空。它可以和
-L配合使用,在清空前察看计数器,请参见前文。

  --new 
-N chain        Create a new user-defined chain   //根据给出的名称建立一个新的用户定义链。这必须保证没有同名的链存在

  --delete-chain                                                                                 //删除指定的用户自定义链。这个链必须没有被引用,如果被引用,在删除之前你必须删除或者替换与之有关的规则。如果没有给出参数,这条命令将试着删除每个非内建的链。

            -X [chain]  Delete a user-defined  chain  //清除mangle表中,所有规则链中的规则

  --policy 
-P chain target        //设置链的目标规则

                Change policy on chain to target

  --rename-chain

            -E old-chain new-chain   //根据用户给出的名字对指定链进行重命名,这仅仅是修饰,对整个表的结构没有影响。TARGETS参数给出一个合法的目标。只有非用户自定义链可以使用规则,而且内建链和用户自定义链都不能是规则的目标

                Change chain name,
(moving any references)

                

//这些可被iptables识别的选项可以区分不同的种类                

Options:

  --proto    -p
[!]
proto    protocol: by number
or name, eg.
'tcp'//规则或者包检查(待检查包)的协议。指定协议可以是tcp、udp、icmp中的一个或者全部,也可以是数值,代表这些协议中的某一个。当然也可以使用在/etc/protocols中定义的协议名。在协议名前加上"!"表示相反的规则。数字0相当于所有all。Protocol
 all会匹配所有协议,而且这是缺省时的选项。在和check命令结合时,all可以不被使用。

  --source    -s
[!] address[/mask]  
 //指定源地址,可以是主机名、网络名和清楚的IP地址。mask说明可以是网络掩码或清楚的数字,在网络掩码的左边指定网络掩码左边"1"的个数,因此,mask值为24等于255.255.255.0。在指定地址前加上"!"说明指定了相反的地址段。标志
--src 是这个选项的简写。

                source specification    

  --destination
-d [!] address[/mask] //指定目标地址,要获取详细说明请参见
-s标志的说明

                destination specification

  --in-interface
-i [!] input
name[+]

                network interface name 
([+]
for wildcard)

  --jump    -j target    //目标跳转,指定规则的目标;也就是说,如果包匹配应当做什么。目标可以是用户自定义链(不是这条规则所在的),某个会立即决定包的命运的专用内建目标,或者一个扩展(参见下面的EXTENSIONS)。如果规则的这个选项被忽略,那么匹配的过程不会对包产生影响,不过规则的计数器会增加。

                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[+] //输出接口[名称],这是包经由该接口送出的可选的出口名称,包通过该口输出(在链FORWARD、OUTPUT和POSTROUTING中送出的包)。当在接口名前使用"!"说明后,指的是相反的名称。如果接口名后面加上"+",则所有以此接口名开头的接口都会被匹配。如果这个选项被忽略,会假设为"+",那么将匹配所有任意接口。

                network interface name 
([+]
for wildcard)

  --table    -t table    table to manipulate
(default: 
'filter') //指定表名

  --verbose    -v        verbose mode

  --line-numbers        print line numbers when listing

  --exact    -x        expand numbers
(display exact values)

[!]
--fragment    -f        match second
or further fragments only //这意味着在分片的包中,规则只询问第二及以后的片。自那以后由于无法判断这种把包的源端口或目标端口(或者是ICMP类型的),这类包将不能匹配任何指定对他们进行匹配的规则。如果"!"说明用在了"-f"标志之前,表示相反的意思。

  --modprobe=<command>        try to insert modules using
this command

  --set-counters PKTS BYTES    set the counter during insert/append

[!]
--version    -V        print package version.

进入main函数之前,先看分析几个重要的数据结构,这对看理解源码很有帮助:

iptables 工具还把规则表存储在结构iptc_handle 的变量中,然后,再与内核交互得到或设置规

则表信息。结构iptc_handle 指向一个具体的规则表,表信息从内核中的filter、nat 等表提取.

结构如下:

typedef struct iptc_handle *iptc_handle_t;    

#define STRUCT_TC_HANDLE    struct iptc_handle

    

    STRUCT_TC_HANDLE

{

    int changed;                                                                 //是否有变化

    struct list_head chains;                    //规则链

    

    struct chain_head
*chain_iterator_cur;      //当前规则链

    struct rule_head
*rule_iterator_cur;
//当前规则

    unsigned int num_chains;                    //用户定义的链数

    struct chain_head 
**chain_index;    /* array
for fast chain list access*/

    unsigned int chain_index_sz;
/*
size of chain index array
*/

    STRUCT_GETINFO info;                //对应内核结构ipt_getinfo,包含hook 相关信息

    STRUCT_GET_ENTRIES *entries;        //规则链的规则条目,表对应内核中结构

};

    

//存储规则

struct rule_head

{

    struct list_head list;

    struct chain_head 
*chain;

    struct counter_map counter_map;

    unsigned int index;        /* index
(needed for counter_map)
*/

    unsigned int offset;        /* offset
in rule blob */

    enum iptcc_rule_type type;

    struct chain_head 
*jump;    /* jump target,
if IPTCC_R_JUMP */

    unsigned int size;        /*
size of entry data
*/

    STRUCT_ENTRY entry[0];

};

//存储规则链

struct chain_head

{

    struct list_head list;

    char name[TABLE_MAXNAMELEN];

    unsigned int hooknum;       /* hook number+1
if builtin */

    unsigned int references;    /* how many jumps reference us
*/

    int verdict;                /* verdict
if builtin */

    STRUCT_COUNTERS counters;    /* per-chain counters
*/

    struct counter_map counter_map;

    unsigned int num_rules;        /* number
of rules in list
*/

    struct list_head rules;        /* list
of rules */

    unsigned int index;        /* index
(needed for jump resolval)
*/

    unsigned int head_offset;    /* offset
in rule blob */

    unsigned int foot_index;    /* index
(needed for counter_map)
*/

    unsigned int foot_offset;    /* offset
in rule blob */

};

----------------------------------------------------------------------------------------    

    struct iptables_match 
*iptables_matches = NULL;

    struct iptables_target 
*iptables_targets = NULL;

    

    1.iptables_match  存储了扩展匹配的操作函数指针,如:扩展匹配模块的初始化、选项分析、检查及帮助等函数,它还包括了结构ipt_entry_match 成员指针,而ipt_entry_match 存储了扩展匹配的条目名及内核使用的匹配操作指针等.ipt_entry_match 将用户空间与内核空间的匹配结构连接起来

    2.iptables_target 存储了扩展目标与选项-j 相关的操作函数指针,如:初始化、选项分析、检查及帮助等函数。它在还包括了结构ipt_entry_target 成员,而ipt_entry_target 存储了扩展目标的条目名及内核空间使用的目标操作指针等信息.同上

struct iptables_match

{

    struct iptables_match 
*next;

    ipt_chainlabel name;           //链名

    u_int8_t revision;             //匹配模块的版本序号,缺省值为0

    const char *version;           //版本字符串

    size_t size;                   //匹配的数据大小                                                      

    size_t userspacesize;          //匹配的数据大小,用于在用户空间进行比较

    void (*help)(void);            //打印使用信息

    void (*init)(struct ipt_entry_match
*m, unsigned 
int *nfcache); //初始化匹配

    int (*parse)(int c, char
**argv,
int invert, unsigned
int *flags,//分析命令行选项,如果分析完一个选项,返回true

    const struct ipt_entry 
*entry,

    unsigned int *nfcache,

    struct ipt_entry_match 
**match);

    void (*final_check)(unsigned
int flags);  //对匹配最终的检查,检查出不正确就退出

    void (*print)(const
struct ipt_ip *ip, const
struct ipt_entry_match 
*match, intumeric); //打印出匹配信息,放置NULL 在字符串末尾。

    void (*save)(const
struct ipt_ip *ip, const
struct ipt_entry_match 
*match);                                      
      //存储分析出的匹配信息到标准输出stdout

    const struct option
*extra_opts;          //指向附加命令行选项链表

    unsigned int option_offset;

    struct ipt_entry_match 
*m;                //内核中匹配的操作函数及信息

    unsigned int mflags;

};

    

struct ipt_entry_match

{

union {

    struct {

                        u_int16_t match_size;                   //匹配的大小

                        char name[IPT_FUNCTION_MAXNAMELEN-1];   //用户空间使用的匹配名

                        u_int8_t revision;                      //版本序号

                 } user;                                        //用户空间匹配条目信息

    struct {

                        u_int16_t match_size;                   //匹配大小

                        struct ipt_match 
*match;                //内核空间使用的匹配结构

                 } kernel;                                      //内核空间使用的匹配结构信息

            u_int16_t match_size; //匹配的总大小

            } u;

    unsigned char data[0];

};    

    

结构 xt_match 定义了扩展匹配的内核模块的操作函数集,其列出如下:

        #define ipt_match xt_match

struct xt_match

{

            struct list_head list;

            const char name[XT_FUNCTION_MAXNAMELEN-1];    
//匹配名

            u_int8_t revision;

//匹配的实现函数               

            int (*match)(const
struct sk_buff *skb,

                                     const struct net_device
*in,

                                     const struct net_device
*out,

                                     const void *matchinfo,

                                     int offset,

                                     unsigned int protoff,

                                     int 
*hotdrop);

                                     

//当用户插入一个条目到这个类型时调用这个函数,返回true 或false

            int (*checkentry)(const char
*tablename,

                                                const void 
*ip,

                                                void *matchinfo,

                                                unsigned 
int matchinfosize,

                                                unsigned int hook_mask);                                        

void (*destroy)(void
*matchinfo, unsigned
int matchinfosize);    //当这个类型的条目被删除时调用

struct modules *me;                                              //如果是内核模块,设置到指针THIS_MODULE,否则为NULL

};

struct option {

    const char *name;         
     //长选项的名字

    int has_arg;                     //长选项参数值个数

    int *flag;                       //NULL 时,getopt_long()返回val,否则返回0

    int val;                         //长选项对应的短选项字符

};

----------------------------------------------------------------------------------------

哎,有些结构体必需结构netfiter来看,还找些资料,结构体注解就到这吧,正式进入源码吧,哈哈.

int

main(int argc, char
*argv[])

{

    int ret; 

    char *table =
"filter";      //默认表filter

    iptc_handle_t handle
= NULL; //用来储存表的所有规则

    

    program_name = 
"iptables"; 

    program_version = XTABLES_VERSION;

    lib_dir = getenv("XTABLES_LIBDIR"); //得到环境变量

    if (lib_dir
== NULL) {

        lib_dir = getenv("IPTABLES_LIB_DIR");

        if (lib_dir
!= NULL)

            fprintf(stderr,
"IPTABLES_LIB_DIR is deprecated\n");

    }

    if (lib_dir
== NULL)

        lib_dir = XTABLES_LIBDIR;

#ifdef NO_SHARED_LIBS

    init_extensions();       //设置版本号

#endif

    ret = 
do_command(argc, argv, &table, &handle);
 //分析命令选项,将规则存入handle

    if (ret)

        ret = iptc_commit(&handle);   //通过系统调用setsockopt
 将规则设置到Linux 的netfilter 模块的规则表中

    if (!ret) {

        fprintf(stderr,
"iptables: %s\n",

            iptc_strerror(errno));

        if (errno
== EAGAIN) {

            exit(RESOURCE_PROBLEM);

        }

    }

    exit(!ret);

}

主函数二步工作:1.do_command接受用户输入的命令规则,根据输入的表名,将规则存到结构iptc_handle 2.iptc_commit通过系统调用setsockopt将规则设置到Linux
的netfilter 模块的规则表中.
进入do_command,看下一篇。






iptables 分析(二) (2010-11-10 14:04)
分类:
iptables


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

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;

}

接下来还是根据一条规则来分析源码,这样比较直观.

命令: iptables -t filter -A INPUT -j ACCEPT

意思:是在filter 表的INPUT 链里追加一条规则(作为最后一条规则)

匹配所有访问本机IP 的数据包,匹配到的接收.

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:",

opts,
NULL))
!= 
-1) {

        switch (c)
{

..............

case 't': if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --table"); *table = argv[optind-1];
break;

        case 'A':

add_command(&command, CMD_APPEND,
 CMD_NONE,

                 invert);

chain
= optarg; 

            break;

case 'j':
set_option(&options, OPT_JUMP, &fw.ip.invflags, invert); jumpto = parse_target(optarg); target = find_target(jumpto, TRY_LOAD); 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); opts = merge_options(opts, target->extra_opts,
 &target->option_offset); if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } break;

......................

接受用户输入规则,getopt_long函数用来分析命令行选项,-t,-A,-j调用对应开关语句,getopt_long原型:

int
getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
=====================================
optstring:短选项字符串,如ho:v::a,表示有效选项是-h,-o与-v并且第二个参数-o后面需要一个参数,v后接参数不能空格.

longopts: struct option结构
struct option {

const char *name; //长选项的名字

int has_arg; //长选项参数值个数

int val; //长选项对应的短选项字符

};
longindex: 如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。
=======================================
-t 后面必有一参数,表名其为filter,把它保存到*table.
-A 后面必有一参数,链名保存在chain,add_command函数用来添加CMD_APPEND命令宏保存到command,以备后使这个命令宏来执行这个追加动作.代码:
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;

}

-j 后面必有一参数,set_option函数首先将j对应的宏OPT_JUMP添加到options,OPT_JUMP在代码中就代表用户的j命令了:

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];

    }

}

获取-j后面所跟参数,也就是其目标名,解析目标名判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况,返回正确的字符串保存到jumpto:
static
const char 
*

parse_target(const
char *targetname) //例如:ACCEPT

{

    const char 
*ptr;

    if (strlen(targetname)
< 1)

        exit_error(PARAMETER_PROBLEM,

             "Invalid target name (too short)");

    if (strlen(targetname)+1
> sizeof(ipt_chainlabel))

        exit_error(PARAMETER_PROBLEM,

             "Invalid target name `%s' (%u chars max)",

             targetname, 
(unsigned int)sizeof(ipt_chainlabel)-1);

    for (ptr 
= targetname; *ptr; ptr++)

        if (isspace(*ptr))
//检查参数*ptr是否为空格字符,也就是判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况

            exit_error(PARAMETER_PROBLEM,

                 "Invalid target name `%s'", targetname);

    return targetname;

}

字符串目标名正确,从全局的目标链表xptables_targets 中查找这个目标名,设置尝试加载目标标识:

struct xtables_target
*find_target(const
char *name,
enum xt_tryload tryload)

{

    struct xtables_target 
*ptr;

    /* Standard target? */

    if (strcmp(name,
"") 
== 0

     || 
strcmp(name, XTC_LABEL_ACCEPT)
== 0 
       //#define XTC_LABEL_ACCEPT "ACCEPT"

     || 
strcmp(name, XTC_LABEL_DROP)
== 0             //#define XTC_LABEL_DROP "DROP"

     || 
strcmp(name, XTC_LABEL_QUEUE)
== 0            //#define XTC_LABEL_QUEUE "QUEUE"

     || 
strcmp(name, XTC_LABEL_RETURN)
== 0)        //#define XTC_LABEL_RETURN "RETURN"

     name = "standard";
//**

    for (ptr
= xtables_targets; ptr; ptr
= ptr->next)
{ //从全局xtables_target 结构中查找目标名

        if (strcmp(name, ptr->name)
== 0)//直到找到

            break;

    }

#ifndef NO_SHARED_LIBS

    if (!ptr
&& tryload 
!= DONT_LOAD 
&& tryload 
!= DURING_LOAD)
{

        ptr = load_extension(lib_dir, afinfo.libprefix, name,
true);
//加载

        if (ptr
== 
NULL && tryload
== LOAD_MUST_SUCCEED)

            exit_error(PARAMETER_PROBLEM,

                 "Couldn't load target `%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 target `%s'\n", name);

    }

#endif

    if (ptr)

        ptr->used
= 1;

    return ptr;

}

iptables 分析(三) (2010-11-10 17:36)
分类:
iptables

接上篇文章,find_target查到目标并加载成功,返回一个xtables_target型对象保存在target,分配空间,拷贝目标,初始化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); 
//保存目标名到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!");

            }

接下来看个比较重要的函数iptc_init来从内核获取表的规则信息,tables就是是-t保存的表名

/*为了用户能够对内核态的规则进行操作,需要将内核态的规则信息读取到用户空间,对用户空间的规则进行修改后,再根据用户态的规则信息设置内核态的规则信息*/

    if (!*handle)

        *handle = iptc_init(*table);
//调用 iptc_init获取表的规则信息,调用list_entries函数显示规则

    /* 加载此模块再获取 */

    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));

进入iptc_init:

iptc_handle_t iptc_init(const
char *tablename);

#define TC_INIT        iptc_init
//宏定义

TC_HANDLE_T  TC_INIT(const
char *tablename)
//获取表信息

{

    TC_HANDLE_T h;

    STRUCT_GETINFO info;

    unsigned int tmp;

    socklen_t s;

    iptc_fn = TC_INIT;

    if (strlen(tablename)
>= TABLE_MAXNAMELEN)
{

        errno = EINVAL;

        return NULL;

    }

 /*为获取信息打开一个套接字接口*/

    if (sockfd_use
== 0)
{

        sockfd = socket(TC_AF,
SOCK_RAW, 
IPPROTO_RAW);

        if (sockfd
< 0)

            return NULL;

    }

    sockfd_use++;

retry:

    s = sizeof(info);

    strcpy(info.name, tablename);

/*获取表基本信息,保存在info*/    

    if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO,
&info, 
&s) < 0)
{

        if (--sockfd_use
== 0)
{

            close(sockfd);

            sockfd = -1;

        }

        return NULL;

    }

    DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",

        info.valid_hooks, info.num_entries, info.size);

    if ((h
= alloc_handle(info.name, info.size, info.num_entries))

     == 
NULL) {

        if (--sockfd_use
== 0)
{

            close(sockfd);

            sockfd = -1;

        }

        return NULL;

    }

    /* Initialize current state */

    h->info 
= info;

    h->entries->size
= h->info.size;

    tmp = sizeof(STRUCT_GET_ENTRIES)
+ h->info.size;

    if (getsockopt(sockfd, TC_IPPROTO,
SO_GET_ENTRIES, h->entries,

         &tmp) 
< 0)//最后规则信息存入h->entries

        goto error;

#ifdef IPTC_DEBUG2

    {

        int fd =
open("/tmp/libiptc-so_get_entries.blob",

                O_CREAT|O_WRONLY);

        if (fd 
>= 0) 
{

            write(fd, h->entries, tmp);

            close(fd);

        }

    }

#endif

    if (parse_table(h)
< 0)

        goto error;

    CHECK(h);

    return h;

error:

    TC_FREE(&h);

    /* A different process changed the ruleset size, retry */

    if (errno
== EAGAIN)

        goto retry;

    return NULL;

}

iptables与内核的交互,都是使用setsockopt函数(设置与某个套接字关联的选 项)来实现的,创建socket,通过getsockopt函数是获取与对应的套接字关联的选项,其标志位是SO_GET_INFO,获取保存在STRUCT_GETINFO类型info里,STRUCT_GETINFO结构也就是#define
STRUCT_GETINFO struct ipt_getinfo,最后getsockopt返回规则信息是ipt_getinfo型而TC_INIT函数返回一个具体的规则表信息iptc_handle型.

未完...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: