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

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 35750 | 回復: 0
打印 上一主題 下一主題

[網(wǎng)絡子系統(tǒng)] Netfilter之連接跟蹤實現(xiàn)機制初步分析 [復制鏈接]

論壇徽章:
169
申猴
日期:2013-10-09 10:10:16天秤座
日期:2013-10-10 15:28:08天蝎座
日期:2014-07-17 14:02:54丑牛
日期:2014-07-17 14:03:04處女座
日期:2014-07-17 14:03:12雙子座
日期:2014-07-17 14:03:21天秤座
日期:2014-07-17 14:03:29酉雞
日期:2014-07-17 14:03:39金牛座
日期:2014-07-21 10:37:54水瓶座
日期:2014-07-22 16:56:09巳蛇
日期:2014-07-23 11:48:03天蝎座
日期:2014-07-31 10:16:36
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2019-07-26 17:03 |只看該作者 |倒序瀏覽


Netfilter之連接跟蹤實現(xiàn)機制初步分析    原作者:Minit

本帖子只作為初步分析,因為時間和自身水平的緣故,其中定存在不妥的地方,希望看到本帖的各位能夠指正。當修正和完善后再提供PDF文檔,謝謝大家的關注。

1.     前言
      Netfilter中的連接跟蹤模塊作為地址轉(zhuǎn)換等的基礎,在對Netfilter的實現(xiàn)機制有所了解的基礎上再深入理解連接跟蹤的實現(xiàn)機制,對于充分應用Netfilter框架的功能和擴展其他的模塊有著重大的作用。本文只是簡要的分析連接跟蹤的整體框架,其中的重要數(shù)據(jù)結(jié)構(gòu)和重要函數(shù),并粗略的描繪了數(shù)據(jù)包轉(zhuǎn)發(fā)的連接跟蹤流程。分析的內(nèi)核源碼為2.6.21.2。有關Netfilter的分析請參考:
2.     整體框架
      連接跟蹤機制是基于Netfilter架構(gòu)實現(xiàn)的,其在Netfilter的不同鉤子點中注冊了相應的鉤子函數(shù),下面圖2-1描繪了連接跟蹤在Netfilter架構(gòu)中注冊的鉤子函數(shù)。

3.重要數(shù)據(jù)結(jié)構(gòu)

3.1.連接記錄

      Linux內(nèi)核中,連接記錄由ip_conntrack結(jié)構(gòu)表示,其結(jié)構(gòu)如圖3-1所示。在該結(jié)構(gòu)中,包含一個nf_conntrack類型的結(jié)構(gòu),其記錄了連接記錄被公開應用的計數(shù),也方便其他地方對連接跟蹤的引用。每個連接記錄都對應一個指向連接超時的函數(shù)指針,當較長時間內(nèi)未使用該連接,將調(diào)用該指針所指向的函數(shù)。如果針對某種協(xié)議的連接跟蹤需要擴展模塊的輔助,則在連接記錄中會有一指向ip_conntrack_helper結(jié)構(gòu)體的指針。連接記錄中的結(jié)構(gòu)體ip_conntrack_tuple_hash實際記錄了連接所跟蹤的地址信息(源和目的地址)和協(xié)議的特定信息(端口)。所有連接記錄的ip_conntrack_tuple_hash以散列形式保存在連接跟蹤表中。


3.2.連接跟蹤表

      連接跟蹤表是記錄所有連接記錄的散列表,其由全局變量ip_conntrack_hash所指向。連接跟蹤表實際是一個以散列值排列的雙向鏈表數(shù)組,鏈表中的元素即為連接記錄所包含的ip_conntrack_tuple_hash結(jié)構(gòu),表的結(jié)構(gòu)如下圖3-2所示。

