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;
}