Open vSwitch從接收數(shù)據(jù)包開始分析

一、工作流程(datapath數(shù)據(jù)路徑)

ovs數(shù)據(jù)包處理流程圖.png
  • 一般的數(shù)據(jù)包在linux網(wǎng)絡(luò)協(xié)議棧中的流向為黑色箭頭流向:從網(wǎng)卡eth0上接收到數(shù)據(jù)包后層層往上分析,最后離開內(nèi)核態(tài),把數(shù)據(jù)傳送到用戶態(tài)。當(dāng)然也有些數(shù)據(jù)包只是在內(nèi)核網(wǎng)絡(luò)協(xié)議棧中操作,然后再從某個網(wǎng)卡發(fā)出去。
  • 【以下代碼分析基于Open vSwitch 2.5.0版本】
  • 但當(dāng)其中有Open Vswitch時,數(shù)據(jù)包的流向就不一樣了。首先是創(chuàng)建一個網(wǎng)橋:ovs-vsctl add-br br0——底層實現(xiàn)原理是調(diào)用datapath/datapath.c中的ovs_dp_cmd_new函數(shù)new_vport函數(shù)去初始化一個datapath對象和vport對象。
    然后是綁定某個網(wǎng)卡:綁定網(wǎng)卡:ovs-vsctl add-port br0 eth0;這里綁定了eth0網(wǎng)卡。
  • linux內(nèi)核通過netif_receive_skb()函數(shù)從網(wǎng)卡eth0收到的一個數(shù)據(jù)包struct sk_buff *skb的流向是從網(wǎng)卡eth0上轉(zhuǎn)到Open Vswitch的端口vport上進入Open Vswitch中,首先是調(diào)用的datapath/vport-netdev.c中的struct sk_buff *netdev_frame_hook(struct sk_buff *skb)函數(shù)并再調(diào)用void netdev_port_receive(struct sk_buff *skb, struct ip_tunnel_info *tun_info)函數(shù),對數(shù)據(jù)包做一些檢查,
  • 然后調(diào)用datapath/vport.c中的int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, const struct ip_tunnel_info *tun_info)函數(shù),調(diào)用ovs_flow_extract函數(shù)基于skb生成key值,并檢查是否有錯。
  • 然后轉(zhuǎn)到調(diào)用datapath/datapath.c中的void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)函數(shù)來處理數(shù)據(jù)包是根據(jù)key值調(diào)用ovs_flow_tbl_lookup_stats函數(shù)進行流表的匹配。如果匹配成功,則根據(jù)流表中對應(yīng)的action找到其對應(yīng)的操作方法,調(diào)用ovs_execute_actions執(zhí)行對應(yīng)的action完成相應(yīng)的動作(這個動作有可能是把數(shù)據(jù)包從其他端口發(fā)出去,也有可能是直接丟棄,也可以自己設(shè)計action);如果匹配不成功,則調(diào)用ovs_dp_upcall上傳至用戶空間的vswitchd守護進程進行處理,該進程又和sdn控制器直接進行通信。
    數(shù)據(jù)包匹配流表走向

*【具體函數(shù)定義如下】

void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
{
    const struct vport *p = OVS_CB(skb)->input_vport;
    struct datapath *dp = p->dp;
    struct sw_flow *flow;
    struct sw_flow_actions *sf_acts;
    struct dp_stats_percpu *stats;
    u64 *stats_counter;
    u32 n_mask_hit;

    stats = this_cpu_ptr(dp->stats_percpu);

    /* Look up flow. */
    flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb),
                     &n_mask_hit);  //查找匹配的流表
    if (unlikely(!flow)) {          //匹配不成功
        struct dp_upcall_info upcall;
        int error;

        memset(&upcall, 0, sizeof(upcall));
        upcall.cmd = OVS_PACKET_CMD_MISS;
        upcall.portid = ovs_vport_find_upcall_portid(p, skb);
        upcall.mru = OVS_CB(skb)->mru;
        error = ovs_dp_upcall(dp, skb, key, &upcall);  //轉(zhuǎn)到上層用戶空間處理
        if (unlikely(error))
            kfree_skb(skb);
        else
            consume_skb(skb);
        stats_counter = &stats->n_missed;
        goto out;
    }
    //匹配成功
    ovs_flow_stats_update(flow, key->tp.flags, skb);
    sf_acts = rcu_dereference(flow->sf_acts);    //獲取流表動作集
    ovs_execute_actions(dp, skb, sf_acts, key);  //執(zhí)行流表的動作

    stats_counter = &stats->n_hit;