3.3.連接跟蹤輔助模塊
     在連接跟蹤的實現(xiàn)機制中提供了helper輔助模塊以擴展連接跟蹤功能,一個輔助模塊由一個結(jié)構(gòu)體ip_conntrack_helper保存,該結(jié)構(gòu)如下圖3-3所示,所有注冊的模塊由全局變量helpers所指向的鏈表保存。函數(shù)ip_conntrack_helper_register()和ip_conntrack_helper_unregister()用于在鏈表中添加和刪除ip_conntrack_helper類型的結(jié)構(gòu);顒拥FTP協(xié)議就使用了相應的helper模塊來實現(xiàn)。

3.4.期望連接
     在連接跟蹤機制中為了實現(xiàn)對活動協(xié)議的支持,還使用到了結(jié)構(gòu)體ip_conntrack_expect,其用于將預期連接分配給現(xiàn)有連接,有關于活動協(xié)議(如FTP)的分析在此不做分析。ip_conntrack_expect結(jié)構(gòu)如下圖3-4所示。所有的ip_conntrack_expect結(jié)構(gòu)由全局變量ip_conntrack_expect_list指向的全局鏈表保存。

3.5.傳輸協(xié)議
     連接跟蹤機制可以支持多種傳輸協(xié)議,不同的協(xié)議所采用的跟蹤方式會有所不同。傳輸協(xié)議用結(jié)構(gòu)ip_conntrack_protocol保存,所有的已注冊的傳輸協(xié)議列表由全局變量ip_ct_protos所指向的一維數(shù)組保存,且按照協(xié)議號的順序依次排列。函數(shù)ip_conntrack_protocol_register()ip_conntrack_protocol_unregister()用于向協(xié)議列表中添加或刪除一個協(xié)議。



4.重要函數(shù)

4.1.ip_conntrack_defrag()

     ip_conntrack_defrag()函數(shù)對分片的包進行重組,其調(diào)用ip_ct_gather_frag()收集已經(jīng)到達的分片包,然后再調(diào)用函數(shù)ip_defrag()實現(xiàn)數(shù)據(jù)分片包的重組。ip_conntrack_defrag()被掛載在鉤子點NF_IP_PRE_ROUTINGNF_IP_LOCAL_OUT,即從外面進來的數(shù)據(jù)包或本地主機生成的數(shù)據(jù)包會首先調(diào)用該函數(shù)。該函數(shù)只操作數(shù)據(jù)包的內(nèi)容,對連接跟蹤記錄沒有影響也沒有操作,如果不需要進行重組操作則直接返回NF_ACCEPT。函數(shù)的定義如下:

static unsigned int ip_conntrack_defrag(unsigned int hooknum,
                    struct sk_buff **pskb,
                    const struct net_device *in,
                    const struct net_device *out,
                    int (*okfn)(struct sk_buff *))
{
#if !defined(CONFIG_IP_NF_NAT) && !defined(CONFIG_IP_NF_NAT_MODULE)
    /* Previously seen (loopback)? Ignore. Do this before
     fragment check. */

    if ((*pskb)->nfct)
        return NF_ACCEPT;
#endif

    /* Gather fragments. */
    if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
        *pskb = ip_ct_gather_frags(*pskb,
                     hooknum == NF_IP_PRE_ROUTING ?
                     IP_DEFRAG_CONNTRACK_IN :
                     IP_DEFRAG_CONNTRACK_OUT);
        if (!*pskb)
            return NF_STOLEN;
    }
    return NF_ACCEPT;
}


4.2.  ip_conntrack_in()
   函數(shù)ip_conntrack_in()被掛載在鉤子點NF_IP_PRE_ROUTING,同時該函數(shù)也被掛載在鉤子點NF_IP_LOCAL_OUT的函數(shù)ip_conntrack_local()調(diào)用,連接跟蹤模塊在這兩個鉤子點掛載的函數(shù)對數(shù)據(jù)包的處理區(qū)別僅在于對分片包的重組方式有所不同。
