datapath: VLAN actions should use push/pop semantics
authorPravin Shelar <pshelar@nicira.com>
Sat, 10 Sep 2011 01:13:26 +0000 (18:13 -0700)
committerPravin Shelar <pshelar@nicira.com>
Sat, 10 Sep 2011 01:13:26 +0000 (18:13 -0700)
Currently the kernel vlan actions mirror those used by OpenFlow 1.0.
i.e. MODIFY and STRIP. More flexible approach is to have an action to
push a tag and pop a tag off, so that it can handle multiple levels of vlan
tags. Plus it aligns with newer version of OpenFlow.
        As this patch replaces MODIFY with PUSH semantic, action
mapping done in userpace is fixed accordingly.
        GSO handling for multiple levels of vlan tags is also added as
Jesse suggested before.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
14 files changed:
datapath/actions.c
datapath/datapath.c
datapath/linux/compat/include/linux/if_ether.h
datapath/linux/compat/include/linux/netdevice.h
datapath/linux/compat/netdevice.c
datapath/vport-netdev.c
include/openvswitch/datapath-protocol.h
lib/bond.c
lib/dpif-netdev.c
lib/odp-util.c
lib/packets.c
lib/packets.h
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif.c

index 8aec438baa17d1eb1a8fe5722e09fe716e1607c8..83b531adfff3143fc29e59f2515527aea804c732 100644 (file)
@@ -39,20 +39,13 @@ static int make_writable(struct sk_buff *skb, int write_len)
        return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
 }
 
-static int strip_vlan(struct sk_buff *skb)
+/* remove VLAN header from packet and update csum accrodingly. */
+static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
 {
        struct ethhdr *eh;
+       struct vlan_ethhdr *veth;
        int err;
 
-       if (vlan_tx_tag_present(skb)) {
-               vlan_set_tci(skb, 0);
-               return 0;
-       }
-
-       if (unlikely(skb->protocol != htons(ETH_P_8021Q) ||
-           skb->len < VLAN_ETH_HLEN))
-               return 0;
-
        err = make_writable(skb, VLAN_ETH_HLEN);
        if (unlikely(err))
                return err;
@@ -61,9 +54,12 @@ static int strip_vlan(struct sk_buff *skb)
                skb->csum = csum_sub(skb->csum, csum_partial(skb->data
                                        + ETH_HLEN, VLAN_HLEN, 0));
 
+       veth = (struct vlan_ethhdr *) skb->data;
+       *current_tci = veth->h_vlan_TCI;
+
        memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
 
-       eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
+       eh = (struct ethhdr *)__skb_pull(skb, VLAN_HLEN);
 
        skb->protocol = eh->h_proto;
        skb->mac_header += VLAN_HLEN;
@@ -71,21 +67,52 @@ static int strip_vlan(struct sk_buff *skb)
        return 0;
 }
 
-static int modify_vlan_tci(struct sk_buff *skb, __be16 tci)
+static int pop_vlan(struct sk_buff *skb)
 {
-       if (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_8021Q)) {
-               int err;
+       __be16 tci;
+       int err;
 
-               if (unlikely(skb->len < VLAN_ETH_HLEN))
+       if (likely(vlan_tx_tag_present(skb))) {
+               vlan_set_tci(skb, 0);
+       } else {
+               if (unlikely(skb->protocol != htons(ETH_P_8021Q) ||
+                   skb->len < VLAN_ETH_HLEN))
                        return 0;
 
-               err = strip_vlan(skb);
-               if (unlikely(err))
+               err = __pop_vlan_tci(skb, &tci);
+               if (err)
                        return err;
        }
+       /* move next vlan tag to hw accel tag */
+       if (likely(skb->protocol != htons(ETH_P_8021Q) ||
+           skb->len < VLAN_ETH_HLEN))
+               return 0;
+
+       err = __pop_vlan_tci(skb, &tci);
+       if (unlikely(err))
+               return err;
 
        __vlan_hwaccel_put_tag(skb, ntohs(tci));
+       return 0;
+}
+
+static int push_vlan(struct sk_buff *skb, __be16 new_tci)
+{
+       if (unlikely(vlan_tx_tag_present(skb))) {
+               u16 current_tag;
+
+               /* push down current VLAN tag */
+               current_tag = vlan_tx_tag_get(skb);
+
+               if (!__vlan_put_tag(skb, current_tag))
+                       return -ENOMEM;
 
+               if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
+                       skb->csum = csum_add(skb->csum, csum_partial(skb->data
+                                       + ETH_HLEN, VLAN_HLEN, 0));
+
+       }
+       __vlan_hwaccel_put_tag(skb, ntohs(new_tci));
        return 0;
 }
 
