編寫一個Linux虛擬網卡來實作類別NVI

來源:互聯網
上載者:User

我們可以在Linux上使用loopback介面來類比兩個階段的路由抉擇,第一個階段是走一遍PRE/POST ROUTING流程,將NAT實施完畢,第二階段完成單純路由轉寄。然而需要在Netfilter上掛鈎子,以便取消關聯在skb上的路由項,並且取消關聯在skb上的conntrack資訊,因為在第二階段的單純路由流程裡面,我不希望再有什麼基於conntrack的動作,因此如果需要有基於conntrack的操作,務必在第一階段內和NAT一併完成。
        回過頭來看loopback的實現,不是那麼完美,因為像在Netfilter上掛載鉤子完成的這種事完全可以在虛擬網卡的xmit操作中完成,因此有必要重新寫一個虛擬網卡,之所以最終還是考慮重新寫,是因為這個模組超級簡單,基本可以照搬loopback.c的實現,所不同的是xmit的操作:

static netdev_tx_t nvi_xmit(struct sk_buff *skb,                                 struct net_device *dev){    int len;    //注意,我把原始的資料包入介面寫在了skb的mark中了,為何能這麼做呢?因為...    struct net_device * real_dev = dev_get_by_index(dev_net(dev), skb->mark);    skb_orphan(skb);    skb->protocol = eth_type_trans(skb, real_dev);    //取消關聯的路由項,以便可以在ip_input的時候重新policy routing    skb_dst_drop(skb);    //取消conntrack,因為它的任務在第一階段已經完成了    skb->nfct = &nf_conntrack_untracked.ct_general;    skb->nfctinfo = IP_CT_NEW;    nf_conntrack_get(skb->nfct);        len = skb->len;        if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {        ...//做點什麼好呢?統計?    } else {        ...//...    }    return NETDEV_TX_OK;}

對NVI介面的註冊也非常簡單:
dev = alloc_netdev(0, "nvi", nvi_setup);
為何可以使用skb的mark來儲存入介面index呢?實際上在32位的機器上它完全可以儲存原始入網卡dev的地址,強轉成net_device類型指標即可。我並不是一開始就直接把入網卡的index儲存在mark中了,因為可能Netfilter鉤子還要用這個mark,我也沒有使用mark的掩碼掩去一些位來儲存index,因為不知情者可能會誤用。我採用的方式是在“確認不可能有Netfilter鉤子使用mark的時候再將其覆蓋成入網卡的index,那麼何時合適呢?精通Netfilter的都知道,在POSTROUTING的最後做這件事比較合適,因此我就把這個HOOK安置於POSTROUTING的nf_confirm之後。是否會有流控用到mark我不管,畢竟流控是在物理網卡上做的,和第一輪的路由無關。然而問題是,到了POSTROUTING的時候我還能取到原始的入網卡的index嗎?Oh,NO!:
int ip_output(struct sk_buff *skb){    struct net_device *dev = skb_dst(skb)->dev;    IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);    //在此處,POSTROUTING前替換了skb的dev...    skb->dev = dev;    skb->protocol = htons(ETH_P_IP);    return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,                ip_finish_output,                !(IPCB(skb)->flags & IPSKB_REROUTED));}
所以,到了POSTROUTING,就再也取不到原始網卡的index了!那麼變通的辦法就是將Linux協議棧的這段代碼改了:
int ip_output(struct sk_buff *skb){    struct net_device *orig_dev    struct net_device *dev = skb_dst(skb)->dev;    IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);    //儲存原始入網卡的dev(反正都是forward包...)    orig_dev = skb->dev    skb->dev = dev;    skb->protocol = htons(ETH_P_IP);    //在HOOK調用的時候傳入indev    if (orig_dev && orig_dev->flags & IFF_LOOPBACK) {        orig_dev = NULL;    }    return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, orig_dev, dev,                ip_finish_output,                !(IPCB(skb)->flags & IPSKB_REROUTED));}
我個人覺得,對於將Linux作為路由BOX來用的人來講,對於FORWARDING的資料包,在POSTROUTING的時候能取到資料包從哪個網卡進入,可以實施更多的控制策略,這難道不更好嗎?好吧,如果非要說這樣改不好,我還有更加標準的做法,那就是在conntrack結構體中註冊一個新的extend,其實就是一個結構體,將原始的入網卡作為一個欄位放進去,在NVI介面的xmit中,conntrack重設為nf_conntrack_untracked之前,取出這個網卡,調用eth_type_trans介面即可,這樣好了吧,我沒有觸動Linux kernel的主協議棧,還是基於Netfilter來做擴充!事實上,Netfilter的擴充能力是無限的!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.