函數(shù)ip_conntrack_in()首先調(diào)用__ip_conntrack_proto_find(),根據(jù)數(shù)據(jù)包的協(xié)議找到其應該使用的傳輸協(xié)議的連接跟蹤模塊,接下來調(diào)用協(xié)議模塊的error()對數(shù)據(jù)包進行正確性檢查,然后調(diào)用函數(shù)resolve_normal_ct()選擇正確的連接跟蹤記錄,如果沒有,則創(chuàng)建一個新紀錄。接著調(diào)用協(xié)議模塊的packet()函數(shù),如果返回失敗,則nf_conntrack_put()將釋放連接記錄。ip_conntrack_in()函數(shù)的源碼如下,函數(shù)resolve_normal_ct()實際操作了數(shù)據(jù)包和連接跟蹤表的內(nèi)容。
unsigned int ip_conntrack_in(unsigned int hooknum,
                 struct sk_buff **pskb,
                 const struct net_device *in,
                 const struct net_device *out,
                 int (*okfn)(struct sk_buff *))
{
    struct ip_conntrack *ct;
    enum ip_conntrack_info ctinfo;
    struct ip_conntrack_protocol *proto;
    int set_reply = 0;
    int ret;

    /* Previously seen (loopback or untracked)?  Ignore. */
    if ((*pskb)->nfct) {
        CONNTRACK_STAT_INC_ATOMIC(ignore);
        return NF_ACCEPT;
    }

    /* Never happen */
    if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
        if (net_ratelimit()) {
        printk(KERN_ERR "ip_conntrack_in: Frag of proto %u (hook=%u)/n",
               (*pskb)->nh.iph->protocol, hooknum);
        }
        return NF_DROP;
    }

    ......

    /* rcu_read_lock()ed by nf_hook_slow */
    proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol);
/* It may be an special packet, error, unclean...
     * inverse of the return code tells to the netfilter
     * core what to do with the packet. */

    if (proto->error != NULL
        && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) {
        CONNTRACK_STAT_INC_ATOMIC(error);
        CONNTRACK_STAT_INC_ATOMIC(invalid);
        return -ret;
    }

    if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) {
        /* Not valid part of a connection */
        CONNTRACK_STAT_INC_ATOMIC(invalid);
        return NF_ACCEPT;
    }

    if (IS_ERR(ct)) {
        /* Too stressed to deal. */
        CONNTRACK_STAT_INC_ATOMIC(drop);
        return NF_DROP;
    }

    IP_NF_ASSERT((*pskb)->nfct);

    ret = proto->packet(ct, *pskb, ctinfo);
    if (ret < 0) {
        /* Invalid: inverse of the return code tells
         * the netfilter core what to do*/

        nf_conntrack_put((*pskb)->nfct);
        (*pskb)->nfct = NULL;
        CONNTRACK_STAT_INC_ATOMIC(invalid);
        return -ret;
    }

    if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
        ip_conntrack_event_cache(IPCT_STATUS, *pskb);

    return ret;
}


     函數(shù)resolve_normal_ct()搜索與傳遞來的sk_buff結(jié)構(gòu)相匹配的連接記錄,其首先調(diào)用函數(shù)ip_ct_get_tuple(),利用包的協(xié)議模塊的pkt_to_tuple()函數(shù)創(chuàng)建一個ip_conntrack_tuple類型的結(jié)構(gòu),接下來調(diào)用函數(shù)ip_conntrack_find_get()在連接跟蹤表中查找匹配的記錄。如果沒有找到匹配的項,將調(diào)用函數(shù)init_conntrack()創(chuàng)建一個新的連接跟蹤記錄。最后確定數(shù)據(jù)包sk_buff結(jié)構(gòu)的狀態(tài)域的值,對其中的nfct和nfctinfo進行賦值。函數(shù)resolve_normal_ct()的源碼如下所示:

static inline struct nf_conn *
resolve_normal_ct(struct sk_buff *skb,
          unsigned int dataoff,
          u_int16_t l3num,
          u_int8_t protonum,
          struct nf_conntrack_l3proto *l3proto,
          struct nf_conntrack_l4proto *l4proto,
          int *set_reply,
          enum ip_conntrack_info *ctinfo)
{
   

