linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘建议留言网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > article > unix > unix提高 >
栏目导栏
资料搜索
热门文章
·20%的SOLARIS知识解决80%的问题
·Solaris启动过程详解 zt
·查看Solaris系统硬件配置的命令
·Unix系列shell程序编写(中)
·STRUTS 源码学习笔记
·SOLARIS技巧篇
·snort源码分析
·Unix系列shell程序编写(下)
·在solaris 10/x86下安装oracle
·solaris 10 硬盘安装
·Solaris安全配置手册
·Apache源代码分析——关于模块
·HP-UX基本指令快速参考
· Tomcat Server源码启动分析
·Solaris8 双网卡配置(路由器用
最新文章
·solaris系统安全配置
·RHEL5.0操作系统下NFS服务的配
·Solaris Linux 9下Vsftpd的配置
·在HP-UX下建立只归属于某个目录
·SolarWinds2002使用说明
·从外部存储划盘并加入vg,为fs扩
·因带库问题导致系统光纤卡报错
·Unix下多线程中条件变量的使用
·UNIX和Linux中信号的个数
·不同的类UNIX操作系统密码破解
·AIX系统下Domino邮件服务器安装
·限制ROOT用户远程登陆UNIX系统
·Unix操作系统入侵追踪反击战
·AIX CDE不能启动的故障一般性解
·CentOS4.4用VSFTPD架设FTP服务
Google
 
iptables 源码分析
[ 作者:  加入时间:2006-06-20 12:54:03  来自: ]
 iptables 源码分析
作者:独孤九贱  出处:www.chinaunix.net  更新时间: 2005年12月22日 
一、规则的显示6ckLinux联盟
选择先来说明规则的显示,因为他涉及到的东东简单,而且又全面,了解了规则的显示,对于其它操作的了解就显得容易了。6ckLinux联盟
6ckLinux联盟
iptables version 1.2.76ckLinux联盟
6ckLinux联盟
iptables有两条线:ipv4 和ipv6,这里只分析v4的,因为v6偶暂时还用不着,没有去看。6ckLinux联盟
6ckLinux联盟
iptables_standardone.c6ckLinux联盟
主函数:6ckLinux联盟
int main(int argc, char *argv[])6ckLinux联盟
{6ckLinux联盟
        int ret;6ckLinux联盟
        char *table = "filter";                                /*默认的表是filter*/6ckLinux联盟
        iptc_handle_t handle = NULL;6ckLinux联盟
6ckLinux联盟
        program_name = "iptables";6ckLinux联盟
        program_version = IPTABLES_VERSION;6ckLinux联盟
6ckLinux联盟
#ifdef NO_SHARED_LIBS6ckLinux联盟
        init_extensions();6ckLinux联盟
#endif6ckLinux联盟
6ckLinux联盟
        /*进入命令行处理函数*/6ckLinux联盟
ret = do_command(argc, argv, &table, &handle);6ckLinux联盟
        if (ret)6ckLinux联盟
                ret = iptc_commit(&handle);6ckLinux联盟
6ckLinux联盟
        if (!ret)6ckLinux联盟
                fprintf(stderr, "iptables: %s\n",6ckLinux联盟
                        iptc_strerror(errno));6ckLinux联盟
6ckLinux联盟
        exit(!ret);6ckLinux联盟
}6ckLinux联盟
table表示表的名称,就是iptables -t 后面跟的那个,默认是"filter"6ckLinux联盟
iptc_handle_t handle = NULL;  这个东东很重要,现在初始化NULL,后面他被用来存储一个表的所有规则的快照。6ckLinux联盟
        program_name = "iptables";6ckLinux联盟
        program_version = IPTABLES_VERSION;6ckLinux联盟
设置名称和版本。6ckLinux联盟
#ifdef NO_SHARED_LIBS6ckLinux联盟
        init_extensions();6ckLinux联盟
#endif6ckLinux联盟
iptables很多东东,是用共享库*.so的形式(我们安装会,可以在诸如/lib/iptables下边看到),如果不采用共享库,则进行一个初始化操作。我们假设是采用共享库的,忽略它。6ckLinux联盟
6ckLinux联盟
然后就进入核心处理模块:6ckLinux联盟
do_command(argc, argv, &table, &handle);6ckLinux联盟
6ckLinux联盟
do_command 函数是整个系统的核心,负责处理整个用户的输入命令。函数首先对一些结构、变量进行初始化,初始化完毕后,进入while循环,分析用户输入的命令,设置相关的标志变量,然后根据相应标志,调用对应的处理函数。6ckLinux联盟
6ckLinux联盟
struct ipt_entry fw, *e = NULL;6ckLinux联盟
        int invert = 0;6ckLinux联盟
        unsigned int nsaddrs = 0, ndaddrs = 0;6ckLinux联盟
        struct in_addr *saddrs = NULL, *daddrs = NULL;6ckLinux联盟
6ckLinux联盟
        int c, verbose = 0;6ckLinux联盟
        const char *chain = NULL;6ckLinux联盟
        const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;6ckLinux联盟
        const char *policy = NULL, *newname = NULL;6ckLinux联盟
        unsigned int rulenum = 0, options = 0, command = 0;6ckLinux联盟
        const char *pcnt = NULL, *bcnt = NULL;6ckLinux联盟
        int ret = 1;6ckLinux联盟
        struct iptables_match *m;6ckLinux联盟
        struct iptables_target *target = NULL;6ckLinux联盟
        struct iptables_target *t;6ckLinux联盟
        const char *jumpto = "";6ckLinux联盟
        char *protocol = NULL;6ckLinux联盟
        const char *modprobe = NULL;6ckLinux联盟
6ckLinux联盟
        /*初始化变量*/6ckLinux联盟
memset(&fw, 0, sizeof(fw));6ckLinux联盟
6ckLinux联盟
        opts = original_opts;6ckLinux联盟
        global_option_offset = 0;6ckLinux联盟
6ckLinux联盟
        /* re-set optind to 0 in case do_command gets called6ckLinux联盟
         * a second time */6ckLinux联盟
        optind = 0;6ckLinux联盟
6ckLinux联盟
        /*初始化两个全局变量*/6ckLinux联盟
/* clear mflags in case do_command gets called a second time6ckLinux联盟
         * (we clear the global list of all matches for security)*/6ckLinux联盟
        for (m = iptables_matches; m; m = m->next) {6ckLinux联盟
                m->mflags = 0;6ckLinux联盟
                m->used = 0;6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        for (t = iptables_targets; t; t = t->next) {6ckLinux联盟
                t->tflags = 0;6ckLinux联盟
                t->used = 0;6ckLinux联盟
        }6ckLinux联盟
ps:开头一大堆的变量定义和初始化,可以在程序分析的时候看它们的作用,有两个全局结构变量很重要:iptables_matches和iptables_targets。现在来分析他们的作用会有一点困难,因为它们涉及到了太多方面的东东,这里,可以先把它们“想像成”用户空间用来读取内核规则的结构(当然,这有点错误)。6ckLinux联盟
6ckLinux联盟
/*开始化析命令行*/6ckLinux联盟
while ((c = getopt_long(argc, argv,6ckLinux联盟
          "-A:C:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",6ckLinux联盟
                                           opts, NULL)) != -1) 6ckLinux联盟
{6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
这个while循环处理所有的用户输入,对应规则输出-L,有:6ckLinux联盟
case 'L':6ckLinux联盟
add_command(&command, CMD_LIST, CMD_ZERO,6ckLinux联盟
invert);6ckLinux联盟
if (optarg) chain = optarg;6ckLinux联盟
else if (optind < argc && argv[optind][0] != '-'6ckLinux联盟
&& argv[optind][0] != '!')6ckLinux联盟
chain = argv[optind++];6ckLinux联盟
break;6ckLinux联盟
6ckLinux联盟
add_command函数负责将命令标志变量command与令标志 CMD_LIST求&运算, CMD_ZERO只是一个附加的判断标志而已,invert);然后,从命令行中取得要显示的链名(如果有的话)。6ckLinux联盟
6ckLinux联盟
与此相关的还有用t参数指定了表名:6ckLinux联盟
                case 't':6ckLinux联盟
                        if (invert)6ckLinux联盟
                                exit_error(PARAMETER_PROBLEM,6ckLinux联盟
                                           "unexpected ! flag before --table");6ckLinux联盟
                        *table = argv[optind-1];6ckLinux联盟
                        break;6ckLinux联盟
即,如果有’t’参数,则取’t’后跟的表名:*table = argv[optind-1],否则,它应该是主函数中默认的filter表。6ckLinux联盟
6ckLinux联盟
命令处理完毕后,即进入执行模块:6ckLinux联盟
/*因为程序定义了共享库的话,iptables_matches/iptables_target这两个结构运行至此是NULL,并且target也是NULL,对于规则显示而言,这一部份的处理目前没有实际意义,回过头再来看这一段更易理解。final_check成员函数的作用是作最终的标志检查,如果检测失则,则退出*/6ckLinux联盟
        for (m = iptables_matches; m; m = m->next) {6ckLinux联盟
                if (!m->used)6ckLinux联盟
                        continue;6ckLinux联盟
6ckLinux联盟
                m->final_check(m->mflags);6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        if (target)6ckLinux联盟
                target->final_check(target->tflags);6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
接着对参数作一些必要的合法性检查:6ckLinux联盟
        /* Fix me: must put inverse options checking here --MN */6ckLinux联盟
6ckLinux联盟
        if (optind < argc)6ckLinux联盟
                exit_error(PARAMETER_PROBLEM,6ckLinux联盟
                           "unknown arguments found on commandline");6ckLinux联盟
        if (!command)6ckLinux联盟
                exit_error(PARAMETER_PROBLEM, "no command specified");6ckLinux联盟
        if (invert)6ckLinux联盟
                exit_error(PARAMETER_PROBLEM,6ckLinux联盟
                           "nothing appropriate following !");6ckLinux联盟
6ckLinux联盟
/*对于如果要进行(CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)处理来说,如果没有设置来源/目的地址及掩码,则给予它们一个默认值*/6ckLinux联盟
        if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {6ckLinux联盟
                if (!(options & OPT_DESTINATION))6ckLinux联盟
                        dhostnetworkmask = "0.0.0.0/0";6ckLinux联盟
                if (!(options & OPT_SOURCE))6ckLinux联盟
                        shostnetworkmask = "0.0.0.0/0";6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
/*对来源/目的地址及掩码进行拆分,它们总是以 addr/mask的形式来出现的,根据’/’前面的字符串取得地址值,根据’/’后面的掩码位数,求得正确的掩码值,值得注意的是,同时要处理主机地址和网络地址的情况*/6ckLinux联盟
        if (shostnetworkmask)6ckLinux联盟
                parse_hostnetworkmask(shostnetworkmask, &saddrs,6ckLinux联盟
                                      &(fw.ip.smsk), &nsaddrs);6ckLinux联盟
6ckLinux联盟
        if (dhostnetworkmask)6ckLinux联盟
                parse_hostnetworkmask(dhostnetworkmask, &daddrs,6ckLinux联盟
                                      &(fw.ip.dmsk), &ndaddrs);6ckLinux联盟
6ckLinux联盟
/*然后检查来源/目的网络地址的合法性*/6ckLinux联盟
        if ((nsaddrs > 1 || ndaddrs > 1) &&6ckLinux联盟
            (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))6ckLinux联盟
                exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"6ckLinux联盟
                           " source or destination IP addresses");6ckLinux联盟
6ckLinux联盟
/*对命令行格式进行合法性检查*/6ckLinux联盟
generic_opt_check(command, options);6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
如果前面只是热身的话,那么从现在开始,就进入实质性阶段了:6ckLinux联盟
6ckLinux联盟
do_command函数最后一个参数handle,是一个指向了具体表,如filter、nat表的句柄,这里判断,如果handle为空,则调用iptc_init,根据table的名称,让handle指针指向相应的表的地址空间,也就是把对应表的所有信息从内核中取出来:6ckLinux联盟
        /* only allocate handle if we weren't called with a handle */6ckLinux联盟
        if (!*handle)6ckLinux联盟
                *handle = iptc_init(*table);6ckLinux联盟
6ckLinux联盟
        /*如果获取换败,将试着插入模块,再次获取*/6ckLinux联盟
        if (!*handle) {6ckLinux联盟
                /* try to insmod the module if iptc_init failed */6ckLinux联盟
                iptables_insmod("ip_tables", modprobe);6ckLinux联盟
                *handle = iptc_init(*table);6ckLinux联盟
        /*仍然失败,则退出*/6ckLinux联盟
        if (!*handle)6ckLinux联盟
                exit_error(VERSION_PROBLEM,6ckLinux联盟
                           "can't initialize iptables table `%s': %s",6ckLinux联盟
                           *table, iptc_strerror(errno));6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
/*继续进行一些简单的判断*/6ckLinux联盟
if (command == CMD_APPEND6ckLinux联盟
            || command == CMD_DELETE6ckLinux联盟
            || command == CMD_INSERT6ckLinux联盟
            || command == CMD_REPLACE) {6ckLinux联盟
                /*List命令不在判断之列,暂时不分析*/6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
/*判断命令标志,调用相关函数进行处理*/6ckLinux联盟
switch (command) {6ckLinux联盟
        case CMD_LIST:6ckLinux联盟
                ret = list_entries(chain,6ckLinux联盟
                                   options&OPT_VERBOSE,6ckLinux联盟
                                   options&OPT_NUMERIC,6ckLinux联盟
                                   options&OPT_EXPANDED,6ckLinux联盟
                                   options&OPT_LINENUMBERS,6ckLinux联盟
                                   handle);6ckLinux联盟
}6ckLinux联盟
list_entries是规则显示的主要处理函数。6ckLinux联盟
Options是显示的标志变量:6ckLinux联盟
OPT_VERBOSE:对应-v6ckLinux联盟
OPT_NUMERIC:对应-n6ckLinux联盟
OPT_EXPANDED:对应-x6ckLinux联盟
OPT_LINENUMBERS: -l6ckLinux联盟
6ckLinux联盟
看来很简单,说了这么大一圈子,就是调用 iptc_init获取表的规则信息,调用list_entries函数显示规则。6ckLinux联盟
1.1        表的查找6ckLinux联盟
再回到iptc_init 函数上来,它根据表名,从内核获取对应的表的相关信息,handle是一个iptc_handle_t类型的指针,在libiptc.c中,有如下定义:6ckLinux联盟
/* Transparent handle type. */6ckLinux联盟
typedef struct iptc_handle *iptc_handle_t;6ckLinux联盟
6ckLinux联盟
在Libip4tc中:6ckLinux联盟
#define STRUCT_TC_HANDLE        struct iptc_handle6ckLinux联盟
在Libiptc.c中,可以找到STRUCT_TC_HANDLE的定义:6ckLinux联盟
STRUCT_TC_HANDLE6ckLinux联盟
{6ckLinux联盟
        /* Have changes been made? */6ckLinux联盟
        int changed;6ckLinux联盟
        /* Size in here reflects original state. */6ckLinux联盟
        STRUCT_GETINFO info;6ckLinux联盟
6ckLinux联盟
        struct counter_map *counter_map;6ckLinux联盟
        /* Array of hook names */6ckLinux联盟
        const char **hooknames;6ckLinux联盟
6ckLinux联盟
        /* Cached position of chain heads (NULL = no cache). */6ckLinux联盟
        unsigned int cache_num_chains;6ckLinux联盟
        unsigned int cache_num_builtins;6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
        /* Rule iterator: terminal rule */6ckLinux联盟
        STRUCT_ENTRY *cache_rule_end;6ckLinux联盟
6ckLinux联盟
        /* Number in here reflects current state. */6ckLinux联盟
        unsigned int new_number;6ckLinux联盟
        STRUCT_GET_ENTRIES entries;6ckLinux联盟
};6ckLinux联盟
6ckLinux联盟
再来看看iptc_init函数,同样在在Libip4tc中,有如下定义:6ckLinux联盟
#define TC_INIT                        iptc_init6ckLinux联盟
6ckLinux联盟
在Libiptc.c中,可以看到函数的实现,基本上iptables与内核的交互,都是使用setsockopt函数来实现的,对于获取取规是信息来说,标志位是SO_GET_INFO,而从内核返回回来的规则信息是一个STRUCT_GETINFO结构:6ckLinux联盟
6ckLinux联盟
TC_HANDLE_T TC_INIT(const char *tablename)6ckLinux联盟
{6ckLinux联盟
        TC_HANDLE_T h;6ckLinux联盟
        STRUCT_GETINFO info;6ckLinux联盟
        unsigned int i;6ckLinux联盟
        int tmp;6ckLinux联盟
        socklen_t s;6ckLinux联盟
6ckLinux联盟
        iptc_fn = TC_INIT;6ckLinux联盟
6ckLinux联盟
        if (sockfd != -1)6ckLinux联盟
                close(sockfd);6ckLinux联盟
6ckLinux联盟
        /*为获取信息打开一个套接字接口*/6ckLinux联盟
        sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);6ckLinux联盟
        if (sockfd < 0)6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
        s = sizeof(info);6ckLinux联盟
        if (strlen(tablename) >= TABLE_MAXNAMELEN) {6ckLinux联盟
                errno = EINVAL;6ckLinux联盟
                return NULL;6ckLinux联盟
        }6ckLinux联盟
        strcpy(info.name, tablename);6ckLinux联盟
/*获取规则信息*/6ckLinux联盟
        if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
        if ((h = alloc_handle(info.name, info.size, info.num_entries))6ckLinux联盟
            == NULL)6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
/* Too hard --RR */6ckLinux联盟
#if 06ckLinux联盟
        sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);6ckLinux联盟
        dynlib = dlopen(pathname, RTLD_NOW);6ckLinux联盟
        if (!dynlib) {6ckLinux联盟
                errno = ENOENT;6ckLinux联盟
                return NULL;6ckLinux联盟
        }6ckLinux联盟
        h->hooknames = dlsym(dynlib, "hooknames");6ckLinux联盟
        if (!h->hooknames) {6ckLinux联盟
                errno = ENOENT;6ckLinux联盟
                return NULL;6ckLinux联盟
        }6ckLinux联盟
#else6ckLinux联盟
        h->hooknames = hooknames;6ckLinux联盟
#endif6ckLinux联盟
6ckLinux联盟
        /* Initialize current state */6ckLinux联盟
        h->info = info;6ckLinux联盟
        h->new_number = h->info.num_entries;6ckLinux联盟
        for (i = 0; i < h->info.num_entries; i++)6ckLinux联盟
                h->counter_map6ckLinux联盟
                        = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});6ckLinux联盟
6ckLinux联盟
        h->entries.size = h->info.size;6ckLinux联盟
6ckLinux联盟
        tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;6ckLinux联盟
6ckLinux联盟
        if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,6ckLinux联盟
                       &tmp) < 0) {6ckLinux联盟
                free(h);6ckLinux联盟
                return NULL;6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        CHECK(h);6ckLinux联盟
        return h;6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
        函数为h分配空间,然后赋予相应的值。要理解这个函数,还需要了解STRUCT_GETINFO结构和分配内存空间的函数alloc_handle。6ckLinux联盟
6ckLinux联盟
#define STRUCT_GETINFO                struct ipt_getinfo6ckLinux联盟
6ckLinux联盟
/* The argument to IPT_SO_GET_INFO */ 6ckLinux联盟
6ckLinux联盟
struct ipt_getinfo6ckLinux联盟
{6ckLinux联盟
        /* Which table: caller fills this in. */6ckLinux联盟
        char name[IPT_TABLE_MAXNAMELEN];6ckLinux联盟
6ckLinux联盟
        /* Kernel fills these in. */6ckLinux联盟
        /* Which hook entry points are valid: bitmask */6ckLinux联盟
        unsigned int valid_hooks;6ckLinux联盟
6ckLinux联盟
        /* Hook entry points: one per netfilter hook. */6ckLinux联盟
        unsigned int hook_entry[NF_IP_NUMHOOKS];6ckLinux联盟
6ckLinux联盟
        /* Underflow points. */6ckLinux联盟
        unsigned int underflow[NF_IP_NUMHOOKS];6ckLinux联盟
6ckLinux联盟
        /* Number of entries */6ckLinux联盟
        unsigned int num_entries;6ckLinux联盟
6ckLinux联盟
        /* Size of entries. */6ckLinux联盟
        unsigned int size;6ckLinux联盟
};6ckLinux联盟
6ckLinux联盟
/* Allocate handle of given size */6ckLinux联盟
static TC_HANDLE_T6ckLinux联盟
alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)6ckLinux联盟
{6ckLinux联盟
        size_t len;6ckLinux联盟
        TC_HANDLE_T h;6ckLinux联盟
6ckLinux联盟
        len = sizeof(STRUCT_TC_HANDLE)6ckLinux联盟
                + size6ckLinux联盟
                + num_rules * sizeof(struct counter_map);6ckLinux联盟
6ckLinux联盟
        if ((h = malloc(len)) == NULL) {6ckLinux联盟
                errno = ENOMEM;6ckLinux联盟
                return NULL;6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        h->changed = 0;6ckLinux联盟
        h->cache_num_chains = 0;6ckLinux联盟
        h->cache_chain_heads = NULL;6ckLinux联盟
        h->counter_map = (void *)h6ckLinux联盟
                + sizeof(STRUCT_TC_HANDLE)6ckLinux联盟
                + size;6ckLinux联盟
        strcpy(h->info.name, tablename);6ckLinux联盟
        strcpy(h->entries.name, tablename);6ckLinux联盟
6ckLinux联盟
        return h;6ckLinux联盟
6ckLinux联盟
函数list_entries用于显示表下边的链:6ckLinux联盟
/*显示某table下的chain*/6ckLinux联盟
static int6ckLinux联盟
list_entries(const ipt_chainlabel chain, int verbose, int numeric,6ckLinux联盟
             int expanded, int linenumbers, iptc_handle_t *handle)6ckLinux联盟
{6ckLinux联盟
        int found = 0;6ckLinux联盟
        unsigned int format;6ckLinux联盟
        const char *this;6ckLinux联盟
6ckLinux联盟
        format = FMT_OPTIONS;                                /*设置输出格式*/6ckLinux联盟
        if (!verbose)                                        /*详细输出模式,,对应-v ,显示匹配的包的数目,包的大小等*/6ckLinux联盟
                format |= FMT_NOCOUNTS;6ckLinux联盟
        else6ckLinux联盟
                format |= FMT_VIA;                        6ckLinux联盟
6ckLinux联盟
        if (numeric)                                        /*对应-n,以数字的形式输出地址和端口*/6ckLinux联盟
                format |= FMT_NUMERIC;6ckLinux联盟
6ckLinux联盟
        if (!expanded)                                        /*对应-x,expand numbers (display exact values)*/6ckLinux联盟
                format |= FMT_KILOMEGAGIGA;6ckLinux联盟
6ckLinux联盟
        if (linenumbers)                                /*输出行的编号*/6ckLinux联盟
                format |= FMT_LINENUMBERS;6ckLinux联盟
6ckLinux联盟
        for (this = iptc_first_chain(handle);                /*遍历当前table的所有chain*/6ckLinux联盟
             this;6ckLinux联盟
             this = iptc_next_chain(handle))6ckLinux联盟
{                6ckLinux联盟
                const struct ipt_entry *i;6ckLinux联盟
                unsigned int num;6ckLinux联盟
6ckLinux联盟
                if (chain && strcmp(chain, this) != 0)        /*匹配指定chain名,这里用chain &&,即若不指定chain,输出所有chain*/6ckLinux联盟
                        continue;6ckLinux联盟
6ckLinux联盟
                if (found) printf("\n");6ckLinux联盟
6ckLinux联盟
                print_header(format, this, handle);        /*输出标头*/6ckLinux联盟
                i = iptc_first_rule(this, handle);        /*移至当前chain的第一条规则*/6ckLinux联盟
6ckLinux联盟
                num = 0;6ckLinux联盟
                while (i) {6ckLinux联盟
                        print_firewall(i,                                        /*输出当前规则*/6ckLinux联盟
                                       iptc_get_target(i, handle),6ckLinux联盟
                                       num++,6ckLinux联盟
                                       format,6ckLinux联盟
                                       *handle);6ckLinux联盟
                        i = iptc_next_rule(i, handle);                        /*移至下一条规则*/6ckLinux联盟
                }6ckLinux联盟
                found = 1;6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        errno = ENOENT;6ckLinux联盟
        return found;6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
可见,在函数中,由iptc_first_chain和iptc_next_chain实现了遍历,iptc_first_rule和iptc_next_rule实现了链中规是的遍历,print_firewall函数在遍历到规则的时候,向终端输出防火墙规则,其第二个参数iptc_get_target又用于获取规则的target。6ckLinux联盟
6ckLinux联盟
前面提到过,在内核中,handler指针指向了从内核中返回的对应的表的信息,handler对应的结构中,涉及到链的结构成员主要有两个:6ckLinux联盟
        struct chain_cache *cache_chain_heads;6ckLinux联盟
        struct chain_cache *cache_chain_iteration;6ckLinux联盟
前者用于指向第一个链,后者指向当前链。而struct chain_cache的定义如下:6ckLinux联盟
struct chain_cache6ckLinux联盟
{6ckLinux联盟
        char name[TABLE_MAXNAMELEN];                /*链名*/6ckLinux联盟
        STRUCT_ENTRY *start;                        /*该链的第一条规则*/6ckLinux联盟
        STRUCT_ENTRY *end;                        /*该链的最后一条规则*/6ckLinux联盟
};6ckLinux联盟
理解了这两个成员,和结构struct chain_cache,再来理解链的遍历函数就不难了。所谓链的遍历,就是将handler对应成员的值取出来。6ckLinux联盟
6ckLinux联盟
#define TC_FIRST_CHAIN                iptc_first_chain6ckLinux联盟
#define TC_NEXT_CHAIN                iptc_next_chain6ckLinux联盟
6ckLinux联盟
函数TC_FIRST_CHAIN用于返回第一个链:6ckLinux联盟
/* Iterator functions to run through the chains. */6ckLinux联盟
const char *6ckLinux联盟
TC_FIRST_CHAIN(TC_HANDLE_T *handle)6ckLinux联盟
{6ckLinux联盟
        /*链首为空,则返回NULL*/6ckLinux联盟
if ((*handle)->cache_chain_heads == NULL6ckLinux联盟
            && !populate_cache(*handle))6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
        /*当前链的指针指向链表首部*/6ckLinux联盟
        (*handle)->cache_chain_iteration6ckLinux联盟
                = &(*handle)->cache_chain_heads[0];6ckLinux联盟
        /*返回链的名称*/6ckLinux联盟
        return (*handle)->cache_chain_iteration->name;6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
/* Iterator functions to run through the chains.  Returns NULL at end. */6ckLinux联盟
const char *6ckLinux联盟
TC_NEXT_CHAIN(TC_HANDLE_T *handle)6ckLinux联盟
{6ckLinux联盟
        /*很简单,用heads开始,用++就可以实现遍历了*/6ckLinux联盟
(*handle)->cache_chain_iteration++;6ckLinux联盟
6ckLinux联盟
        if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads6ckLinux联盟
            == (*handle)->cache_num_chains)6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
        return (*handle)->cache_chain_iteration->name;6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
规则的遍历6ckLinux联盟
当遍历到某个链的时候,接下来,就需要遍历当前链下的所有规则了,输出之了。前面叙述了链的遍历,那么规则的遍历,应该就是根据链的名称,找到对应的成员结构struct chain_cache ,这里面包含了当前链的第一条规则与最后一条规则的指针:6ckLinux联盟
6ckLinux联盟
#define TC_FIRST_RULE                iptc_first_rule6ckLinux联盟
#define TC_NEXT_RULE                iptc_next_rule6ckLinux联盟
/* Get first rule in the given chain: NULL for empty chain. */6ckLinux联盟
const STRUCT_ENTRY *6ckLinux联盟
TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)6ckLinux联盟
{6ckLinux联盟
        struct chain_cache *c;6ckLinux联盟
6ckLinux联盟
        c = find_label(chain, *handle);        /*根据链名,返回对应的struct chain_cache结构*/6ckLinux联盟
        if (!c) {                                                /*没有找到,返回NULL*/6ckLinux联盟
                errno = ENOENT;6ckLinux联盟
                return NULL;6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        /* Empty chain: single return/policy rule */6ckLinux联盟
        if (c->start == c->end)                /*如果是空链*/6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
        (*handle)->cache_rule_end = c->end;6ckLinux联盟
        return c->start;                                /*返回链的首条规则*/6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
/* Returns NULL when rules run out. */6ckLinux联盟
const STRUCT_ENTRY *6ckLinux联盟
TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)6ckLinux联盟
{6ckLinux联盟
        if ((void *)prev + prev->next_offset6ckLinux联盟
            == (void *)(*handle)->cache_rule_end)6ckLinux联盟
                return NULL;6ckLinux联盟
6ckLinux联盟
        return (void *)prev + prev->next_offset;6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
要更解TC_NEXT_RULE函数是如何实现查找下一条规则的,需要首先理解STRUCT_ENTRY结构:6ckLinux联盟
#define STRUCT_ENTRY                struct ipt_entry6ckLinux联盟
ipt_entry结构用于存储链的规则,每一个包过滤规则可以分成两部份:条件和动作。前者在Netfilter中,称为match,后者称之为target。Match又分为两部份,一部份为一些基本的元素,如来源/目的地址,进/出网口,协议等,对应了struct ipt_ip,我们常常将其称为标准的match,另一部份match则以插件的形式存在,是动态可选择,也允许第三方开发的,常常称为扩展的match,如字符串匹配,p2p匹配等。同样,规则的target也是可扩展的。这样,一条规则占用的空间,可以分为:struct ipt_ip+n*match+n*target,(n表示了其个数,这里的match指的是可扩展的match部份)。基于此,规则对应的结构如下:6ckLinux联盟
/* This structure defines each of the firewall rules.  Consists of 36ckLinux联盟
   parts which are 1) general IP header stuff 2) match specific6ckLinux联盟
   stuff 3) the target to perform if the rule matches */6ckLinux联盟
struct ipt_entry6ckLinux联盟
{6ckLinux联盟
        struct ipt_ip ip;                /*标准的match部份*/6ckLinux联盟
6ckLinux联盟
        /* Mark with fields that we care about. */6ckLinux联盟
        unsigned int nfcache;6ckLinux联盟
6ckLinux联盟
        /* Size of ipt_entry + matches */6ckLinux联盟
        u_int16_t target_offset;                /*target的开始位置,是sizeof(ipt_entry+n*match)*/6ckLinux联盟
        /* Size of ipt_entry + matches + target */6ckLinux联盟
        u_int16_t next_offset;                /*下一条规则相对于本条规则的位置,是sizeof(ipt_entry)加上所有的match,以及所有的target*/6ckLinux联盟
6ckLinux联盟
        /* Back pointer */6ckLinux联盟
        unsigned int comefrom;6ckLinux联盟
6ckLinux联盟
        /* Packet and byte counters. */6ckLinux联盟
        struct ipt_counters counters;6ckLinux联盟
6ckLinux联盟
        /* The matches (if any), then the target. */6ckLinux联盟
        unsigned char elems[0];6ckLinux联盟
};6ckLinux联盟
6ckLinux联盟
有了这样的基础,就不难理解遍历规则中,寻找下一条规则语句:6ckLinux联盟
return (void *)prev + prev->next_offset;6ckLinux联盟
即是本条规则加上下一条规则的偏移值。 6ckLinux联盟
输出规则6ckLinux联盟
print_firewall 函数用于规则的输出:6ckLinux联盟
print_firewall(i, iptc_get_target(i, handle), num++,format,*handle);6ckLinux联盟
i:当前的规则;6ckLinux联盟
iptc_get_target(i, handle):用于规则的target部份的处理;6ckLinux联盟
num:规则序号;6ckLinux联盟
format:输出格式;6ckLinux联盟
handler:表的信息;6ckLinux联盟
6ckLinux联盟
6ckLinux联盟
/* e is called `fw' here for hysterical raisins */6ckLinux联盟
static void6ckLinux联盟
print_firewall(const struct ipt_entry *fw,6ckLinux联盟
               const char *targname,6ckLinux联盟
               unsigned int num,6ckLinux联盟
               unsigned int format,6ckLinux联盟
               const iptc_handle_t handle)6ckLinux联盟
{6ckLinux联盟
        struct iptables_target *target = NULL;6ckLinux联盟
        const struct ipt_entry_target *t;6ckLinux联盟
        u_int8_t flags;6ckLinux联盟
        char buf[BUFSIZ];6ckLinux联盟
6ckLinux联盟
        if (!iptc_is_chain(targname, handle))6ckLinux联盟
                target = find_target(targname, TRY_LOAD);6ckLinux联盟
        else6ckLinux联盟
                target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);6ckLinux联盟
6ckLinux联盟
        t = ipt_get_target((struct ipt_entry *)fw);6ckLinux联盟
        flags = fw->ip.flags;6ckLinux联盟
6ckLinux联盟
        if (format & FMT_LINENUMBERS)                        /*输出行号*/6ckLinux联盟
                printf(FMT("%-4u ""%u "), num+1);6ckLinux联盟
6ckLinux联盟
        if (!(format & FMT_NOCOUNTS)) {                        /*详细模式,列出计数器*/6ckLinux联盟
                print_num(fw->counters.pcnt, format);        /*匹配当前规则的数据包个数*/6ckLinux联盟
                print_num(fw->counters.bcnt, format);        /*--------------------大小*/6ckLinux联盟
        }6ckLinux联盟
        /*输出目标名称*/6ckLinux联盟
        if (!(format & FMT_NOTARGET))                        /*目标名称,即拦截、通过等动作*/6ckLinux联盟
                printf(FMT("%-9s ""%s "), targname);6ckLinux联盟
6ckLinux联盟
        /*输出协议名*/6ckLinux联盟
        fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);6ckLinux联盟
        {6ckLinux联盟
                char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);6ckLinux联盟
                if (pname)6ckLinux联盟
                        printf(FMT("%-5s""%s "), pname);6ckLinux联盟
                else6ckLinux联盟
                        printf(FMT("%-5hu""%hu "), fw->ip.proto);6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        /*输出选项字段*/6ckLinux联盟
        if (format & FMT_OPTIONS) {6ckLinux联盟
                if (format & FMT_NOTABLE)6ckLinux联盟
                        fputs("opt ", stdout);6ckLinux联盟
                fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);        //#define IP_FW_INV_FRAG  0x0080  /* Invert the sense of IP_FW_F_FRAG. */        6ckLinux联盟
                fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);                        //#define IP_FW_F_FRAG        0x0004  /* Set if rule is a fragment rule */6ckLinux联盟
                fputc(' ', stdout);6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        if (format & FMT_VIA) {6ckLinux联盟
                char iface[IFNAMSIZ+2];6ckLinux联盟
6ckLinux联盟
                if (fw->ip.invflags & IPT_INV_VIA_IN) {                /*输入端口取反标志*/6ckLinux联盟
                        iface[0] = '!';                                /*设置取反标志符*/6ckLinux联盟
                        iface[1] = '\0';6ckLinux联盟
                }6ckLinux联盟
                else iface[0] = '\0';6ckLinux联盟
6ckLinux联盟
                if (fw->ip.iniface[0] != '\0') {6ckLinux联盟
                        strcat(iface, fw->ip.iniface);6ckLinux联盟
                }6ckLinux联盟
                else if (format & FMT_NUMERIC) strcat(iface, "*");6ckLinux联盟
                else strcat(iface, "any");6ckLinux联盟
                printf(FMT(" %-6s ","in %s "), iface);                /*输出输入端口*/6ckLinux联盟
6ckLinux联盟
                if (fw->ip.invflags & IPT_INV_VIA_OUT) {        /*输出端口取反标志*/6ckLinux联盟
                        iface[0] = '!';                                /*设置取反标志符*/6ckLinux联盟
                        iface[1] = '\0';6ckLinux联盟
                }6ckLinux联盟
                else iface[0] = '\0';6ckLinux联盟
6ckLinux联盟
                if (fw->ip.outiface[0] != '\0') {6ckLinux联盟
                        strcat(iface, fw->ip.outiface);6ckLinux联盟
                }6ckLinux联盟
                else if (format & FMT_NUMERIC) strcat(iface, "*");                6ckLinux联盟
                else strcat(iface, "any");6ckLinux联盟
                printf(FMT("%-6s ","out %s "), iface);                        /*输出输出端口*/6ckLinux联盟
        }                /*end print in/out interface */6ckLinux联盟
6ckLinux联盟
        /*输出源地址及掩码*/6ckLinux联盟
        fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);                /*源地址取反标志*/6ckLinux联盟
        if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))                /*源地址为任意*/6ckLinux联盟
                printf(FMT("%-19s ","%s "), "anywhere");6ckLinux联盟
        else {                                                                        6ckLinux联盟
                if (format & FMT_NUMERIC)6ckLinux联盟
                        sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));6ckLinux联盟
                else6ckLinux联盟
                        sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));6ckLinux联盟
                strcat(buf, mask_to_dotted(&(fw->ip.smsk)));6ckLinux联盟
                printf(FMT("%-19s ","%s "), buf);6ckLinux联盟
        }6ckLinux联盟
        6ckLinux联盟
        /*输出目的地址及掩码*/6ckLinux联盟
        fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);6ckLinux联盟
        if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))6ckLinux联盟
                printf(FMT("%-19s","-> %s"), "anywhere");6ckLinux联盟
        else {6ckLinux联盟
                if (format & FMT_NUMERIC)6ckLinux联盟
                        sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));6ckLinux联盟
                else6ckLinux联盟
                        sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));6ckLinux联盟
                strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));6ckLinux联盟
                printf(FMT("%-19s","-> %s"), buf);6ckLinux联盟
        }6ckLinux联盟
