亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

FreeBSD之netgraph簡(jiǎn)要解析

系統(tǒng) 2710 0
FreeBSD的netgraph真是太帥了,它到底是個(gè)什么玩藝呢?知道Linux的Netfilter的不少,那么就用Netfilter來(lái)類比吧。netgraph是一個(gè)基于圖的鉤子系統(tǒng),正如其名稱所展示的那樣,什么樣的圖呢?很簡(jiǎn)單,就是通過(guò)邊連接的節(jié)點(diǎn),和數(shù)據(jù)結(jié)構(gòu)里面學(xué)到的一樣。netgraph系統(tǒng)掛接在內(nèi)核協(xié)議棧的特定點(diǎn)上,哪些點(diǎn)呢?這個(gè)和Netfilter很類似,但是卻不是Netfilter精心設(shè)計(jì)的那5個(gè)點(diǎn),而是更簡(jiǎn)單的每一層處理的輸入點(diǎn)和輸出點(diǎn),如下圖所示:
FreeBSD之netgraph簡(jiǎn)要解析
netgraph到底長(zhǎng)什么樣子呢?到目前為止,我們只是知道了一張圖掛上去了,這僅僅是個(gè)接口,一個(gè)開(kāi)始,既然掛上去了,數(shù)據(jù)包就從此處進(jìn)入這張圖了,把它叫做地圖更加適合,因此從此以后,數(shù)據(jù)包就要在游歷于這張地圖了,最終的結(jié)果有兩個(gè):
1.數(shù)據(jù)包從地圖的某處出來(lái),重新進(jìn)入系統(tǒng)標(biāo)準(zhǔn)的協(xié)議棧的當(dāng)初被攔截的那個(gè)地方;
2.數(shù)據(jù)包再也沒(méi)有出來(lái)回到原點(diǎn),要么被地圖吃掉了(進(jìn)入了某一房間?),要么就是從某處出去,進(jìn)入?yún)f(xié)議棧的別的地方。

以上兩點(diǎn)很類似于Netfilter的ACCEPT,STOLEN這樣的結(jié)果,仔細(xì)想想不是么?netgraph和標(biāo)準(zhǔn)協(xié)議棧的銜接如下圖所示:
FreeBSD之netgraph簡(jiǎn)要解析
既然知道了netgraph的位置,那么下面就看看它的樣子吧。還是先給出一幅圖
FreeBSD之netgraph簡(jiǎn)要解析
該圖中有兩種元素,一種是節(jié)點(diǎn),另一種是連接到節(jié)點(diǎn)的邊的兩端的頂點(diǎn)。在netgraph的術(shù)語(yǔ)中,節(jié)點(diǎn)就是Node,而頂點(diǎn)叫做hook,一條邊連接兩個(gè)hook,hook通過(guò)CONNECT/MKPEER構(gòu)成一條邊。從上圖中可以看出,一條邊的兩端必然有兩個(gè)hook,從命名上可以看出這些“邊的端點(diǎn)”其實(shí)就是真正處理數(shù)據(jù)的地方,而Node其實(shí)就是一個(gè)“數(shù)據(jù)+操作”的封裝,一個(gè)Node可以有多個(gè)hook,通過(guò)這些hook連接到其它的Node。
我們可以用OO的思想來(lái)理解這些個(gè)netgraph的概念,Node就是一個(gè)對(duì)象,每一個(gè)Node都有它所屬的Type,可以將Type理解成類。而hook其實(shí)就是一個(gè)Node對(duì)象的私有數(shù)據(jù),整個(gè)graph通過(guò)“各個(gè)hook的對(duì)接”來(lái)完成,F(xiàn)reeBSD提供了豐富的命令來(lái)完成netgraph的構(gòu)建,說(shuō)白了其實(shí)就是以下幾步驟:
1.生成一系列的Node對(duì)象;
2.為每一個(gè)Node定義一個(gè)或多個(gè)hook;
3.將特定的Node通過(guò)hook連接在一起。

如此一來(lái)整個(gè)graph就構(gòu)建好了,F(xiàn)reeBSD提供了struct ng_type,它便是代表了一個(gè)類,然后你每生成一個(gè)特定ng_type的實(shí)例就相當(dāng)于生成了一個(gè)對(duì)象,通過(guò)對(duì)該結(jié)構(gòu)體里面的一些字段的理解,我們就可以完整理解數(shù)據(jù)包在這個(gè)graph中的游歷過(guò)成了。struct ng_type定義如下:
    struct ng_type {
    u_int32_t       version;        /* must equal NG_API_VERSION */
    const char      *name;          /* Unique type name */
    modeventhand_t  mod_event;      /* Module event handler (optional) */
    ng_constructor_t *constructor;  /* Node constructor */
    ng_rcvmsg_t     *rcvmsg;        /* control messages come here */
    ng_close_t      *close;         /* warn about forthcoming shutdown */
    ng_shutdown_t   *shutdown;      /* reset, and free resources */
    ng_newhook_t    *newhook;       /* first notification of new hook */
    ng_findhook_t   *findhook;      /* only if you have lots of hooks */
    ng_connect_t    *connect;       /* final notification of new hook */
    ng_rcvdata_t    *rcvdata;       /* data comes here */
    ng_disconnect_t *disconnect;    /* notify on disconnect */
    const struct    ng_cmdlist *cmdlist;    /* commands we can convert */
    LIST_ENTRY(ng_type) types;              /* linked list of all types */
    int                 refs;               /* number of instances */
};
  

注釋很清楚了,自不必說(shuō),如果我們看看其中一些回調(diào)函數(shù)的定義,就更能理解了。“構(gòu)造函數(shù)”和“析構(gòu)函數(shù)”都有,每一個(gè)“成員函數(shù)”的參數(shù)列表的第一個(gè)參數(shù)類型都是node_p,這難道不是this么?這里唯一要注意的就是rcvdata回調(diào)函數(shù),該函數(shù)接收從另一個(gè)Node發(fā)送過(guò)來(lái)的數(shù)據(jù),接收者是hook,而不是Node,再次強(qiáng)調(diào),Node之間通過(guò)hook相連接,而不是通過(guò)node本身,然而每一個(gè)hook都要唯一綁定一個(gè)Node對(duì)象,因此我們可以從hook解析出唯一的Node對(duì)象,卻不能從Node中直接得到hook(一個(gè)Node對(duì)象擁有N多hook呢),要分清一對(duì)一和一對(duì)多的關(guān)系。因此rcvdata的第一個(gè)參數(shù)是hook_p就是合理的了。
Node和Node之間通過(guò)hook傳遞控制信息,而網(wǎng)絡(luò)數(shù)據(jù)包則是通過(guò)一個(gè)hook向其peer hook發(fā)送消息的方式完成的,當(dāng)然所謂的發(fā)送消息大多數(shù)情況下就是函數(shù)直接調(diào)用。既然一條邊兩端有兩個(gè)hook,那么每一個(gè)hook就有一個(gè)peer,每當(dāng)我們將數(shù)據(jù)包發(fā)送到一個(gè)hook的時(shí)候,實(shí)際的效果就是數(shù)據(jù)包被發(fā)送到了該hook的peer,這是netgraph的核心邏輯實(shí)現(xiàn)的,我們可以從下面的這個(gè)核心宏中看到這一點(diǎn):
    #define NG_FWD_ITEM_HOOK_FLAGS(error, item, hook, flags)                \
    do {                                                            \
        (error) =                                               \
        ng_address_hook(NULL, (item), (hook), NG_NOFLAGS);  \
        if (error == 0) {                                       \
            SAVE_LINE(item);                                \
            (error) = ng_snd_item((item), (flags));         \
        }                                                       \
        (item) = NULL;                                          \
    } while (0)
  

其中ng_address_hook完成了peer的定位,這個(gè)peer可以通過(guò)ngctl命令來(lái)設(shè)置。
就這樣,一個(gè)數(shù)據(jù)包在整個(gè)netgraph中通過(guò)“離開(kāi)一個(gè)Node的某個(gè)hook,進(jìn)入另一個(gè)Node的某個(gè)hook的rcvdata”的方式游歷,Node在這里的作用就是封裝私有數(shù)據(jù)和統(tǒng)一的操作,當(dāng)然,你可以重載掉一個(gè)Node內(nèi)統(tǒng)一的rcvdata回調(diào)函數(shù),而是為每一個(gè)hook都設(shè)置一個(gè)私有的rcvdata回調(diào)函數(shù),再次強(qiáng)調(diào),是hook在rcvdata,而不是Node在rcvdata,Node的rcvdata是一個(gè)該Node所有hook通用的回調(diào)函數(shù),如果沒(méi)有hook私有的rcvdata,該通用函數(shù)將被調(diào)用,ng_snd_item最終將進(jìn)入下面的邏輯:
    if ((!(rcvdata = hook->hk_rcvdata)) &&
    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
    error = 0;
    NG_FREE_ITEM(item);
    break;
}
  

由此看出,Node有一個(gè)默認(rèn)的對(duì)所有hook都適用的rcvdata回調(diào)函數(shù),然而各個(gè)hook可以重載掉這個(gè)默認(rèn)的rcvdata回調(diào)函數(shù)。
接下來(lái)我們看一下netgraph如何和協(xié)議棧對(duì)接,不要把操作系統(tǒng)想得太神奇,實(shí)際上完成這種工作只需要一個(gè)回調(diào)函數(shù)即可。以以太網(wǎng)接收為例,以太網(wǎng)接收處理函數(shù)中會(huì)調(diào)用ng_ether_input_p回調(diào)函數(shù),你只需要將其定義一下即可,對(duì)于很多場(chǎng)合都使用的ng_ether,它將此函數(shù)定義為:
    static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp)
{
    const node_p node = IFP2NG(ifp);
    const priv_p priv = NG_NODE_PRIVATE(node);
    int error;
    /* If "lower" hook not connected, let packet continue */
    if (priv->lower == NULL)
        return;
    NG_SEND_DATA_ONLY(error, priv->lower, *mp);     /* sets *mp = NULL */
}
  

最后通過(guò)NG_SEND_DATA_ONLY將數(shù)據(jù)包發(fā)送給priv->lower這個(gè)hook,最終數(shù)據(jù)包會(huì)進(jìn)入priv->lower的peer,調(diào)用priv->lower->peer的rcvdata回調(diào)函數(shù),在一切開(kāi)始工作之前,你首先需要構(gòu)建好整個(gè)graph。對(duì)于以太網(wǎng)發(fā)送函數(shù),也有類似的_p回調(diào)函數(shù)。
netgraph和Netfilter的區(qū)別在于它可以將graph“掛接”在特定的interface上,而Netfilter卻把HOOK直接掛在協(xié)議棧本身,interface在Netfilter中只是一個(gè)match。如此一比較,效率差異就很明顯了。以以太網(wǎng)為例,在ether_input中就會(huì)調(diào)用netgraph,如果加載了ng_ether的話,就會(huì)調(diào)用下面的函數(shù):
    static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp)
{
    const node_p node = IFP2NG(ifp);
    const priv_p priv = NG_NODE_PRIVATE(node);
    int error;
    /* If "lower" hook not connected, let packet continue */
    if (priv->lower == NULL) //如果這塊網(wǎng)卡上沒(méi)有任何hook,將不作處理直接返回。
        return;
    NG_SEND_DATA_ONLY(error, priv->lower, *mp);     /* sets *mp = NULL */
}
  

如果本ifp上沒(méi)有掛接任何graph,則直接返回標(biāo)準(zhǔn)協(xié)議棧處理,如果掛接了一個(gè)graph,則數(shù)據(jù)包將進(jìn)入該graph,你可以將firewall rule配置在此graph里面。對(duì)于Netfilter而言,在網(wǎng)卡接收這一層,沒(méi)有任何HOOK,只有到了IP層,才會(huì)進(jìn)入PREROUTING/INPUT/FORWARD...等HOOK,哪怕你配置了一條rule,所有的包都將接受檢查以確定是否匹配,在Netfilter的rule中,所謂的interface只是一個(gè)match。
需要說(shuō)明的是,netgraph也可以像Netfilter那樣工作,你只需要將其掛在ip_in(out)put上即可。
我們給出兩個(gè)例子來(lái)看看netgraph如何實(shí)現(xiàn)bridge和bonding,這些在Linux上都是通過(guò)虛擬net_device來(lái)實(shí)現(xiàn)的,其發(fā)送邏輯都是該虛擬net_device的hard_xmit實(shí)現(xiàn)的,而其數(shù)據(jù)接收邏輯則是硬編碼在netif_receive_skb中的,bridge是通過(guò)handle_bridge這個(gè)硬編碼hook進(jìn)入的,而bonding是通過(guò)skb_bond來(lái)實(shí)現(xiàn)的。也就是說(shuō)Linux是通過(guò)對(duì)既有的協(xié)議棧進(jìn)行硬修改來(lái)實(shí)現(xiàn)的,而netgraph則不需要這樣,對(duì)于FreeBSD,我們只需要構(gòu)建一張graph就可以實(shí)現(xiàn)bridge或者bonding,首先我們先看看bridge的實(shí)現(xiàn)邏輯,如下圖所示:
FreeBSD之netgraph簡(jiǎn)要解析
我個(gè)人以為圖示已經(jīng)很清晰了。需要注意的是,netgraph將本地的網(wǎng)卡作為了局域網(wǎng)上一張普通的網(wǎng)卡來(lái)看待,并沒(méi)有刻意區(qū)分流量是本機(jī)發(fā)出的還是從其它機(jī)器發(fā)出的,因此,如果你只是想將bridge作為一個(gè)二層設(shè)備,那么可以斷開(kāi)Hook-ethX-low和Hook-ethX-upper之間的邊即可,netgraph實(shí)現(xiàn)的bridge,你看不到虛擬設(shè)備,這種實(shí)現(xiàn)更純粹,偉大的BSD將這種思想帶給了其衍生出來(lái)的Cisco IOS。
下面是bonding的實(shí)現(xiàn)邏輯:
FreeBSD之netgraph簡(jiǎn)要解析
由于bonding網(wǎng)卡大多數(shù)負(fù)責(zé)的是本地IP層發(fā)出的數(shù)據(jù),需要和路由轉(zhuǎn)發(fā)表相配合,因此需要有一塊虛擬網(wǎng)卡,這個(gè)是通過(guò)ng_eiface的構(gòu)造函數(shù)ng_eiface_constructor實(shí)現(xiàn)的。依然無(wú)其它話可說(shuō)。
以上兩個(gè)圖展示了netgraph的魅力,既然這樣,也就可以依照這種方式實(shí)現(xiàn)VLAN,IPSec等了,要比Linux的Netfilter加設(shè)備驅(qū)動(dòng)模型的實(shí)現(xiàn)方式更“可插拔”,有netgraph,F(xiàn)reeBSD可以將所有的協(xié)議處理在一張張的graph中進(jìn)行,數(shù)據(jù)包在graph中游歷在每一個(gè)Node被接收到的hook處理,主要你能根據(jù)協(xié)議處理邏輯構(gòu)建好一張圖,將這張圖掛接在協(xié)議棧,甚至掛接在驅(qū)動(dòng)上,你就能很方便的實(shí)現(xiàn)網(wǎng)絡(luò)的任意擴(kuò)展...
最后看一下netgraph的依賴關(guān)系,在netgraph中,每張圖都是相對(duì)獨(dú)立的,數(shù)據(jù)包從某處進(jìn)入一張圖A,然后從某處出來(lái),在另一處再進(jìn)入圖B,此時(shí)它將不能再使用圖A。這和Netfilter不同,Netfilter基于HOOK設(shè)計(jì),使用一些match來(lái)進(jìn)行filter,比如NAT就需要ip_conntrack,ctdir需要ip_conntrack等等,ip_conntrack一直都面臨table full的問(wèn)題,因此你要用raw表的NOTRACK這個(gè)target來(lái)免除追蹤不感興趣流量來(lái)緩解這個(gè)問(wèn)題。有下面的需求:
從網(wǎng)段M發(fā)出到網(wǎng)段N的流量(兩個(gè)方向)打上tag待策略路由來(lái)處理,從網(wǎng)段N發(fā)出到網(wǎng)段M的流量(兩個(gè)方向)不打tag。
分析:
很顯然要使用ctdir這個(gè)match,否則將會(huì)過(guò)濾掉返回流量,于是有以下target為NOTRACK的match:
!dst N/interface $內(nèi)網(wǎng)口
然而意味著從網(wǎng)段N發(fā)出到達(dá)M的返回流量也將被conntrack,這是因?yàn)閏tdir和conntrack相互依賴才導(dǎo)致了這樣的問(wèn)題,在raw表中,你甚至都不知道數(shù)據(jù)包到底是走INPUT還是FORWARD,所以你很難讓所有這一切關(guān)聯(lián)起來(lái),雖然conntrack可以保持一個(gè)流信息在內(nèi)存中,但是卻可能存在大量不相關(guān)的流也被保存。如果使用netgraph呢?很簡(jiǎn)單,我們可以寫在兩個(gè)命令中:
No.x check-status
No.z skip No.y from N to M #對(duì)于返回流量,只檢測(cè)conntrack
No.y netgraph tag from M to N keep-status

如此即可。FreeBSD不需要conntrack,它內(nèi)建了一個(gè)動(dòng)態(tài)ruleset,凡是keep-status的流量都將自動(dòng)將返回流量加入動(dòng)態(tài)ruleset中,實(shí)際上也就是“保持了一個(gè)流信息在內(nèi)存中”,F(xiàn)reeBSD的conntrack和單獨(dú)的rule相關(guān)聯(lián)而不是和整個(gè)協(xié)議棧關(guān)聯(lián),這實(shí)際上也是netgraph的思想,我們看一下rule相關(guān)的conntrack和協(xié)議棧香瓜的conntrack的區(qū)別:
IPFW:沒(méi)有全局的conntrack信息,然而需要查詢動(dòng)態(tài)ruleset,以匹配返回流量;
Netfilter:需要查詢?nèi)值腸onntrack表,可以取出一切頭包經(jīng)過(guò)時(shí)流量的匹配結(jié)果,不需要也沒(méi)有動(dòng)態(tài)ruleset

我們看一下全局的conntrack和全局的ruleset所針對(duì)的對(duì)象有何不同。很簡(jiǎn)單,全局的conntrack針對(duì)除了NOTRACK的所有的數(shù)據(jù)包,然而如果NOTRACK需要指明方向,就會(huì)需要循環(huán)依賴,問(wèn)題將無(wú)解。全局的動(dòng)態(tài)ruleset僅僅針對(duì)匹配到的數(shù)據(jù)包,對(duì)其它的沒(méi)有匹配到的數(shù)據(jù)包除了一個(gè)查詢性能影響之外沒(méi)有其他影響,事到如今,我想查詢性能應(yīng)該不是問(wèn)題吧,再說(shuō)動(dòng)態(tài)ruleset一般都比全局conntrackset小得多,查詢conntrackset都不怕,查詢動(dòng)態(tài)ruleset就怕了么?換句話說(shuō),Netfilter的ip_conntrack是寧可枉殺一千,不能使一人漏網(wǎng),而ipfw則是精確的匹配。效率啊,BSD不愧是網(wǎng)絡(luò)領(lǐng)頭軍!

FreeBSD之netgraph簡(jiǎn)要解析


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日韩精品一区二区三区中文精品 | 免费一级毛片不卡在线播放 | 亚洲精品成人a在线观看 | 国产免费一级精品视频 | 毛片免费看 | 99国产高清久久久久久网站 | 亚洲美女在线视频 | 在线欧美激情 | 欧美一级毛片免费播放aa | 亚洲国产精品久久久久婷婷老年 | 成人高清毛片a | 伊人久久香蕉 | 天天射天天干天天插 | 99久久精品免费看国产情侣 | 久久国产三级精品 | 四虎永久免费地址在线网站 | 精品国产网| 五月婷婷亚洲综合 | 麻豆精品久久精品色综合 | 人人狠狠综合久久亚洲婷婷 | 亚洲欧美天堂网 | 夜间福利影院 | 亚洲精品视 | 综合欧美日韩一区二区三区 | 精品久久久中文字幕二区 | 高清欧美一区二区免费影视 | 国产精品福利视频主播真会玩 | 免费在线精品视频 | 目韩一区二区三区系列片丶 | 四影虎库最新2021 | 中中文字幕亚州无线码 | 欧美日本在线播放 | 一级a级毛片 | 国产真实乱子伦精品视 | 日本a视频在线 | 欧美三级做爰在线 | 日本激情啪啪 | 亚洲欧美中文日韩综合 | 神马午夜剧场 | 亚洲伊人久久大香线蕉啊 | 九九影院韩国理伦片 |