out:
    /* Update datapath statistics. */
    u64_stats_update_begin(&stats->syncp);
    (*stats_counter)++;
    stats->n_mask_hit += n_mask_hit;
    u64_stats_update_end(&stats->syncp);
}

二、數(shù)據(jù)包struct sk_buff *skb的內(nèi)容

  • skb數(shù)據(jù)包是由linux內(nèi)核從網(wǎng)卡接收,該結(jié)構(gòu)體也由linux內(nèi)核定義,結(jié)構(gòu)體中包含的信息很豐富,若是在上一節(jié)列出的某個處理接收數(shù)據(jù)包的函數(shù)中對數(shù)據(jù)包進行解析統(tǒng)計一條流(五元組匹配確定)的信息,可用于構(gòu)建用于分類識別的流量數(shù)據(jù)集。
struct sk_buff {
    /* These two members must be first. */
    struct sk_buff      *next;  //因為sk_buff結(jié)構(gòu)體是雙向鏈表,所以有前驅(qū)后繼。
    struct sk_buff      *prev;  //這是指向前一個sk_buff結(jié)構(gòu)體指針
 //2.6版本以前應(yīng)該還有個字段:sk_buff_head *list 即每個sk_buff結(jié)構(gòu)都有個指針指向頭節(jié)點
    struct sock         *sk;  // 指向擁有此緩沖的套接字sock結(jié)構(gòu)體
    ktime_t         tstamp;  // 時間戳,表示這個skb的接收到的時間,
                            //一般是在包從驅(qū)動中往二層發(fā)送的接口函數(shù)中設(shè)置
    struct net_device   *dev;  // 表示一個網(wǎng)絡(luò)設(shè)備
    unsigned long   _skb_dst;  // 主要用于路由子系統(tǒng),保存路由有關(guān)的東西
    char            cb[48];  // 保存每層的控制信息,每一層的私有信息
    unsigned int    len,  // 表示數(shù)據(jù)區(qū)的長度(tail - data)與分片結(jié)構(gòu)體數(shù)據(jù)區(qū)的長度之和。
                              //其實這個len中數(shù)據(jù)區(qū)長度是個有效長度,
      // 因為不刪除協(xié)議頭,所以只計算有效協(xié)議頭和包內(nèi)容。如:當(dāng)在L3時,不會計算L2的協(xié)議頭長度。
            data_len;  // 只表示分片結(jié)構(gòu)體數(shù)據(jù)區(qū)的長度,所以len = (tail - data) + data_len;
    __u16           mac_len,  // mac報頭的長度
                hdr_len;  // 用于clone時,表示clone的skb的頭長度
    // 接下來是校驗相關(guān)域
    __u32           priority;  // 優(yōu)先級,主要用于QOS
    kmemcheck_bitfield_begin(flags1);
    __u8            local_df:1,  // 是否可以本地切片的標(biāo)志
                cloned:1,  // 為1表示該結(jié)構(gòu)被克隆,或者自己是個克隆的結(jié)構(gòu)體;
                          //同理被克隆時,自身skb和克隆skb的cloned都要置1
                ip_summed:2, 
                nohdr:1,  // nohdr標(biāo)識payload是否被單獨引用,不存在協(xié)議首部。
// 如果被引用,則決不能再修改協(xié)議首部,也不能通過skb->data來訪問協(xié)議首部。
                nfctinfo:3;
    __u8            pkt_type:3,  // 標(biāo)記幀的類型
                fclone:2,   // 這個成員字段是克隆時使用,表示克隆狀態(tài)
                ipvs_property:1,
                peeked:1,
                nf_trace:1;
    __be16          protocol:16;  // 這是包的協(xié)議類型,標(biāo)識是IP包還是ARP包或者其他數(shù)據(jù)包。
    kmemcheck_bitfield_end(flags1);
    void    (*destructor)(struct sk_buff *skb);  //這是析構(gòu)函數(shù),后期在skb內(nèi)存銷毀時會用到
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
    struct nf_conntrack *nfct;
    struct sk_buff      *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
    struct nf_bridge_info   *nf_bridge;
#endif
    int         iif;  // 接受設(shè)備的index
#ifdef CONFIG_NET_SCHED
    __u16           tc_index;   /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
    __u16           tc_verd;    /* traffic control verdict */
#endif
#endif
    kmemcheck_bitfield_begin(flags2);
    __u16           queue_mapping:16;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
    __u8            ndisc_nodetype:2;
#endif
    kmemcheck_bitfield_end(flags2);
    /* 0/14 bit hole */
#ifdef CONFIG_NET_DMA
    dma_cookie_t        dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
    __u32           secmark;
#endif
    __u32           mark;
    __u16           vlan_tci;
    sk_buff_data_t      transport_header;    // 指向四層幀頭結(jié)構(gòu)體指針
    sk_buff_data_t      network_header;      // 指向三層IP頭結(jié)構(gòu)體指針
    sk_buff_data_t      mac_header;        // 指向二層mac頭的頭
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t      tail;   // 指向數(shù)據(jù)區(qū)中實際數(shù)據(jù)結(jié)束的位置
    sk_buff_data_t      end;    // 指向數(shù)據(jù)區(qū)中結(jié)束的位置(非實際數(shù)據(jù)區(qū)域結(jié)束位置)
    unsigned char       *head,  // 指向數(shù)據(jù)區(qū)中開始的位置(非實際數(shù)據(jù)區(qū)域開始位置)
                *data;            // 指向數(shù)據(jù)區(qū)中實際數(shù)據(jù)開始的位置            
    unsigned int truesize; //表示總長度包括sk_buff自身長度和數(shù)據(jù)區(qū)以及分片結(jié)構(gòu)體的數(shù)據(jù)區(qū)長度
    atomic_t        users;    // skb被克隆引用的次數(shù),在內(nèi)存申請和克隆時會用到
};   //end sk_buff
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • OVS 核心代碼 OVS 架構(gòu) OVS 主要的數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)關(guān)系圖主要的數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)結(jié)構(gòu)的參數(shù)數(shù)據(jù)結(jié)構(gòu)代碼 d...
    NightCat閱讀 28,142評論 3 24
  • Open vSwitch介紹 在過去,數(shù)據(jù)中心的服務(wù)器是直接連在硬件交換機上,后來VMware實現(xiàn)了服務(wù)器虛擬化技...
    殺破魂閱讀 25,156評論 1 18
  • 裝載自http://sdnhub.cn/index.php/openv-switch-full-guide/ 1 ...
    ximitc閱讀 6,329評論 0 11
  • 前言 Weave 作為 Docker 跨主機集群網(wǎng)絡(luò)解決方案的一種,可以用于連接部署在多臺主機上的 Docker ...
    UCloud云計算閱讀 1,417評論 0 0
  • 夢想, 就如心愛的紫藤花, 一串串墜滿藤蘿, 逆著驕陽仰視, 伸展臂膀, 夠不著, 奮力蹦躍, 似乎抓得住, 那明...
    麻小寶007閱讀 223評論 0 0

友情鏈接更多精彩內(nèi)容