亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
1234567下一頁(yè)
最近訪問(wèn)板塊 發(fā)新帖
查看: 15529 | 回復(fù): 64
打印 上一主題 下一主題

iptables 源碼分析 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2005-12-07 09:17 |只看該作者 |倒序?yàn)g覽
這里只是一個(gè)筆記,我會(huì)將進(jìn)一步修改、整理,補(bǔ)充后的版本發(fā)在我的個(gè)人主頁(yè)上:
http://www.skynet.org.cn/viewthr ... 2&page=1#pid262

應(yīng)用軟件源碼分析發(fā)在這里不知道是否合適:D

最近看了一下iptables的實(shí)現(xiàn),現(xiàn)在想陸續(xù)把看的心得貼出來(lái),和大家討論一下,共同學(xué)習(xí)……

一、規(guī)則的顯示
選擇先來(lái)說(shuō)明規(guī)則的顯示,因?yàn)樗婕暗降臇|東簡(jiǎn)單,而且又全面,了解了規(guī)則的顯示,對(duì)于其它操作的了解就顯得容易了。

iptables version 1.2.7

iptables有兩條線:ipv4 和ipv6,這里只分析v4的,因?yàn)関6偶暫時(shí)還用不著,沒(méi)有去看。

iptables_standardone.c
主函數(shù):
int main(int argc, char *argv[])
{
        int ret;
        char *table = "filter";                                /*默認(rèn)的表是filter*/
        iptc_handle_t handle = NULL;

        program_name = "iptables";
        program_version = IPTABLES_VERSION;

#ifdef NO_SHARED_LIBS
        init_extensions();
#endif

        /*進(jìn)入命令行處理函數(shù)*/
ret = do_command(argc, argv, &table, &handle);
        if (ret)
                ret = iptc_commit(&handle);

        if (!ret)
                fprintf(stderr, "iptables: %s\n",
                        iptc_strerror(errno));

        exit(!ret);
}
table表示表的名稱,就是iptables -t 后面跟的那個(gè),默認(rèn)是"filter"
iptc_handle_t handle = NULL;  這個(gè)東東很重要,現(xiàn)在初始化NULL,后面他被用來(lái)存儲(chǔ)一個(gè)表的所有規(guī)則的快照。
        program_name = "iptables";
        program_version = IPTABLES_VERSION;
設(shè)置名稱和版本。
#ifdef NO_SHARED_LIBS
        init_extensions();
#endif
iptables很多東東,是用共享庫(kù)*.so的形式(我們安裝會(huì),可以在諸如/lib/iptables下邊看到),如果不采用共享庫(kù),則進(jìn)行一個(gè)初始化操作。我們假設(shè)是采用共享庫(kù)的,忽略它。

然后就進(jìn)入核心處理模塊:
do_command(argc, argv, &table, &handle);

do_command 函數(shù)是整個(gè)系統(tǒng)的核心,負(fù)責(zé)處理整個(gè)用戶的輸入命令。函數(shù)首先對(duì)一些結(jié)構(gòu)、變量進(jìn)行初始化,初始化完畢后,進(jìn)入while循環(huán),分析用戶輸入的命令,設(shè)置相關(guān)的標(biāo)志變量,然后根據(jù)相應(yīng)標(biāo)志,調(diào)用對(duì)應(yīng)的處理函數(shù)。

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 iptables_match *m;
        struct iptables_target *target = NULL;
        struct iptables_target *t;
        const char *jumpto = "";
        char *protocol = NULL;
        const char *modprobe = NULL;

        /*初始化變量*/
memset(&fw, 0, sizeof(fw));

        opts = original_opts;
        global_option_offset = 0;

        /* re-set optind to 0 in case do_command gets called
         * a second time */
        optind = 0;

        /*初始化兩個(gè)全局變量*/
/* clear mflags in case do_command gets called a second time
         * (we clear the global list of all matches for security)*/
        for (m = iptables_matches; m; m = m->next) {
                m->mflags = 0;
                m->used = 0;
        }

        for (t = iptables_targets; t; t = t->next) {
                t->tflags = 0;
                t->used = 0;
        }
ps:開(kāi)頭一大堆的變量定義和初始化,可以在程序分析的時(shí)候看它們的作用,有兩個(gè)全局結(jié)構(gòu)變量很重要:iptables_matches和iptables_targets,F(xiàn)在來(lái)分析他們的作用會(huì)有一點(diǎn)困難,因?yàn)樗鼈兩婕暗搅颂喾矫娴臇|東,這里,可以先把它們“想像成”用戶空間用來(lái)讀取內(nèi)核規(guī)則的結(jié)構(gòu)(當(dāng)然,這有點(diǎn)錯(cuò)誤)。

/*開(kāi)始化析命令行*/
while ((c = getopt_long(argc, argv,
          "-A:C:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
                                           opts, NULL)) != -1)
{
}

這個(gè)while循環(huán)處理所有的用戶輸入,對(duì)應(yīng)規(guī)則輸出-L,有:
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++];
break;

add_command函數(shù)負(fù)責(zé)將命令標(biāo)志變量command與令標(biāo)志 CMD_LIST求&運(yùn)算, CMD_ZERO只是一個(gè)附加的判斷標(biāo)志而已,invert);然后,從命令行中取得要顯示的鏈名(如果有的話)。

與此相關(guān)的還有用t參數(shù)指定了表名:
                case 't':
                        if (invert)
                                exit_error(PARAMETER_PROBLEM,
                                           "unexpected ! flag before --table");
                        *table = argv[optind-1];
                        break;
即,如果有’t’參數(shù),則取’t’后跟的表名:*table = argv[optind-1],否則,它應(yīng)該是主函數(shù)中默認(rèn)的filter表。

命令處理完畢后,即進(jìn)入執(zhí)行模塊:
/*因?yàn)槌绦蚨x了共享庫(kù)的話,iptables_matches/iptables_target這兩個(gè)結(jié)構(gòu)運(yùn)行至此是NULL,并且target也是NULL,對(duì)于規(guī)則顯示而言,這一部份的處理目前沒(méi)有實(shí)際意義,回過(guò)頭再來(lái)看這一段更易理解。final_check成員函數(shù)的作用是作最終的標(biāo)志檢查,如果檢測(cè)失則,則退出*/
        for (m = iptables_matches; m; m = m->next) {
                if (!m->used)
                        continue;

                m->final_check(m->mflags);
        }

        if (target)
                target->final_check(target->tflags);


接著對(duì)參數(shù)作一些必要的合法性檢查:
        /* 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 !");

/*對(duì)于如果要進(jìn)行(CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)處理來(lái)說(shuō),如果沒(méi)有設(shè)置來(lái)源/目的地址及掩碼,則給予它們一個(gè)默認(rèn)值*/
        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";
        }



/*對(duì)來(lái)源/目的地址及掩碼進(jìn)行拆分,它們總是以 addr/mask的形式來(lái)出現(xiàn)的,根據(jù)’/’前面的字符串取得地址值,根據(jù)’/’后面的掩碼位數(shù),求得正確的掩碼值,值得注意的是,同時(shí)要處理主機(jī)地址和網(wǎng)絡(luò)地址的情況*/
        if (shostnetworkmask)
                parse_hostnetworkmask(shostnetworkmask, &saddrs,
                                      &(fw.ip.smsk), &nsaddrs);

        if (dhostnetworkmask)
                parse_hostnetworkmask(dhostnetworkmask, &daddrs,
                                      &(fw.ip.dmsk), &ndaddrs);

/*然后檢查來(lái)源/目的網(wǎng)絡(luò)地址的合法性*/
        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");

/*對(duì)命令行格式進(jìn)行合法性檢查*/
generic_opt_check(command, options);


如果前面只是熱身的話,那么從現(xiàn)在開(kāi)始,就進(jìn)入實(shí)質(zhì)性階段了:

do_command函數(shù)最后一個(gè)參數(shù)handle,是一個(gè)指向了具體表,如filter、nat表的句柄,這里判斷,如果handle為空,則調(diào)用iptc_init,根據(jù)table的名稱,讓handle指針指向相應(yīng)的表的地址空間,也就是把對(duì)應(yīng)表的所有信息從內(nèi)核中取出來(lái):
        /* only allocate handle if we weren't called with a handle */
        if (!*handle)
                *handle = iptc_init(*table);

        /*如果獲取換敗,將試著插入模塊,再次獲取*/
        if (!*handle) {
                /* try to insmod the module if iptc_init failed */
                iptables_insmod("ip_tables", modprobe);
                *handle = iptc_init(*table);
        /*仍然失敗,則退出*/
        if (!*handle)
                exit_error(VERSION_PROBLEM,
                           "can't initialize iptables table `%s': %s",
                           *table, iptc_strerror(errno));


/*繼續(xù)進(jìn)行一些簡(jiǎn)單的判斷*/
if (command == CMD_APPEND
            || command == CMD_DELETE
            || command == CMD_INSERT
            || command == CMD_REPLACE) {
                /*List命令不在判斷之列,暫時(shí)不分析*/
        }


/*判斷命令標(biāo)志,調(diào)用相關(guān)函數(shù)進(jìn)行處理*/
switch (command) {
        case CMD_LIST:
                ret = list_entries(chain,
                                   options&OPT_VERBOSE,
                                   options&OPT_NUMERIC,
                                   options&OPT_EXPANDED,
                                   options&OPT_LINENUMBERS,
                                   handle);
}
list_entries是規(guī)則顯示的主要處理函數(shù)。
Options是顯示的標(biāo)志變量:
OPT_VERBOSE:對(duì)應(yīng)-v
OPT_NUMERIC:對(duì)應(yīng)-n
OPT_EXPANDED:對(duì)應(yīng)-x
OPT_LINENUMBERS: -l

看來(lái)很簡(jiǎn)單,說(shuō)了這么大一圈子,就是調(diào)用 iptc_init獲取表的規(guī)則信息,調(diào)用list_entries函數(shù)顯示規(guī)則。

[ 本帖最后由 獨(dú)孤九賤 于 2006-3-5 14:49 編輯 ]

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2005-12-07 09:18 |只看該作者

iptc_init

1.1        表的查找
再回到iptc_init 函數(shù)上來(lái),它根據(jù)表名,從內(nèi)核獲取對(duì)應(yīng)的表的相關(guān)信息,handle是一個(gè)iptc_handle_t類型的指針,在libiptc.c中,有如下定義:
/* Transparent handle type. */
typedef struct iptc_handle *iptc_handle_t;

在Libip4tc中:
#define STRUCT_TC_HANDLE        struct iptc_handle
在Libiptc.c中,可以找到STRUCT_TC_HANDLE的定義:
STRUCT_TC_HANDLE
{
        /* Have changes been made? */
        int changed;
        /* Size in here reflects original state. */
        STRUCT_GETINFO info;

        struct counter_map *counter_map;
        /* Array of hook names */
        const char **hooknames;

        /* Cached position of chain heads (NULL = no cache). */
        unsigned int cache_num_chains;
        unsigned int cache_num_builtins;


        /* Rule iterator: terminal rule */
        STRUCT_ENTRY *cache_rule_end;

        /* Number in here reflects current state. */
        unsigned int new_number;
        STRUCT_GET_ENTRIES entries;
};

再來(lái)看看iptc_init函數(shù),同樣在在Libip4tc中,有如下定義:
#define TC_INIT                        iptc_init

在Libiptc.c中,可以看到函數(shù)的實(shí)現(xiàn),基本上iptables與內(nèi)核的交互,都是使用setsockopt函數(shù)來(lái)實(shí)現(xiàn)的,對(duì)于獲取取規(guī)是信息來(lái)說(shuō),標(biāo)志位是SO_GET_INFO,而從內(nèi)核返回回來(lái)的規(guī)則信息是一個(gè)STRUCT_GETINFO結(jié)構(gòu):

TC_HANDLE_T TC_INIT(const char *tablename)
{
        TC_HANDLE_T h;
        STRUCT_GETINFO info;
        unsigned int i;
        int tmp;
        socklen_t s;

        iptc_fn = TC_INIT;

        if (sockfd != -1)
                close(sockfd);

        /*為獲取信息打開(kāi)一個(gè)套接字接口*/
        sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
        if (sockfd < 0)
                return NULL;

        s = sizeof(info);
        if (strlen(tablename) >= TABLE_MAXNAMELEN) {
                errno = EINVAL;
                return NULL;
        }
        strcpy(info.name, tablename);
/*獲取規(guī)則信息*/
        if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
                return NULL;

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

/* Too hard --RR */
#if 0
        sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
        dynlib = dlopen(pathname, RTLD_NOW);
        if (!dynlib) {
                errno = ENOENT;
                return NULL;
        }
        h->hooknames = dlsym(dynlib, "hooknames");
        if (!h->hooknames) {
                errno = ENOENT;
                return NULL;
        }
#else
        h->hooknames = hooknames;
#endif

        /* Initialize current state */
        h->info = info;
        h->new_number = h->info.num_entries;
        for (i = 0; i < h->info.num_entries; i++)
                h->counter_map[i]
                        = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});

        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) {
                free(h);
                return NULL;
        }

        CHECK(h);
        return h;
}

        函數(shù)為h分配空間,然后賦予相應(yīng)的值。要理解這個(gè)函數(shù),還需要了解STRUCT_GETINFO結(jié)構(gòu)和分配內(nèi)存空間的函數(shù)alloc_handle。

#define STRUCT_GETINFO                struct ipt_getinfo

/* The argument to IPT_SO_GET_INFO */

struct ipt_getinfo
{
        /* Which table: caller fills this in. */
        char name[IPT_TABLE_MAXNAMELEN];

        /* Kernel fills these in. */
        /* Which hook entry points are valid: bitmask */
        unsigned int valid_hooks;

        /* Hook entry points: one per netfilter hook. */
        unsigned int hook_entry[NF_IP_NUMHOOKS];

        /* Underflow points. */
        unsigned int underflow[NF_IP_NUMHOOKS];

        /* Number of entries */
        unsigned int num_entries;

        /* Size of entries. */
        unsigned int size;
};

/* Allocate handle of given size */
static TC_HANDLE_T
alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
{
        size_t len;
        TC_HANDLE_T h;

        len = sizeof(STRUCT_TC_HANDLE)
                + size
                + num_rules * sizeof(struct counter_map);

        if ((h = malloc(len)) == NULL) {
                errno = ENOMEM;
                return NULL;
        }

        h->changed = 0;
        h->cache_num_chains = 0;
        h->cache_chain_heads = NULL;
        h->counter_map = (void *)h
                + sizeof(STRUCT_TC_HANDLE)
                + size;
        strcpy(h->info.name, tablename);
        strcpy(h->entries.name, tablename);

        return h;
}

論壇徽章:
0
3 [報(bào)告]
發(fā)表于 2005-12-07 09:20 |只看該作者

list_entries

函數(shù)list_entries用于顯示表下邊的鏈:
/*顯示某table下的chain*/
static int
list_entries(const ipt_chainlabel chain, int verbose, int numeric,
             int expanded, int linenumbers, iptc_handle_t *handle)
{
        int found = 0;
        unsigned int format;
        const char *this;

        format = FMT_OPTIONS;                                /*設(shè)置輸出格式*/
        if (!verbose)                                        /*詳細(xì)輸出模式,,對(duì)應(yīng)-v ,顯示匹配的包的數(shù)目,包的大小等*/
                format |= FMT_NOCOUNTS;
        else
                format |= FMT_VIA;                       

        if (numeric)                                        /*對(duì)應(yīng)-n,以數(shù)字的形式輸出地址和端口*/
                format |= FMT_NUMERIC;

        if (!expanded)                                        /*對(duì)應(yīng)-x,expand numbers (display exact values)*/
                format |= FMT_KILOMEGAGIGA;

        if (linenumbers)                                /*輸出行的編號(hào)*/
                format |= FMT_LINENUMBERS;

        for (this = iptc_first_chain(handle);                /*遍歷當(dāng)前table的所有chain*/
             this;
             this = iptc_next_chain(handle))
{               
                const struct ipt_entry *i;
                unsigned int num;

                if (chain && strcmp(chain, this) != 0)        /*匹配指定chain名,這里用chain &&,即若不指定chain,輸出所有chain*/
                        continue;

                if (found) printf("\n");

                print_header(format, this, handle);        /*輸出標(biāo)頭*/
                i = iptc_first_rule(this, handle);        /*移至當(dāng)前chain的第一條規(guī)則*/

                num = 0;
                while (i) {
                        print_firewall(i,                                        /*輸出當(dāng)前規(guī)則*/
                                       iptc_get_target(i, handle),
                                       num++,
                                       format,
                                       *handle);
                        i = iptc_next_rule(i, handle);                        /*移至下一條規(guī)則*/
                }
                found = 1;
        }

        errno = ENOENT;
        return found;
}

可見(jiàn),在函數(shù)中,由iptc_first_chain和iptc_next_chain實(shí)現(xiàn)了遍歷,iptc_first_rule和iptc_next_rule實(shí)現(xiàn)了鏈中規(guī)是的遍歷,print_firewall函數(shù)在遍歷到規(guī)則的時(shí)候,向終端輸出防火墻規(guī)則,其第二個(gè)參數(shù)iptc_get_target又用于獲取規(guī)則的target。

前面提到過(guò),在內(nèi)核中,handler指針指向了從內(nèi)核中返回的對(duì)應(yīng)的表的信息,handler對(duì)應(yīng)的結(jié)構(gòu)中,涉及到鏈的結(jié)構(gòu)成員主要有兩個(gè):
        struct chain_cache *cache_chain_heads;
        struct chain_cache *cache_chain_iteration;
前者用于指向第一個(gè)鏈,后者指向當(dāng)前鏈。而struct chain_cache的定義如下:
struct chain_cache
{
        char name[TABLE_MAXNAMELEN];                /*鏈名*/
        STRUCT_ENTRY *start;                        /*該鏈的第一條規(guī)則*/
        STRUCT_ENTRY *end;                        /*該鏈的最后一條規(guī)則*/
};
理解了這兩個(gè)成員,和結(jié)構(gòu)struct chain_cache,再來(lái)理解鏈的遍歷函數(shù)就不難了。所謂鏈的遍歷,就是將handler對(duì)應(yīng)成員的值取出來(lái)。

#define TC_FIRST_CHAIN                iptc_first_chain
#define TC_NEXT_CHAIN                iptc_next_chain

函數(shù)TC_FIRST_CHAIN用于返回第一個(gè)鏈:
/* Iterator functions to run through the chains. */
const char *
TC_FIRST_CHAIN(TC_HANDLE_T *handle)
{
        /*鏈?zhǔn)诪榭,則返回NULL*/
if ((*handle)->cache_chain_heads == NULL
            && !populate_cache(*handle))
                return NULL;

        /*當(dāng)前鏈的指針指向鏈表首部*/
        (*handle)->cache_chain_iteration
                = &(*handle)->cache_chain_heads[0];
        /*返回鏈的名稱*/
        return (*handle)->cache_chain_iteration->name;
}

/* Iterator functions to run through the chains.  Returns NULL at end. */
const char *
TC_NEXT_CHAIN(TC_HANDLE_T *handle)
{
        /*很簡(jiǎn)單,用heads開(kāi)始,用++就可以實(shí)現(xiàn)遍歷了*/
(*handle)->cache_chain_iteration++;

        if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
            == (*handle)->cache_num_chains)
                return NULL;

        return (*handle)->cache_chain_iteration->name;
}

規(guī)則的遍歷
當(dāng)遍歷到某個(gè)鏈的時(shí)候,接下來(lái),就需要遍歷當(dāng)前鏈下的所有規(guī)則了,輸出之了。前面敘述了鏈的遍歷,那么規(guī)則的遍歷,應(yīng)該就是根據(jù)鏈的名稱,找到對(duì)應(yīng)的成員結(jié)構(gòu)struct chain_cache ,這里面包含了當(dāng)前鏈的第一條規(guī)則與最后一條規(guī)則的指針:

#define TC_FIRST_RULE                iptc_first_rule
#define TC_NEXT_RULE                iptc_next_rule
/* Get first rule in the given chain: NULL for empty chain. */
const STRUCT_ENTRY *
TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
{
        struct chain_cache *c;

        c = find_label(chain, *handle);        /*根據(jù)鏈名,返回對(duì)應(yīng)的struct chain_cache結(jié)構(gòu)*/
        if (!c) {                                                /*沒(méi)有找到,返回NULL*/
                errno = ENOENT;
                return NULL;
        }

        /* Empty chain: single return/policy rule */
        if (c->start == c->end)                /*如果是空鏈*/
                return NULL;

        (*handle)->cache_rule_end = c->end;
        return c->start;                                /*返回鏈的首條規(guī)則*/
}

/* Returns NULL when rules run out. */
const STRUCT_ENTRY *
TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
{
        if ((void *)prev + prev->next_offset
            == (void *)(*handle)->cache_rule_end)
                return NULL;

        return (void *)prev + prev->next_offset;
}

要更解TC_NEXT_RULE函數(shù)是如何實(shí)現(xiàn)查找下一條規(guī)則的,需要首先理解STRUCT_ENTRY結(jié)構(gòu):
#define STRUCT_ENTRY                struct ipt_entry
ipt_entry結(jié)構(gòu)用于存儲(chǔ)鏈的規(guī)則,每一個(gè)包過(guò)濾規(guī)則可以分成兩部份:條件和動(dòng)作。前者在Netfilter中,稱為match,后者稱之為target。Match又分為兩部份,一部份為一些基本的元素,如來(lái)源/目的地址,進(jìn)/出網(wǎng)口,協(xié)議等,對(duì)應(yīng)了struct ipt_ip,我們常常將其稱為標(biāo)準(zhǔn)的match,另一部份match則以插件的形式存在,是動(dòng)態(tài)可選擇,也允許第三方開(kāi)發(fā)的,常常稱為擴(kuò)展的match,如字符串匹配,p2p匹配等。同樣,規(guī)則的target也是可擴(kuò)展的。這樣,一條規(guī)則占用的空間,可以分為:struct ipt_ip+n*match+n*target,(n表示了其個(gè)數(shù),這里的match指的是可擴(kuò)展的match部份);诖耍(guī)則對(duì)應(yīng)的結(jié)構(gòu)如下:
/* This structure defines each of the firewall rules.  Consists of 3
   parts which are 1) general IP header stuff 2) match specific
   stuff 3) the target to perform if the rule matches */
struct ipt_entry
{
        struct ipt_ip ip;                /*標(biāo)準(zhǔn)的match部份*/

        /* Mark with fields that we care about. */
        unsigned int nfcache;

        /* Size of ipt_entry + matches */
        u_int16_t target_offset;                /*target的開(kāi)始位置,是sizeof(ipt_entry+n*match)*/
        /* Size of ipt_entry + matches + target */
        u_int16_t next_offset;                /*下一條規(guī)則相對(duì)于本條規(guī)則的位置,是sizeof(ipt_entry)加上所有的match,以及所有的target*/

        /* Back pointer */
        unsigned int comefrom;

        /* Packet and byte counters. */
        struct ipt_counters counters;

        /* The matches (if any), then the target. */
        unsigned char elems[0];
};

有了這樣的基礎(chǔ),就不難理解遍歷規(guī)則中,尋找下一條規(guī)則語(yǔ)句:
return (void *)prev + prev->next_offset;
即是本條規(guī)則加上下一條規(guī)則的偏移值。

論壇徽章:
0
4 [報(bào)告]
發(fā)表于 2005-12-07 09:21 |只看該作者
輸出規(guī)則
print_firewall 函數(shù)用于規(guī)則的輸出:
print_firewall(i, iptc_get_target(i, handle), num++,format,*handle);
i:當(dāng)前的規(guī)則;
iptc_get_target(i, handle):用于規(guī)則的target部份的處理;
num:規(guī)則序號(hào);
format:輸出格式;
handler:表的信息;


/* e is called `fw' here for hysterical raisins */
static void
print_firewall(const struct ipt_entry *fw,
               const char *targname,
               unsigned int num,
               unsigned int format,
               const iptc_handle_t handle)
{
        struct iptables_target *target = NULL;
        const struct ipt_entry_target *t;
        u_int8_t flags;
        char buf[BUFSIZ];

        if (!iptc_is_chain(targname, handle))
                target = find_target(targname, TRY_LOAD);
        else
                target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);

        t = ipt_get_target((struct ipt_entry *)fw);
        flags = fw->ip.flags;

        if (format & FMT_LINENUMBERS)                        /*輸出行號(hào)*/
                printf(FMT("%-4u ", "%u "), num+1);

        if (!(format & FMT_NOCOUNTS)) {                        /*詳細(xì)模式,列出計(jì)數(shù)器*/
                print_num(fw->counters.pcnt, format);        /*匹配當(dāng)前規(guī)則的數(shù)據(jù)包個(gè)數(shù)*/
                print_num(fw->counters.bcnt, format);        /*--------------------大小*/
        }
        /*輸出目標(biāo)名稱*/
        if (!(format & FMT_NOTARGET))                        /*目標(biāo)名稱,即攔截、通過(guò)等動(dòng)作*/
                printf(FMT("%-9s ", "%s "), targname);

        /*輸出協(xié)議名*/
        fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
        {
                char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
                if (pname)
                        printf(FMT("%-5s", "%s "), pname);
                else
                        printf(FMT("%-5hu", "%hu "), fw->ip.proto);
        }

        /*輸出選項(xiàng)字段*/
        if (format & FMT_OPTIONS) {
                if (format & FMT_NOTABLE)
                        fputs("opt ", stdout);
                fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);        //#define IP_FW_INV_FRAG  0x0080  /* Invert the sense of IP_FW_F_FRAG. */       
                fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);                        //#define IP_FW_F_FRAG        0x0004  /* Set if rule is a fragment rule */
                fputc(' ', stdout);
        }

        if (format & FMT_VIA) {
                char iface[IFNAMSIZ+2];

                if (fw->ip.invflags & IPT_INV_VIA_IN) {                /*輸入端口取反標(biāo)志*/
                        iface[0] = '!';                                /*設(shè)置取反標(biāo)志符*/
                        iface[1] = '\0';
                }
                else iface[0] = '\0';

                if (fw->ip.iniface[0] != '\0') {
                        strcat(iface, fw->ip.iniface);
                }
                else if (format & FMT_NUMERIC) strcat(iface, "*");
                else strcat(iface, "any");
                printf(FMT(" %-6s ","in %s "), iface);                /*輸出輸入端口*/

                if (fw->ip.invflags & IPT_INV_VIA_OUT) {        /*輸出端口取反標(biāo)志*/
                        iface[0] = '!';                                /*設(shè)置取反標(biāo)志符*/
                        iface[1] = '\0';
                }
                else iface[0] = '\0';

                if (fw->ip.outiface[0] != '\0') {
                        strcat(iface, fw->ip.outiface);
                }
                else if (format & FMT_NUMERIC) strcat(iface, "*");               
                else strcat(iface, "any");
                printf(FMT("%-6s ","out %s "), iface);                        /*輸出輸出端口*/
        }                /*end print in/out interface */

        /*輸出源地址及掩碼*/
        fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);                /*源地址取反標(biāo)志*/
        if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))                /*源地址為任意*/
                printf(FMT("%-19s ","%s "), "anywhere");
        else {                                                                       
                if (format & FMT_NUMERIC)
                        sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
                else
                        sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
                strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
                printf(FMT("%-19s ","%s "), buf);
        }
       
        /*輸出目的地址及掩碼*/
        fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
        if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
                printf(FMT("%-19s","-> %s"), "anywhere");
        else {
                if (format & FMT_NUMERIC)
                        sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
                else
                        sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
                strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
                printf(FMT("%-19s","-> %s"), buf);
        }

        if (format & FMT_NOTABLE)
                fputs("  ", stdout);

        /*輸出擴(kuò)展的MATCH*/

        IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);               

        /*輸出擴(kuò)展的TARGET*/
        if (target) {
                if (target->print)
                        /* Print the target information. */
                        target->print(&fw->ip, t, format & FMT_NUMERIC);
        } else if (t->u.target_size != sizeof(*t))
                printf("[%u bytes of unknown target data] ",
                       t->u.target_size - sizeof(*t));

        if (!(format & FMT_NONEWLINE))
                fputc('\n', stdout);
}

函數(shù)分為三部份:
輸出標(biāo)準(zhǔn)的match部份;
輸出擴(kuò)展的match部份,調(diào)用IPT_MATCH_ITERATE實(shí)現(xiàn);
調(diào)用對(duì)應(yīng)的target的print函數(shù)輸出target部份。

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2005-12-07 09:22 |只看該作者

match的輸出

match的輸出
IPT_MATCH_ITERATE 宏用于實(shí)現(xiàn)擴(kuò)展match的遍歷。這個(gè)宏定義在內(nèi)核include/Linux/Netfilter-ipv4/Ip_tables.h中:
#define IPT_MATCH_ITERATE(e, fn, args...)        \
({                                                \
        unsigned int __i;                        \
        int __ret = 0;                                \
        struct ipt_entry_match *__match;        \
                                                \
        for (__i = sizeof(struct ipt_entry);        \               
             __i < (e)->target_offset;                \               
             __i += __match->u.match_size) {        \
                __match = (void *)(e) + __i;        \
                                                \
                __ret = fn(__match , ## args);        \                /*每找到一個(gè)match,就交由fn函數(shù)來(lái)處理,在print_firewall中,傳遞過(guò)來(lái)的是函數(shù)print_match*/
                if (__ret != 0)                        \
                        break;                        \
        }                                        \
        __ret;                                        \
})

要理解這個(gè)宏,需要先了解規(guī)則的存儲(chǔ),前面提到過(guò),因?yàn)閙atch/target都是可變的,所以在內(nèi)存中,采取了ip_entry+n*match+n*target,即在規(guī)則后,是連續(xù)的若干個(gè)match,而mathc后面,又是若干個(gè)target,在結(jié)構(gòu)ip_entry中,成員u_int16_t target_offset;代表了target的偏移地址,即target的開(kāi)始,match的結(jié)束。我們要查到當(dāng)前規(guī)則對(duì)應(yīng)的所有match,需要了解三個(gè)要素:
1、match從哪里開(kāi)始:起始地址應(yīng)該是 [當(dāng)前規(guī)則地址+sizeof(struct ipt_entry)];
2、match從哪里結(jié)束:結(jié)束地址,應(yīng)該是 [當(dāng)前規(guī)則地址+target_offet];
3、每一個(gè)match的大小,在內(nèi)核中,match對(duì)應(yīng)的結(jié)構(gòu)是ipt_entry_match,其成員u.match_size指明了當(dāng)前match的大小;
這三點(diǎn),對(duì)應(yīng)了for循環(huán):
for (__i = sizeof(struct ipt_entry);        __i < (e)->target_offset;        __i += __match->u.match_size)
這樣,i就對(duì)應(yīng)了某個(gè)match的偏移植,通過(guò):
__match = (void *)(e) + __i;
就得到了match的地址。
再通過(guò)
__ret = fn(__match , ## args);       
輸出之。
fn函數(shù)是在print_firewall中,傳遞過(guò)來(lái)的是函數(shù)print_match。

static int
print_match(const struct ipt_entry_match *m,
            const struct ipt_ip *ip,
            int numeric)
{
        /*根據(jù)match名稱進(jìn)行查找,返回一個(gè)iptables_match結(jié)構(gòu),然后調(diào)用其中封裝的print函數(shù)輸出該match的信息*/
struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);

        if (match) {
                if (match->print)
                        match->print(ip, m, numeric);
                else
                        printf("%s ", match->name);
        } else {
                if (m->u.user.name[0])
                        printf("UNKNOWN match `%s' ", m->u.user.name);
        }
        /* Don't stop iterating. */
        return 0;
}

這里涉及到兩個(gè)重要的結(jié)構(gòu):
struct ipt_entry_match:在內(nèi)核中用于存儲(chǔ)擴(kuò)展match信息
struct ipt_entry_match
{
        union {
                struct {
                        u_int16_t match_size;

