open vswitch研究: action

ovs的action,都是預定義好的行爲,也可以用nlattr結構來定義,

enum ovs_action_attr {
    OVS_ACTION_ATTR_UNSPEC,
    OVS_ACTION_ATTR_OUTPUT,       /* u32 port number. */
    OVS_ACTION_ATTR_USERSPACE,    /* Nested OVS_USERSPACE_ATTR_*. */
    OVS_ACTION_ATTR_SET,          /* One nested OVS_KEY_ATTR_*. */
    OVS_ACTION_ATTR_PUSH_VLAN,    /* struct ovs_action_push_vlan. */
    OVS_ACTION_ATTR_POP_VLAN,     /* No argument. */
    OVS_ACTION_ATTR_SAMPLE,       /* Nested OVS_SAMPLE_ATTR_*. */
    __OVS_ACTION_ATTR_MAX
};

enum ovs_action_attr裏的類型,是nlattr的nl_type,代表了不同的action類型。do_execute_actions對nlattr數組的每一個attribute,先判斷nl_type指定的action類型,之後根據action做不同的事情

        switch (nla_type(a)) {
        case OVS_ACTION_ATTR_OUTPUT:
            prev_port = nla_get_u32(a);
            break;
對於OVS_ACTION_ATTR_OUTPUT,從nlattr的attribute中取得port號,下一輪循環由於prev_port > 0,調用do_output把包發出去

    
        case OVS_ACTION_ATTR_USERSPACE:
            output_userspace(dp, skb, a);
            break;
對於OVS_ACTION_ATTR_USERSPACE,調用output_userspace通過netlink把包發到用戶態


        case OVS_ACTION_ATTR_PUSH_VLAN:
            err = push_vlan(skb, nla_data(a));
            if (unlikely(err)) /* skb already freed. */
                return err;
            break;      
對於OVS_ACTION_ATTR_PUSH_VLAN,nla_data(a)是一個ovs_action_push_vlan的結構,這裏把vlan_tci加入到skb中,重新計算校驗和。

        case OVS_ACTION_ATTR_POP_VLAN:
            err = pop_vlan(skb);
            break;

對於OVS_ACTION_ATTR_POP_VLAN,調用pop_vlan(skb),首先清除skb->vlan_tci,之後調用__pop_vlan_tci,把8021q的4字節挪掉,重新計算校驗和

        case OVS_ACTION_ATTR_SET:
            err = execute_set_action(skb, nla_data(a));
            break;

對於OVS_ACTION_ATTR_SET,execute_set_action根據nlattr->nla_type設置skb的成員項的值爲nlattr的payload

        case OVS_ACTION_ATTR_SAMPLE:
            err = sample(dp, skb, a);
            break;
        }


------------------------------------------ 華麗的分割線 --------------------------------------------


在OVS裏設置vlan和linux bridge還是不同的,linux bridge沒有vlan的功能,需要外接一個vlan virtual NIC(可以通過vconfig add來創建)。而OVS可以在添加port的時候指定vlan tag, e.g.

#ovs-vsctl add-port xenbr0 vif.12345 tag 1024

因此在OVS的cam表裏,我們可以看到不同的vlan信息,e.g.

 port  VLAN  MAC                          Age
    1     5         00:24:97:2e:ef:00   294
    1     4         00:24:97:2e:ef:00   294
    1     3         00:50:56:ba:3c:c6  292
    1    14       00:16:3e:05:3c:2f    287
    1    14       00:16:3e:55:ed:99  281
    1    14       00:16:3e:6e:c2:7c   276
    1     3        00:50:56:ba:74:e4   270

datapath/vlan.h datapath/vlan.c下的代碼比較直觀,這裏簡單介紹下就結束了

vlan_set_tci, vlan_get_tci,都是對OVS_CB(skb)->vlan_tci的讀寫操作

vlan_tx_tag_present,判斷OVS_CB(skb)->vlan_tci的bit 13-16是否爲0

vlan_tx_tag_get,得到OVS_CB(skb)->vlan_tci的bit 13-16

__vlan_hwaccel_put_tag,賦值OVS_CB(skb)->vlan_tci爲vlan_tci | VLAN_TAG_PRESENT,等於保留了1-12bit的vlan ID,而把13-16bit全部設爲1

vlan_deaccel_tag,調用__vlan_put_tag,增加一個8021q頭


static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci)
{
    struct vlan_ethhdr *veth;

    if (skb_cow_head(skb, VLAN_HLEN) < 0) {
        kfree_skb(skb);
        return NULL;
    } 

確保skb_headroom還能多容納一個4字節的8021Q頭

  
    veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN);
skb->data指針往前挪4個字節,此時認爲skb->data指向一個vlan_ethhdr結構

   
    /* Move the mac addresses to the beginning of the new header. */
    memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);

把12個字節的非8021Q以太頭往前挪4個字節的位置


    skb->mac_header -= VLAN_HLEN;
        
    /* first, the ethernet type */
    veth->h_vlan_proto = htons(ETH_P_8021Q);
   
    /* now, the TCI */
    veth->h_vlan_TCI = htons(vlan_tci);
填充8021Q的頭部

    skb->protocol = htons(ETH_P_8021Q);

    return skb;
}


  




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章