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


  




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值