                        /* Used by userspace */
                        char name[IPT_FUNCTION_MAXNAMELEN];
                } user;
                struct {
                        u_int16_t match_size;

                        /* Used inside the kernel */
                        struct ipt_match *match;
                } kernel;

                /* Total length */
                u_int16_t match_size;
        } u;

        unsigned char data[0];
};

struct iptables_match:用于用戶級(jí)的match存儲(chǔ):
/* Include file for additions: new matches and targets. */
struct iptables_match
{
        /* Match鏈,初始為NULL */
struct iptables_match *next;

        /* Match名,和核心模塊加載類似,作為動(dòng)態(tài)鏈接庫(kù)存在的Iptables Extension的命名規(guī)則為libipt_'name'.so */
        ipt_chainlabel name;

        /*版本信息,一般設(shè)為NETFILTER_VERSION */
        const char *version;

        /* Match數(shù)據(jù)的大小,必須用IPT_ALIGN()宏指定對(duì)界*/
        size_t size;

        /*由于內(nèi)核可能修改某些域,因此size可能與確切的用戶數(shù)據(jù)不同,這時(shí)就應(yīng)該把不會(huì)被改變的數(shù)據(jù)放在數(shù)據(jù)區(qū)的前面部分,而這里就應(yīng)該填寫被改變的數(shù)據(jù)區(qū)大。灰话銇(lái)說(shuō),這個(gè)值和size相同*/
        size_t userspacesize;

        /*當(dāng)iptables要求顯示當(dāng)前match的信息時(shí)(比如iptables-m ip_ext -h),就會(huì)調(diào)用這個(gè)函數(shù),輸出在iptables程序的通用信息之后. */
        void (*help)(void);

        /*初始化,在parse之前調(diào)用. */
        void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);

        /*掃描并接收本match的命令行參數(shù),正確接收時(shí)返回非0,flags用于保存狀態(tài)信息*/
        int (*parse)(int c, char **argv, int invert, unsigned int *flags,
                     const struct ipt_entry *entry,
                     unsigned int *nfcache,
                     struct ipt_entry_match **match);

        /* 前面提到過(guò)這個(gè)函數(shù),當(dāng)命令行參數(shù)全部處理完畢以后調(diào)用,如果不正確,應(yīng)該
退出(exit_error())*/
        void (*final_check)(unsigned int flags);

        /*當(dāng)查詢當(dāng)前表中的規(guī)則時(shí),顯示使用了當(dāng)前match的規(guī)則*/
        void (*print)(const struct ipt_ip *ip,
                      const struct ipt_entry_match *match, int numeric);

        /*按照parse允許的格式將本match的命令行參數(shù)輸出到標(biāo)準(zhǔn)輸出,用于iptables-save命令. */
        void (*save)(const struct ipt_ip *ip,
                     const struct ipt_entry_match *match);

        /* NULL結(jié)尾的參數(shù)列表,struct option與getopt(3)使用的結(jié)構(gòu)相同*/
        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
};

理解了這兩個(gè)結(jié)構(gòu)后,再來(lái)看find_match函數(shù):

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2005-12-07 09:35 |只看該作者

find_match

即然match是以可擴(kuò)展的形式表現(xiàn)出來(lái),那么,當(dāng)然就需要find_match這樣的函數(shù)將它們一一找出來(lái)了。

前面說(shuō)過(guò),在輸出規(guī)則的函數(shù)中:
IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
用來(lái)遍歷每一個(gè)match,找到了后,就調(diào)用print_match來(lái)輸出。print_match是調(diào)用find_match來(lái)查找的:

struct iptables_match *
find_match(const char *name, enum ipt_tryload tryload)
{
        struct iptables_match *ptr;

        for (ptr = iptables_matches; ptr; ptr = ptr->next) {
                if (strcmp(name, ptr->name) == 0)
                        break;
        }

#ifndef NO_SHARED_LIBS
        if (!ptr && tryload != DONT_LOAD) {
                char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
                         + strlen(name)];
                sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
                if (dlopen(path, RTLD_NOW)) {
                        /* Found library.  If it didn't register itself,
                           maybe they specified target as match. */
                        ptr = find_match(name, DONT_LOAD);

                        if (!ptr)
                                exit_error(PARAMETER_PROBLEM,
                                           "Couldn't load match `%s'\n",
                                           name);
                } else if (tryload == LOAD_MUST_SUCCEED)
                        exit_error(PARAMETER_PROBLEM,
                                   "Couldn't load match `%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 match `%s'\n", name);
        }
#endif

        if (ptr)
                ptr->used = 1;

        return ptr;
}

分析這個(gè)函數(shù),不從開(kāi)頭來(lái)看,先看這一段:
        if (!ptr && tryload != DONT_LOAD) {
                char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
                         + strlen(name)];
                sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
                if (dlopen(path, RTLD_NOW)) {
                        /* Found library.  If it didn't register itself,
                           maybe they specified target as match. */
                        ptr = find_match(name, DONT_LOAD);

                        if (!ptr)
                                exit_error(PARAMETER_PROBLEM,
                                           "Couldn't load match `%s'\n",
                                           name);
                } else if (tryload == LOAD_MUST_SUCCEED)
                        exit_error(PARAMETER_PROBLEM,
                                   "Couldn't load match `%s':%s\n",
                                   name, dlerror());
        }