    if (!nf_ct_get_tuple(skb, (unsigned int)(skb->nh.raw - skb->data),
                 dataoff, l3num, protonum, &tuple, l3proto,
                 l4proto)) {
        DEBUGP("resolve_normal_ct: Can't get tuple/n");
        return NULL;
    }

    /* look for tuple match */
    h = nf_conntrack_find_get(&tuple, NULL);
    if (!h) {
        h = init_conntrack(&tuple, l3proto, l4proto, skb, dataoff);
        if (!h)
            return NULL;
        if (IS_ERR(h))
            return (void *)h;
    }
    ct = nf_ct_tuplehash_to_ctrack(h);
   

   ......

*ctinfo = IP_CT_NEW;
        }
        *set_reply = 0;
    }
    skb->nfct = &ct->ct_general;
    skb->nfctinfo = *ctinfo;
    return ct;
}
   
            
4.3.  ip_conntrack_help()
    函數(shù)ip_conntrack_help()被掛載在鉤子點NF_IP_LOCAL_IN和NF_IP_POST_ROUTING,其首先根據(jù)傳來的sk_buff結(jié)構(gòu)查找連接跟蹤記錄,如果該包所屬連接有輔助模塊helper,且包符合一定的狀態(tài)要求,則調(diào)用相應輔助模塊的函數(shù)help()處理數(shù)據(jù)包。
static unsigned int ip_conntrack_help(unsigned int hooknum,
                      struct sk_buff **pskb,
                      const struct net_device *in,
                      const struct net_device *out,
                      int (*okfn)(struct sk_buff *))
{
    struct ip_conntrack *ct;
    enum ip_conntrack_info ctinfo;

    /* This is where we call the helper: as the packet goes out. */
    ct = ip_conntrack_get(*pskb, &ctinfo);
    if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) {
        unsigned int ret;
        ret = ct->helper->help(pskb, ct, ctinfo);
        if (ret != NF_ACCEPT)
            return ret;
    }
    return NF_ACCEPT;
}



4.4. ip_confirm()
   
函數(shù)ip_confirm()被掛載在鉤子點NF_IP_LOCAL_INNF_IP_POST_ROUTING,其對數(shù)據(jù)包再次進行連接跟蹤記錄確認,并將新建的連接跟蹤記錄加到表中?紤]到包可能被過濾掉,之前新建的連接跟蹤記錄實際上并未真正加到連接跟蹤表中,而在最后由函數(shù)ip_confirm()確認后真正添加,實際對傳來的sk_buff進行確認的函數(shù)是__ip_conntrack_confirm()。在該函數(shù)中首先調(diào)用函數(shù)ip_conntrack_get()查找相應的連接跟蹤記錄,如果數(shù)據(jù)包不是IP_CT_DIR_ORIGINAL方向的包,則直接ACCEPT,否則接著調(diào)用hash_conntrack()計算所找到的連接跟蹤記錄的ip_conntrack_tuple類型的hash值,且同時計算兩個方向的值。然后根據(jù)這兩個hash值分別查找連接跟蹤記錄的hash表,如果找到了,則返回NF_DROP,如果未找到,則調(diào)用函數(shù)__ip_conntrack_hash_insert()將兩個方向的連接跟蹤記錄加到hash表中。

static unsigned int ip_confirm(unsigned int hooknum,
             struct sk_buff **pskb,
             const struct net_device *in,
             const struct net_device *out,
             int (*okfn)(struct sk_buff *))
{
    /* We've seen it coming out the other side: confirm it */
    return ip_conntrack_confirm(pskb);
}




static inline int ip_conntrack_confirm(struct sk_buff **pskb)
{
    struct ip_conntrack *ct = (struct ip_conntrack *)(*pskb)->nfct;
    int ret = NF_ACCEPT;

    if (ct) {
        if (!is_confirmed(ct) && !is_dying(ct))
            ret = __ip_conntrack_confirm(pskb);
        ip_ct_deliver_cached_events(ct);
    }
    return ret;
}