6ckLinux联盟
        if (format & FMT_NOTABLE)6ckLinux联盟
                fputs("  ", stdout);6ckLinux联盟
6ckLinux联盟
        /*输出扩展的MATCH*/6ckLinux联盟
6ckLinux联盟
        IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);                6ckLinux联盟
6ckLinux联盟
        /*输出扩展的TARGET*/6ckLinux联盟
        if (target) {6ckLinux联盟
                if (target->print)6ckLinux联盟
                        /* Print the target information. */6ckLinux联盟
                        target->print(&fw->ip, t, format & FMT_NUMERIC);6ckLinux联盟
        } else if (t->u.target_size != sizeof(*t))6ckLinux联盟
                printf("[%u bytes of unknown target data] ",6ckLinux联盟
                       t->u.target_size - sizeof(*t));6ckLinux联盟
6ckLinux联盟
        if (!(format & FMT_NONEWLINE))6ckLinux联盟
                fputc('\n', stdout);6ckLinux联盟
}6ckLinux联盟
6ckLinux联盟
函数分为三部份:6ckLinux联盟
输出标准的match部份;6ckLinux联盟
输出扩展的match部份,调用IPT_MATCH_ITERATE实现;6ckLinux联盟
调用对应的target的print函数输出target部份。 6ckLinux联盟
match的输出6ckLinux联盟
IPT_MATCH_ITERATE 宏用于实现扩展match的遍历。这个宏定义在内核include/Linux/Netfilter-ipv4/Ip_tables.h中:6ckLinux联盟
#define IPT_MATCH_ITERATE(e, fn, args...)        \6ckLinux联盟
({