@@ -270,12 +297,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                        OVS_CB(skb)->tun_id = nla_get_be64(a);
                        break;
 
-               case OVS_ACTION_ATTR_SET_DL_TCI:
-                       err = modify_vlan_tci(skb, nla_get_be16(a));
+               case OVS_ACTION_ATTR_PUSH_VLAN:
+                       err = push_vlan(skb, nla_get_be16(a));
+                       if (unlikely(err)) /* skb already freed */
+                               return err;
                        break;
 
-               case OVS_ACTION_ATTR_STRIP_VLAN:
-                       err = strip_vlan(skb);
+               case OVS_ACTION_ATTR_POP_VLAN:
+                       err = pop_vlan(skb);
                        break;
 
                case OVS_ACTION_ATTR_SET_DL_SRC:
index c1c843da51f77555212f5d9d01a79ec67d2e58cf..454f96cc20bcc85e725441fd5d38d7804cacbdfa 100644 (file)
@@ -545,8 +545,8 @@ static int validate_actions(const struct nlattr *attr)
                static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
                        [OVS_ACTION_ATTR_OUTPUT] = 4,
                        [OVS_ACTION_ATTR_USERSPACE] = 8,
-                       [OVS_ACTION_ATTR_SET_DL_TCI] = 2,
-                       [OVS_ACTION_ATTR_STRIP_VLAN] = 0,
+                       [OVS_ACTION_ATTR_PUSH_VLAN] = 2,
+                       [OVS_ACTION_ATTR_POP_VLAN] = 0,
                        [OVS_ACTION_ATTR_SET_DL_SRC] = ETH_ALEN,
                        [OVS_ACTION_ATTR_SET_DL_DST] = ETH_ALEN,
                        [OVS_ACTION_ATTR_SET_NW_SRC] = 4,
@@ -568,7 +568,7 @@ static int validate_actions(const struct nlattr *attr)
                        return -EINVAL;
 
                case OVS_ACTION_ATTR_USERSPACE:
-               case OVS_ACTION_ATTR_STRIP_VLAN:
+               case OVS_ACTION_ATTR_POP_VLAN:
                case OVS_ACTION_ATTR_SET_DL_SRC:
                case OVS_ACTION_ATTR_SET_DL_DST:
                case OVS_ACTION_ATTR_SET_NW_SRC:
@@ -586,7 +586,7 @@ static int validate_actions(const struct nlattr *attr)
                                return -EINVAL;
                        break;
 
-               case OVS_ACTION_ATTR_SET_DL_TCI:
+               case OVS_ACTION_ATTR_PUSH_VLAN:
                        if (nla_get_be16(a) & htons(VLAN_CFI_MASK))
                                return -EINVAL;
                        break;
index b8390e230a008af9819c4b9618002550fb3895b7..41cc05375d509e44fc535f57bf6e347e654bcea7 100644 (file)
 
 #endif /* linux kernel < 2.6.28 */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+
+#define ETH_P_FCOE     0x8906          /* Fibre Channel over Ethernet  */
+
+#endif /* linux kernel < 2.6.30 */
+
 #endif
index 04ebd89a77e33b45c6e32382bc0ef020f57a45cb..1ac3831287a55c21d4f0ba6d412d75b837dead38 100644 (file)
@@ -141,4 +141,30 @@ static inline struct net_device *dev_get_by_index_rcu(struct net *net, int ifind
 #define NETIF_F_FSO 0
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+#define NETIF_F_FCOE_CRC       (1 << 24) /* FCoE CRC32 */
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#define NETIF_F_IPV6_CSUM      16      /* Can checksum TCP/UDP over IPV6 */
+
+#define NETIF_F_V4_CSUM                (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
+#define NETIF_F_V6_CSUM                (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+#define skb_gso_segment rpl_skb_gso_segment
+struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features);
+
+#define netif_skb_features rpl_netif_skb_features
+u32 rpl_netif_skb_features(struct sk_buff *skb);
+
+#define netif_needs_gso rpl_netif_needs_gso
+static inline int rpl_netif_needs_gso(struct sk_buff *skb, int features)
+{
+        return skb_is_gso(skb) && (!skb_gso_ok(skb, features) ||
+                unlikely(skb->ip_summed != CHECKSUM_PARTIAL));
+}
+#endif
+
 #endif
index 1a6f327ad106ec00a74dd447327b5a193e0b4229..91855c47a665fd659bd562e00ce9125470a67469 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/if_link.h>
 #include <linux/netdevice.h>
+#include <linux/if_vlan.h>
 
 /* Linux 2.6.28 introduced dev_get_stats():
  * const struct net_device_stats *dev_get_stats(struct net_device *dev);
@@ -47,3 +48,101 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
        return storage;
 }
 #endif /* kernel version < 2.6.36 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+static bool can_checksum_protocol(unsigned long features, __be16 protocol)
+{
+       return  ((features & NETIF_F_GEN_CSUM) ||
+               ((features & NETIF_F_V4_CSUM) &&
+                               protocol == htons(ETH_P_IP)) ||
+               ((features & NETIF_F_V6_CSUM) &&
+                               protocol == htons(ETH_P_IPV6)) ||
+               ((features & NETIF_F_FCOE_CRC) &&
+                               protocol == htons(ETH_P_FCOE)));
+}
+
+static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
+{
+#ifdef CONFIG_HIGHMEM
+       int i;
+
+       if (dev->features & NETIF_F_HIGHDMA)
+               return 0;
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+               if (PageHighMem(skb_shinfo(skb)->frags[i].page))
+                       return 1;
+
+#endif
+       return 0;
+}
+
+static u32 harmonize_features(struct sk_buff *skb, __be16 protocol, u32 features)
+{
+       if (!can_checksum_protocol(features, protocol)) {
+               features &= ~NETIF_F_ALL_CSUM;
+               features &= ~NETIF_F_SG;
+       } else if (illegal_highdma(skb->dev, skb)) {
+               features &= ~NETIF_F_SG;
+       }
+
+       return features;
+}
+
+u32 rpl_netif_skb_features(struct sk_buff *skb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+       unsigned long vlan_features = 0;
+#else
+       unsigned long vlan_features = skb->dev->vlan_features;
+#endif /* kernel version < 2.6.26 */
+
+       __be16 protocol = skb->protocol;
+       u32 features = skb->dev->features;
+
+       if (protocol == htons(ETH_P_8021Q)) {
+               struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
+               protocol = veh->h_vlan_encapsulated_proto;
+       } else if (!vlan_tx_tag_present(skb)) {
+               return harmonize_features(skb, protocol, features);
+       }
+
+       features &= (vlan_features | NETIF_F_HW_VLAN_TX);
+
+       if (protocol != htons(ETH_P_8021Q)) {
+               return harmonize_features(skb, protocol, features);
+       } else {
+               features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
+                       NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_TX;
+               return harmonize_features(skb, protocol, features);
+       }
+}
+
+struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features)
+{
+       int vlan_depth = ETH_HLEN;
+       __be16 type = skb->protocol;
+       __be16 skb_proto;
+       struct sk_buff *skb_gso;
+
+       while (type == htons(ETH_P_8021Q)) {
+               struct vlan_hdr *vh;
+
+               if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
+                       return ERR_PTR(-EINVAL);
+
+               vh = (struct vlan_hdr *)(skb->data + vlan_depth);
+               type = vh->h_vlan_encapsulated_proto;
+               vlan_depth += VLAN_HLEN;
+       }
+
+       /* this hack needed to get regular skb_gso_segment() */
+#undef skb_gso_segment
+       skb_proto = skb->protocol;
+       skb->protocol = type;
+
+       skb_gso = skb_gso_segment(skb, features);
+       skb->protocol = skb_proto;
+       return skb_gso;
+}
+#endif /* kernel version < 2.6.38 */
index dbc8db2ca7817f0e99028e7b55a1d54b2daeaede..ee39ee3728ac7c7c5059b2ca7e1a61ffa3e4e95e 100644 (file)
@@ -333,19 +333,15 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
        forward_ip_summed(skb, true);
 
        if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) {
-               int features = 0;
+               int features;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
-               features = skb->dev->features & skb->dev->vlan_features;
-#endif
+               features = netif_skb_features(skb);
 
                if (!vlan_tso)
                        features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
                                      NETIF_F_UFO | NETIF_F_FSO);
 
-               if (skb_is_gso(skb) &&
-                   (!skb_gso_ok(skb, features) ||
-                    unlikely(skb->ip_summed != CHECKSUM_PARTIAL))) {
+               if (netif_needs_gso(skb, features)) {
                        struct sk_buff *nskb;
 
                        nskb = skb_gso_segment(skb, features);
index 97a7c04df766992420cc8af90cc9f7e2e94c2846..c1c9ef04ec17f2f72dd5a2d75ba2bcec8c301192 100644 (file)
@@ -409,8 +409,8 @@ enum ovs_action_type {
        OVS_ACTION_ATTR_UNSPEC,
        OVS_ACTION_ATTR_OUTPUT,       /* Output to switch port. */
        OVS_ACTION_ATTR_USERSPACE,    /* Send copy to userspace. */
-       OVS_ACTION_ATTR_SET_DL_TCI,   /* Set the 802.1q TCI value. */
-       OVS_ACTION_ATTR_STRIP_VLAN,   /* Strip the 802.1q header. */
+       OVS_ACTION_ATTR_PUSH_VLAN,    /* Set the 802.1q TCI value. */
+       OVS_ACTION_ATTR_POP_VLAN,     /* Strip the 802.1q header. */
        OVS_ACTION_ATTR_SET_DL_SRC,   /* Ethernet source address. */
        OVS_ACTION_ATTR_SET_DL_DST,   /* Ethernet destination address. */
        OVS_ACTION_ATTR_SET_NW_SRC,   /* IPv4 source address. */
index ae914dd9ac4ba5476d438f598aed21a4f3259eff..5b984fb55a61c71284bbd80c65229c5dd353ec62 100644 (file)
@@ -532,7 +532,7 @@ bond_send_learning_packet(struct bond *bond,
     compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
                           eth_src);
     if (vlan) {
-        eth_set_vlan_tci(&packet, htons(vlan));
+        eth_push_vlan(&packet, htons(vlan));
     }
     error = netdev_send(slave->netdev, &packet);
     ofpbuf_uninit(&packet);
index 5b91b7f5789a65758b63f38ba4cc86c35ab9d940..8aefaf9b0340fa45e1fb6ec792af096e2ccc4de7 100644 (file)
@@ -718,7 +718,7 @@ dpif_netdev_validate_actions(const struct nlattr *actions,
         case OVS_ACTION_ATTR_USERSPACE:
             break;
 
-        case OVS_ACTION_ATTR_SET_DL_TCI:
+        case OVS_ACTION_ATTR_PUSH_VLAN:
             *mutates = true;
             if (nl_attr_get_be16(a) & htons(VLAN_CFI)) {
                 return EINVAL;
@@ -732,7 +732,7 @@ dpif_netdev_validate_actions(const struct nlattr *actions,
             }
             break;
 
-        case OVS_ACTION_ATTR_STRIP_VLAN:
+        case OVS_ACTION_ATTR_POP_VLAN:
         case OVS_ACTION_ATTR_SET_DL_SRC:
         case OVS_ACTION_ATTR_SET_DL_DST:
         case OVS_ACTION_ATTR_SET_NW_SRC:
@@ -1142,7 +1142,7 @@ dpif_netdev_wait(struct dpif *dpif)
 }
 
 static void
-dp_netdev_strip_vlan(struct ofpbuf *packet)
+dp_netdev_pop_vlan(struct ofpbuf *packet)
 {
     struct vlan_eth_header *veh = packet->l2;
     if (packet->size >= sizeof *veh
@@ -1314,12 +1314,12 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
                                      key, nl_attr_get_u64(a));
             break;
 
-        case OVS_ACTION_ATTR_SET_DL_TCI:
-            eth_set_vlan_tci(packet, nl_attr_get_be16(a));
+        case OVS_ACTION_ATTR_PUSH_VLAN:
+            eth_push_vlan(packet, nl_attr_get_be16(a));
             break;
 
-        case OVS_ACTION_ATTR_STRIP_VLAN:
-            dp_netdev_strip_vlan(packet);
+        case OVS_ACTION_ATTR_POP_VLAN:
+            dp_netdev_pop_vlan(packet);
             break;
 
         case OVS_ACTION_ATTR_SET_DL_SRC:
index b42a03c39aa9c6e8784e7e3ee73e13769df70b73..9657595084de2bafd3da8d4df5eefc4f291b5686 100644 (file)
@@ -49,8 +49,8 @@ odp_action_len(uint16_t type)
     switch ((enum ovs_action_type) type) {
     case OVS_ACTION_ATTR_OUTPUT: return 4;
     case OVS_ACTION_ATTR_USERSPACE: return 8;
-    case OVS_ACTION_ATTR_SET_DL_TCI: return 2;
-    case OVS_ACTION_ATTR_STRIP_VLAN: return 0;
+    case OVS_ACTION_ATTR_PUSH_VLAN: return 2;
+    case OVS_ACTION_ATTR_POP_VLAN: return 0;
     case OVS_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN;
     case OVS_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN;
     case OVS_ACTION_ATTR_SET_NW_SRC: return 4;
@@ -113,13 +113,13 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
         ds_put_format(ds, "set_tunnel(%#"PRIx64")",
                       ntohll(nl_attr_get_be64(a)));
         break;
-    case OVS_ACTION_ATTR_SET_DL_TCI:
-        ds_put_format(ds, "set_tci(vid=%"PRIu16",pcp=%d)",
+    case OVS_ACTION_ATTR_PUSH_VLAN:
+        ds_put_format(ds, "push_vlan(vid=%"PRIu16",pcp=%d)",
                       vlan_tci_to_vid(nl_attr_get_be16(a)),
                       vlan_tci_to_pcp(nl_attr_get_be16(a)));
         break;
-    case OVS_ACTION_ATTR_STRIP_VLAN:
-        ds_put_format(ds, "strip_vlan");
+    case OVS_ACTION_ATTR_POP_VLAN:
+        ds_put_format(ds, "pop_vlan");
         break;
     case OVS_ACTION_ATTR_SET_DL_SRC:
         eth = nl_attr_get_unspec(a, ETH_ADDR_LEN);
index e05e3ebca5552de331a9ab7ebf55e31c92894536..094f55366404dde0de4c11442341b75c4dfd5f0d 100644 (file)
@@ -75,33 +75,27 @@ compose_benign_packet(struct ofpbuf *b, const char *tag, uint16_t snap_type,
     memcpy(payload + tag_size, eth_src, ETH_ADDR_LEN);
 }
 
-/* Modify the TCI field of 'packet', whose data must begin with an Ethernet
- * header.  If a VLAN tag is present, its TCI field is replaced by 'tci'.  If a
- * VLAN tag is not present, one is added with the TCI field set to 'tci'.
+/* Insert VLAN header according to given TCI. Packet passed must be Ethernet
+ * packet.
  *
  * Also sets 'packet->l2' to point to the new Ethernet header. */
 void
-eth_set_vlan_tci(struct ofpbuf *packet, ovs_be16 tci)
+eth_push_vlan(struct ofpbuf *packet, ovs_be16 tci)
 {
     struct eth_header *eh = packet->data;
     struct vlan_eth_header *veh;
 
-    if (packet->size >= sizeof(struct vlan_eth_header)
-        && eh->eth_type == htons(ETH_TYPE_VLAN)) {
-        veh = packet->data;
-        veh->veth_tci = tci;
-    } else {
-        /* Insert new 802.1Q header. */
-        struct vlan_eth_header tmp;
-        memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
-        memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
-        tmp.veth_type = htons(ETH_TYPE_VLAN);
-        tmp.veth_tci = tci;
-        tmp.veth_next_type = eh->eth_type;
-
-        veh = ofpbuf_push_uninit(packet, VLAN_HEADER_LEN);
-        memcpy(veh, &tmp, sizeof tmp);
-    }
+    /* Insert new 802.1Q header. */
+    struct vlan_eth_header tmp;
+    memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
+    memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
+    tmp.veth_type = htons(ETH_TYPE_VLAN);
+    tmp.veth_tci = tci;
+    tmp.veth_next_type = eh->eth_type;
+
+    veh = ofpbuf_push_uninit(packet, VLAN_HEADER_LEN);
+    memcpy(veh, &tmp, sizeof tmp);
+
     packet->l2 = packet->data;
 }
 
index a389e6af74ff500a21612c6c98f47ddecc5bc1a8..cb126380921d4295383987689dd033ec21168b01 100644 (file)
@@ -131,7 +131,7 @@ void compose_benign_packet(struct ofpbuf *, const char *tag,
                            uint16_t snap_type,
                            const uint8_t eth_src[ETH_ADDR_LEN]);
 
-void eth_set_vlan_tci(struct ofpbuf *, ovs_be16 tci);
+void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
 
 /* Example:
  *
index d41b7da21f652f6ca9cfa560886e9cd1f53058d1..21ef799f7b2689b62135f339d37aa5341293113f 100644 (file)
@@ -538,7 +538,7 @@ dpif_sflow_received(struct dpif_sflow *ds, const struct dpif_upcall *upcall,
             n_outputs++;
             break;
 
-        case OVS_ACTION_ATTR_SET_DL_TCI:
+        case OVS_ACTION_ATTR_PUSH_VLAN:
             tci = nl_attr_get_be16(a);
             switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(tci);
             switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(tci);
index 5d93153a177f02709557dbe463249857cb8b8710..3074881eaca801f0367795ab49358d53c4c36bd6 100644 (file)
@@ -2289,11 +2289,11 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet)
             }
             break;
 
-        case OVS_ACTION_ATTR_STRIP_VLAN:
+        case OVS_ACTION_ATTR_POP_VLAN:
             vlan_tci = htons(0);
             break;
 
-        case OVS_ACTION_ATTR_SET_DL_TCI:
+        case OVS_ACTION_ATTR_PUSH_VLAN:
             vlan_tci = nl_attr_get_be16(a);
             break;
         }
@@ -2857,9 +2857,12 @@ commit_odp_actions(struct action_xlate_ctx *ctx)
 
     if (base->vlan_tci != flow->vlan_tci) {
         if (!(flow->vlan_tci & htons(VLAN_CFI))) {
-            nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_STRIP_VLAN);
+            nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
         } else {
-            nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_DL_TCI,
+            if (base->vlan_tci != OFP_VLAN_NONE) {
+                nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+            }
+            nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
                             flow->vlan_tci & ~htons(VLAN_CFI));
         }
         base->vlan_tci = flow->vlan_tci;
@@ -3656,13 +3659,17 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan,
         }
         if (dst->vlan != cur_vlan) {
             if (dst->vlan == OFP_VLAN_NONE) {
-                nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_STRIP_VLAN);
+                nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
             } else {
                 ovs_be16 tci;
+
+                if (cur_vlan != OFP_VLAN_NONE) {
+                     nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+                }
                 tci = htons(dst->vlan & VLAN_VID_MASK);
                 tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK);
                 nl_msg_put_be16(ctx->odp_actions,
-                                OVS_ACTION_ATTR_SET_DL_TCI, tci);
+                                OVS_ACTION_ATTR_PUSH_VLAN, tci);
             }
             cur_vlan = dst->vlan;
         }