int
__ip_conntrack_confirm(struct sk_buff **pskb)
{
   

    ct = ip_conntrack_get(*pskb, &ctinfo);

   
    if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
        return NF_ACCEPT;

    hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL.tuple);
    repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY.tuple);

        /* No external references means noone else could have
     confirmed us. */

    IP_NF_ASSERT(!is_confirmed(ct));
    DEBUGP("Confirming conntrack %p/n", ct);

    write_lock_bh(&ip_conntrack_lock);

    list_for_each_entry(h, &ip_conntrack_hash[hash, list)
        if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL.tuple,
                 &h->tuple))
            goto out;
    list_for_each_entry(h, &ip_conntrack_hash[repl_hash, list)
        if (ip_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY.tuple,
                 &h->tuple))
            goto out;

    /* Remove from unconfirmed list */
    list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL.list);

    __ip_conntrack_hash_insert(ct, hash, repl_hash);
   
    ct->timeout.expires += jiffies;
    add_timer(&ct->timeout);
    atomic_inc(&ct->ct_general.use);
    set_bit(IPS_CONFIRMED_BIT, &ct->status);
    CONNTRACK_STAT_INC(insert);
    write_unlock_bh(&ip_conntrack_lock);
    if (ct->helper)
        ip_conntrack_event_cache(IPCT_HELPER, *pskb);


......

out:
    CONNTRACK_STAT_INC(insert_failed);
    write_unlock_bh(&ip_conntrack_lock);
    return NF_DROP;
}

4.5.ip_conntrack_local()
     
函數(shù)ip_conntrack_local()被掛載在鉤子點NF_IP_LOCAL_OUT,該函數(shù)會調(diào)用ip_conntrack_in(),函數(shù)源碼如下:

static unsigned int ip_conntrack_local(unsigned int hooknum,
                 struct sk_buff **pskb,
                 const struct net_device *in,
                 const struct net_device *out,
                 int (*okfn)(struct sk_buff *))
{
    /* root is playing with raw sockets. */
    if ((*pskb)->len < sizeof(struct iphdr)
     || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
        if (net_ratelimit())
            printk("ipt_hook: happy cracking./n");
        return NF_ACCEPT;
    }
    return ip_conntrack_in(hooknum, pskb, in, out, okfn);
}


5.數(shù)據(jù)包轉(zhuǎn)發(fā)的連接跟蹤流程
    下面以數(shù)據(jù)包轉(zhuǎn)發(fā)為例描述連接跟蹤的流程,其中的函數(shù)及結(jié)構(gòu)體為前幾節(jié)所介紹的一部分,圖中主要想體現(xiàn)數(shù)據(jù)包sk_buff在連接跟蹤流程中的相應改變,連接跟蹤記錄與連接跟蹤表的關系,何時查找和修改連接跟蹤表,輔助模塊以及傳輸協(xié)議如何在連接跟蹤中使用等。所有的函數(shù)說明以及結(jié)構(gòu)體在之前都有描述。發(fā)往本機以及本機發(fā)出的數(shù)據(jù)包的連接跟蹤流程在此不再做分析。



6.總結(jié)
   以上只是簡要分析了Netfilter架構(gòu)中連接跟蹤功能的實現(xiàn)機制,其中很多細節(jié)被忽略,如數(shù)據(jù)包的狀態(tài),連接跟蹤記錄的狀態(tài),具體傳輸協(xié)議的連接跟蹤,主要目的是想要對整個實現(xiàn)框架有所認識。另外,對于較復雜的活動協(xié)議,期望連接與主連接之間的關聯(lián)等并未做分析,希望在以后有時間再做分析。鑒于自身水平有限,且可參考資料較少,因此以上的分析不能保證所描述的內(nèi)容完全正確。



PS:對于其中存在的錯誤問題,希望各位能指正,以便將此分析盡量完善,另外,對于我尚未分析完成的部分,也希望大家能夠提出意見,不甚感謝。






您需要登錄后才可以回帖 登錄 | 注冊

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

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP