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全部設爲1vlan_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;
}