函數(shù)根據(jù)傳遞過(guò)來(lái)的match名稱,從指定位置,加載對(duì)應(yīng)的共享庫(kù),呵呵,這些共享庫(kù)的源碼,全部在Extensions目錄下邊:
如果加載它們,那么其_init函數(shù)就會(huì)被調(diào)用。這個(gè)初始化函數(shù)用來(lái)向iptables_match全局結(jié)構(gòu)注冊(cè)當(dāng)前match的相關(guān)處理函數(shù)。(這樣,我們可以寫我們自己的用戶空間的擴(kuò)展match處理工具了)。注冊(cè)好后,函數(shù)再來(lái)調(diào)用自己:
ptr = find_match(name, DONT_LOAD);
遞歸回來(lái)后,呵呵,就是開(kāi)頭那一段了,我們需要從已經(jīng)注冊(cè)好的全局結(jié)構(gòu)中查找與當(dāng)前match名稱相同的iptables_match成員,因?yàn)樵摮蓡T中封裝了print函數(shù),這樣就可以順利地輸出來(lái)了:
比如,加載了libptc_tcp.so,它用來(lái)處理tcp的擴(kuò)展,我們來(lái)看Extensions/libiptc_tcp.c:
static
struct iptables_match tcp
= { NULL,
    "tcp",
    IPTABLES_VERSION,
    IPT_ALIGN(sizeof(struct ipt_tcp)),
    IPT_ALIGN(sizeof(struct ipt_tcp)),
    &help,
    &init,
    &parse,
    &final_check,
    &print,
    &save,
    opts };

void
_init(void)
{
        register_match(&tcp);
}

構(gòu)建了一個(gè)
iptables_match結(jié)構(gòu),其間有其對(duì)應(yīng)的所有用戶空間工具函數(shù),如分析命令行、輸出、保存……
然后,就調(diào)用register_match函數(shù)將其插入至全局結(jié)構(gòu)iptables_match當(dāng)中:
void
register_match(struct iptables_match *me)
{
        struct iptables_match **i;

        if (strcmp(me->version, program_version) != 0) {
                fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
                        program_name, me->name, me->version, program_version);
                exit(1);
        }

        if (find_match(me->name, DONT_LOAD)) {
                fprintf(stderr, "%s: match `%s' already registered.\n",
                        program_name, me->name);
                exit(1);
        }

        if (me->size != IPT_ALIGN(me->size)) {
                fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
                        program_name, me->name, me->size);
                exit(1);
        }

        /* Append to list. */
        for (i = &iptables_matches; *i; i = &(*i)->next);
        me->next = NULL;
        *i = me;

        me->m = NULL;
        me->mflags = 0;
}

函數(shù)就是一個(gè)建立鏈表的過(guò)程。不進(jìn)一步分析了。

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2005-12-07 09:43 |只看該作者
暫時(shí)寫到這里吧,太累了,原以為兩三句就可以寫完的,target的查找與match是一模一樣的,就不寫下去了,添加/刪除等規(guī)則操作,改開(kāi)空了繼續(xù)寫。

因?yàn)榭磇ptables時(shí),沒(méi)有找到參考資料,所以文章中有些地方肯定有錯(cuò)。另一方面,因?yàn)闀r(shí)間關(guān)系,沒(méi)有什么條理。不過(guò)還是歡迎感興趣的朋友,一起討論。

總結(jié)一下顯示流程:
int main(int argc, char *argv[])  //主函數(shù)

int do_command()                    //命令行處理

*handle = iptc_init(*table);     //從內(nèi)核獲取表的規(guī)則快照

list_entries                              //顯示規(guī)則

        for (this = iptc_first_chain(handle);
             this;
             this = iptc_next_chain(handle)) {
                const struct ipt_entry *i;
                unsigned int num;

                if (chain && strcmp(chain, this) != 0)
                        continue;

                if (found) printf("\n");

                print_header(format, this, handle);
                i = iptc_first_rule(this, handle);

                num = 0;
                while (i) {
                        print_firewall(i,
                                       iptc_get_target(i, handle),
                                       num++,
                                       format,
                                       *handle);
                        i = iptc_next_rule(i, handle);
                }
                found = 1;
        }
這一段是遍歷表的每個(gè)鏈,然后再遍歷鏈的每條規(guī)則,然后調(diào)用print_filrewall輸出規(guī)則;

輸出函數(shù)在輸出標(biāo)準(zhǔn)的match,如地址/協(xié)議/網(wǎng)口后,就調(diào)用
        IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
遍歷規(guī)則的每一個(gè)match,然后調(diào)用print_match輸出。
print_match中,調(diào)用find_match加載共享庫(kù),共享庫(kù)又通過(guò)_init函數(shù)向主函數(shù)的全局結(jié)構(gòu)iptables_match注冊(cè)自己的處理函數(shù)。
這樣,根據(jù)match的名稱,我們?cè)趐rint_match中,就可以很容易地調(diào)用其print函數(shù),輸出其值了。

target也是一樣的原理。

休息一下……

論壇徽章:
0
8 [報(bào)告]
發(fā)表于 2005-12-07 09:49 |只看該作者
兄弟,不錯(cuò),謝謝。

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2005-12-07 10:25 |只看該作者
去掉符號(hào)表情吧,要不太亂了

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2005-12-07 10:32 |只看該作者
原帖由 albcamus 于 2005-12-7 10:25 發(fā)表
去掉符號(hào)表情吧,要不太亂了


我也沒(méi)辦法去啊,我把字打出來(lái),一發(fā)表,它就成那樣了
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP