This patch looks very similar to the ones I had posted earlier albeit one key difference being a variable added in ovs_skb_cb and obvious difference in offset calculation code which is done based on this field. Some questions<div>
<div><br></div><div>1. Functions in this patch have similar issues which my initial patch had for e.g. not updating checksum after push/pop.</div><div><br></div><div>2. In the long list of email conversation we had in July at least my understanding was you did not want additional variables to be added to ovs_skb_cb, is this still the case? if not, what&#39;s the advantage of this approach v/s the approach taken in my patch?</div>
<div><br></div><div>3. Do you plan to use this as a base for packet recirculation logic? because I do not see anything that relates to it. Does recirculation for qinq, multiple mpls tags, mpls/gre and other multiple encaps be supported by this logic? </div>
<div><br></div><div>If answer to Q2 is yes, then I feel this would be a bit regress since the kernel patch I had posted earlier was reviewed at the very early stage by Ben and later by Pravin. If answer to Q2 is &quot;more fields can be added&quot;  then the relevant changes in this patch can be easily integrated.  just my 2 cents.</div>
<div><br><div class="gmail_quote">On Wed, Oct 3, 2012 at 12:17 PM, Jesse Gross <span dir="ltr">&lt;<a href="mailto:jesse@nicira.com" target="_blank">jesse@nicira.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
From: Leo Alterman &lt;<a href="mailto:lalterman@nicira.com">lalterman@nicira.com</a>&gt;<br>
<br>
Allow datapath to recognize and extract MPLS labels into flow keys<br>
and execute actions which push, pop, and set labels on packets.<br>
<br>
Signed-off-by: Leo Alterman &lt;<a href="mailto:lalterman@nicira.com">lalterman@nicira.com</a>&gt;<br>
--<br>
I haven&#39;t reviewed this but hopefully it can be used a good starting point.<br>
---<br>
 datapath/actions.c          |   81 +++++++++++++++++++++++++++++++++++++++++++<br>
 datapath/datapath.c         |   56 ++++++++++++++++++++++++++++++<br>
 datapath/datapath.h         |    8 +++++<br>
 datapath/flow.c             |   30 ++++++++++++++++<br>
 datapath/flow.h             |    7 ++++<br>
 datapath/vport.c            |    2 ++<br>
 include/linux/openvswitch.h |   32 +++++++++++++++++<br>
 7 files changed, 216 insertions(+)<br>
<br>
diff --git a/datapath/actions.c b/datapath/actions.c<br>
index 208f260..a199125 100644<br>
--- a/datapath/actions.c<br>
+++ b/datapath/actions.c<br>
@@ -47,6 +47,67 @@ static int make_writable(struct sk_buff *skb, int write_len)<br>
        return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);<br>
 }<br>
<br>
+static __be16 get_ethertype(const struct sk_buff *skb)<br>
+{<br>
+       struct ethhdr *hdr = (struct ethhdr *)(skb_cb_mpls_stack(skb) - ETH_HLEN);<br>
+       return hdr-&gt;h_proto;<br>
+}<br>
+<br>
+static void set_ethertype(struct sk_buff *skb, const __be16 ethertype)<br>
+{<br>
+       struct ethhdr *hdr = (struct ethhdr *)(skb_cb_mpls_stack(skb) - ETH_HLEN);<br>
+       hdr-&gt;h_proto = ethertype;<br>
+}<br>
+<br>
+static int push_mpls(struct sk_buff *skb, const struct ovs_action_push_mpls *mpls)<br>
+{<br>
+       u32 l2_size;<br>
+       __be32 *new_mpls_label;<br>
+<br>
+       if (skb_cow_head(skb, MPLS_HLEN) &lt; 0) {<br>
+               kfree_skb(skb);<br>
+               return -ENOMEM;<br>
+       }<br>
+<br>
+       l2_size = skb_cb_mpls_stack_offset(skb);<br>
+       skb_push(skb, MPLS_HLEN);<br>
+       memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), l2_size);<br>
+       skb_reset_mac_header(skb);<br>
+<br>
+       new_mpls_label = (__be32 *)(skb_mac_header(skb) + l2_size);<br>
+       *new_mpls_label = mpls-&gt;mpls_label;<br>
+<br>
+       set_ethertype(skb, mpls-&gt;mpls_ethertype);<br>
+       return 0;<br>
+}<br>
+<br>
+static int pop_mpls(struct sk_buff *skb, const __be16 *ethertype)<br>
+{<br>
+       __be16 current_ethertype = get_ethertype(skb);<br>
+       if (current_ethertype == htons(ETH_P_MPLS_UC) ||<br>
+               current_ethertype == htons(ETH_P_MPLS_MC)) {<br>
+               u32 l2_size = skb_cb_mpls_stack_offset(skb);<br>
+<br>
+               memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb), l2_size);<br>
+<br>
+               skb_pull(skb, MPLS_HLEN);<br>
+               skb_reset_mac_header(skb);<br>
+<br>
+               set_ethertype(skb, *ethertype);<br>
+       }<br>
+       return 0;<br>
+}<br>
+<br>
+static int set_mpls(struct sk_buff *skb, const __be32 *mpls_label)<br>
+{<br>
+       __be16 current_ethertype = get_ethertype(skb);<br>
+       if (current_ethertype == htons(ETH_P_MPLS_UC) ||<br>
+               current_ethertype == htons(ETH_P_MPLS_MC)) {<br>
+               memcpy(skb_cb_mpls_stack(skb), mpls_label, sizeof(__be32));<br>
+       }<br>
+       return 0;<br>
+}<br>
+<br>
 /* remove VLAN header from packet and update csum accrodingly. */<br>
 static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)<br>
 {<br>
@@ -100,6 +161,9 @@ static int pop_vlan(struct sk_buff *skb)<br>
                return err;<br>
<br>
        __vlan_hwaccel_put_tag(skb, ntohs(tci));<br>
+<br>
+       /* update pointer to MPLS label stack */<br>
+       skb_cb_set_mpls_stack(skb);<br>
        return 0;<br>
 }<br>
<br>
@@ -120,6 +184,9 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla<br>
<br>
        }<br>
        __vlan_hwaccel_put_tag(skb, ntohs(vlan-&gt;vlan_tci) &amp; ~VLAN_TAG_PRESENT);<br>
+<br>
+       /* update pointer to MPLS label stack */<br>
+       skb_cb_set_mpls_stack(skb);<br>
        return 0;<br>
 }<br>
<br>
@@ -396,6 +463,20 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,<br>
                        output_userspace(dp, skb, a);<br>
                        break;<br>
<br>
+               case OVS_ACTION_ATTR_PUSH_MPLS:<br>
+                       err = push_mpls(skb, nla_data(a));<br>
+                       if (unlikely(err)) /* skb already freed. */<br>
+                               return err;<br>
+                       break;<br>
+<br>
+               case OVS_ACTION_ATTR_POP_MPLS:<br>
+                       err = pop_mpls(skb, nla_data(a));<br>
+                       break;<br>
+<br>
+               case OVS_ACTION_ATTR_SET_MPLS:<br>
+                       err = set_mpls(skb, nla_data(a));<br>
+                       break;<br>
+<br>
                case OVS_ACTION_ATTR_PUSH_VLAN:<br>
                        err = push_vlan(skb, nla_data(a));<br>
                        if (unlikely(err)) /* skb already freed. */<br>
diff --git a/datapath/datapath.c b/datapath/datapath.c<br>
index c83ce16..91b70bb 100644<br>
--- a/datapath/datapath.c<br>
+++ b/datapath/datapath.c<br>
@@ -74,6 +74,41 @@ int ovs_net_id __read_mostly;<br>
 int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);<br>
 EXPORT_SYMBOL(ovs_dp_ioctl_hook);<br>
<br>
+void skb_cb_set_mpls_stack(struct sk_buff *skb)<br>
+{<br>
+       struct ethhdr *eth;<br>
+       int nh_ofs;<br>
+       __be16 dl_type = 0;<br>
+<br>
+       eth = eth_hdr(skb);<br>
+       nh_ofs = sizeof(struct ethhdr);<br>
+       if (likely(eth-&gt;h_proto &gt;= htons(ETH_TYPE_MIN))) {<br>
+               dl_type = eth-&gt;h_proto;<br>
+<br>
+               while (dl_type == htons(ETH_P_8021Q) &amp;&amp;<br>
+                               skb-&gt;len &gt;= nh_ofs + sizeof(struct vlan_hdr)) {<br>
+                       struct vlan_hdr *vh = (struct vlan_hdr*)(skb-&gt;data + nh_ofs);<br>
+                       dl_type = vh-&gt;h_vlan_encapsulated_proto;<br>
+                       nh_ofs += sizeof(struct vlan_hdr);<br>
+               }<br>
+<br>
+               OVS_CB(skb)-&gt;mpls_stack = nh_ofs;<br>
+       } else {<br>
+               OVS_CB(skb)-&gt;mpls_stack = 0;<br>
+       }<br>
+}<br>
+<br>
+unsigned char *skb_cb_mpls_stack(const struct sk_buff *skb)<br>
+{<br>
+       return OVS_CB(skb)-&gt;mpls_stack ?<br>
+                                       skb_mac_header(skb) + OVS_CB(skb)-&gt;mpls_stack : 0;<br>
+}<br>
+<br>
+ptrdiff_t skb_cb_mpls_stack_offset(const struct sk_buff *skb)<br>
+{<br>
+       return OVS_CB(skb)-&gt;mpls_stack;<br>
+}<br>
+<br>
 /**<br>
  * DOC: Locking:<br>
  *<br>
@@ -663,12 +698,17 @@ static int validate_actions(const struct nlattr *attr,<br>
                static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {<br>
                        [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),<br>
                        [OVS_ACTION_ATTR_USERSPACE] = (u32)-1,<br>
+                       [OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls),<br>
+                       [OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16),<br>
+                       [OVS_ACTION_ATTR_SET_MPLS] = sizeof(__be32),<br>
                        [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),<br>
                        [OVS_ACTION_ATTR_POP_VLAN] = 0,<br>
                        [OVS_ACTION_ATTR_SET] = (u32)-1,<br>
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1<br>
                };<br>
                const struct ovs_action_push_vlan *vlan;<br>
+               __be16 mpls_ethertype;<br>
+               __be32 mpls_label;<br>
                int type = nla_type(a);<br>
<br>
                if (type &gt; OVS_ACTION_ATTR_MAX ||<br>
@@ -691,6 +731,22 @@ static int validate_actions(const struct nlattr *attr,<br>
                                return -EINVAL;<br>
                        break;<br>
<br>
+               case OVS_ACTION_ATTR_PUSH_MPLS:<br>
+                       mpls_ethertype = nla_get_be16(a);<br>
+                       if (mpls_ethertype != htons(ETH_P_MPLS_UC) &amp;&amp;<br>
+                               mpls_ethertype != htons(ETH_P_MPLS_MC))<br>
+                               return -EINVAL;<br>
+                       break;<br>
+<br>
+               case OVS_ACTION_ATTR_POP_MPLS:<br>
+                       break;<br>
+<br>
+               case OVS_ACTION_ATTR_SET_MPLS:<br>
+                       mpls_label = nla_get_be32(a);<br>
+                       if (mpls_label == htonl(0)) {<br>
+                               return -EINVAL;<br>
+                       }<br>
+                       break;<br>
<br>
                case OVS_ACTION_ATTR_POP_VLAN:<br>
                        break;<br>
diff --git a/datapath/datapath.h b/datapath/datapath.h<br>
index affbf0e..1801ccd 100644<br>
--- a/datapath/datapath.h<br>
+++ b/datapath/datapath.h<br>
@@ -97,6 +97,9 @@ struct datapath {<br>
  * struct ovs_skb_cb - OVS data in skb CB<br>
  * @flow: The flow associated with this packet.  May be %NULL if no flow.<br>
  * @tun_id: ID of the tunnel that encapsulated this packet.  It is 0 if the<br>
+ * packet is not being tunneled.<br>
+ * @mpls_stack: Offset of the packet&#39;s MPLS stack from the beginning of the<br>
+ * ethernet frame.  It is 0 if no MPLS stack is present.<br>
  * @ip_summed: Consistently stores L4 checksumming status across different<br>
  * kernel versions.<br>
  * @csum_start: Stores the offset from which to start checksumming independent<br>
@@ -108,6 +111,7 @@ struct datapath {<br>
 struct ovs_skb_cb {<br>
        struct sw_flow          *flow;<br>
        __be64                  tun_id;<br>
+       ptrdiff_t       mpls_stack;<br>
 #ifdef NEED_CSUM_NORMALIZE<br>
        enum csum_type          ip_summed;<br>
        u16                     csum_start;<br>
@@ -192,4 +196,8 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,<br>
                                         u8 cmd);<br>
<br>
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);<br>
+<br>
+void skb_cb_set_mpls_stack(struct sk_buff *skb);<br>
+unsigned char *skb_cb_mpls_stack(const struct sk_buff *skb);<br>
+ptrdiff_t skb_cb_mpls_stack_offset(const struct sk_buff *skb);<br>
 #endif /* datapath.h */<br>
diff --git a/datapath/flow.c b/datapath/flow.c<br>
index d07337c..40df8ff 100644<br>
--- a/datapath/flow.c<br>
+++ b/datapath/flow.c<br>
@@ -739,6 +739,14 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,<br>
                                key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);<br>
                        }<br>
                }<br>
+       } else if (key-&gt;eth.type == htons(ETH_P_MPLS_UC) ||<br>
+                               key-&gt;eth.type == htons(ETH_P_MPLS_MC)) {<br>
+               error = check_header(skb, MPLS_HLEN);<br>
+               if (unlikely(error))<br>
+                       goto out;<br>
+<br>
+               key_len = SW_FLOW_KEY_OFFSET(mpls.top_label);<br>
+               memcpy(&amp;key-&gt;mpls.top_label, skb_network_header(skb), MPLS_HLEN);<br>
        } else if (key-&gt;eth.type == htons(ETH_P_IPV6)) {<br>
                int nh_len;             /* IPv6 Header + Extensions */<br>
<br>
@@ -836,6 +844,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {<br>
        [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),<br>
        [OVS_KEY_ATTR_VLAN] = sizeof(__be16),<br>
        [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16),<br>
+       [OVS_KEY_ATTR_MPLS] = sizeof(struct ovs_key_mpls),<br>
        [OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4),<br>
        [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),<br>
        [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),<br>
@@ -1141,6 +1150,17 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,<br>
                swkey-&gt;ip.proto = ntohs(arp_key-&gt;arp_op);<br>
                memcpy(swkey-&gt;ipv4.arp.sha, arp_key-&gt;arp_sha, ETH_ALEN);<br>
                memcpy(swkey-&gt;ipv4.arp.tha, arp_key-&gt;arp_tha, ETH_ALEN);<br>
+       } else if (swkey-&gt;eth.type == htons(ETH_P_MPLS_UC) ||<br>
+                               swkey-&gt;eth.type == htons(ETH_P_MPLS_MC)) {<br>
+               const struct ovs_key_mpls *mpls_key;<br>
+<br>
+               if (!(attrs &amp; (1 &lt;&lt; OVS_KEY_ATTR_MPLS)))<br>
+                       return -EINVAL;<br>
+               attrs &amp;= ~(1 &lt;&lt; OVS_KEY_ATTR_MPLS);<br>
+<br>
+               key_len = SW_FLOW_KEY_OFFSET(mpls.top_label);<br>
+               mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]);<br>
+               swkey-&gt;mpls.top_label = mpls_key-&gt;mpls_top_label;<br>
        }<br>
<br>
        if (attrs)<br>
@@ -1284,6 +1304,16 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)<br>
                arp_key-&gt;arp_op = htons(swkey-&gt;ip.proto);<br>
                memcpy(arp_key-&gt;arp_sha, swkey-&gt;ipv4.arp.sha, ETH_ALEN);<br>
                memcpy(arp_key-&gt;arp_tha, swkey-&gt;ipv4.arp.tha, ETH_ALEN);<br>
+       } else if (swkey-&gt;eth.type == htons(ETH_P_MPLS_UC) ||<br>
+                               swkey-&gt;eth.type == htons(ETH_P_MPLS_MC)) {<br>
+               struct ovs_key_mpls *mpls_key;<br>
+<br>
+               nla = nla_reserve(skb, OVS_KEY_ATTR_MPLS, sizeof(*mpls_key));<br>
+               if (!nla)<br>
+                       goto nla_put_failure;<br>
+               mpls_key = nla_data(nla);<br>
+               memset(mpls_key, 0, sizeof(struct ovs_key_mpls));<br>
+               mpls_key-&gt;mpls_top_label = swkey-&gt;mpls.top_label;<br>
        }<br>
<br>
        if ((swkey-&gt;eth.type == htons(ETH_P_IP) ||<br>
diff --git a/datapath/flow.h b/datapath/flow.h<br>
index 5be481e..c18183b 100644<br>
--- a/datapath/flow.h<br>
+++ b/datapath/flow.h<br>
@@ -53,6 +53,9 @@ struct sw_flow_key {<br>
                __be16 type;            /* Ethernet frame type. */<br>
        } eth;<br>
        struct {<br>
+               __be32 top_label;       /* 0 if no MPLS, top label from stack otherwise */<br>
+       } mpls;<br>
+       struct {<br>
                u8     proto;           /* IP protocol or lower 8 bits of ARP opcode. */<br>
                u8     tos;             /* IP ToS. */<br>
                u8     ttl;             /* IP TTL/hop limit. */<br>
@@ -126,6 +129,10 @@ struct arp_eth_header {<br>
        unsigned char       ar_tip[4];          /* target IP address        */<br>
 } __packed;<br>
<br>
+#define ETH_TYPE_MIN 0x600<br>
+<br>
+#define MPLS_HLEN 4<br>
+<br>
 int ovs_flow_init(void);<br>
 void ovs_flow_exit(void);<br>
<br>
diff --git a/datapath/vport.c b/datapath/vport.c<br>
index 172261a..0664e4c 100644<br>
--- a/datapath/vport.c<br>
+++ b/datapath/vport.c<br>
@@ -464,6 +464,8 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)<br>
        if (!(vport-&gt;ops-&gt;flags &amp; VPORT_F_TUN_ID))<br>
                OVS_CB(skb)-&gt;tun_id = 0;<br>
<br>
+       skb_cb_set_mpls_stack(skb);<br>
+<br>
        ovs_dp_process_received_packet(vport, skb);<br>
 }<br>
<br>
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h<br>
index f5c9cca..78960d1 100644<br>
--- a/include/linux/openvswitch.h<br>
+++ b/include/linux/openvswitch.h<br>
@@ -278,6 +278,7 @@ enum ovs_key_attr {<br>
        OVS_KEY_ATTR_ICMPV6,    /* struct ovs_key_icmpv6 */<br>
        OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */<br>
        OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */<br>
+       OVS_KEY_ATTR_MPLS,      /* struct ovs_key_mpls */<br>
        OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */<br>
        __OVS_KEY_ATTR_MAX<br>
 };<br>
@@ -307,6 +308,10 @@ struct ovs_key_ethernet {<br>
        __u8     eth_dst[6];<br>
 };<br>
<br>
+struct ovs_key_mpls {<br>
+       __be32 mpls_top_label;<br>
+};<br>
+<br>
 struct ovs_key_ipv4 {<br>
        __be32 ipv4_src;<br>
        __be32 ipv4_dst;<br>
@@ -437,6 +442,20 @@ enum ovs_userspace_attr {<br>
 #define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)<br>
<br>
 /**<br>
+ * struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.<br>
+ * @mpls_label: MPLS label to push.<br>
+ * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame.<br>
+ *<br>
+ * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and<br>
+ * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Any other values would<br>
+ * produce a corrupt packet.<br>
+ */<br>
+struct ovs_action_push_mpls {<br>
+       __be32 mpls_label;<br>
+       __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */<br>
+};<br>
+<br>
+/**<br>
  * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument.<br>
  * @vlan_tpid: Tag protocol identifier (TPID) to push.<br>
  * @vlan_tci: Tag control identifier (TCI) to push.  The CFI bit must be set<br>
@@ -461,6 +480,16 @@ struct ovs_action_push_vlan {<br>
  * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header.  The<br>
  * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its<br>
  * value.<br>
+ * @OVS_ACTION_ATTR_PUSH_MPLS: Push a new MPLS label onto the top of the packet<br>
+ * MPLS stack. Set the ethertype of the encapsulating frame to either<br>
+ * %ETH_P_MPLS_UC or %ETH_P_MPLS_MC to indicate the new packet contents.<br>
+ * @OVS_ACTION_ATTR_POP_MPLS: Pop an MPLS label off of the packet&#39;s MPLS stack.<br>
+ * Set the encapsulating frame&#39;s ethertype to indicate the new packet contents<br>
+ * (this could potentially still be %ETH_P_MPLS_* if there are remaining MPLS<br>
+ * labels).  If there are no MPLS labels as determined by ethertype, no action<br>
+ * is taken.<br>
+ * @OVS_ACTION_ATTR_SET_MPLS: Set the value of the top-most label in the MPLS<br>
+ * label stack.  If there are no MPLS labels in the packet, no action is taken.<br>
  * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the<br>
  * packet.<br>
  * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.<br>
@@ -477,6 +506,9 @@ enum ovs_action_attr {<br>
        OVS_ACTION_ATTR_OUTPUT,       /* u32 port number. */<br>
        OVS_ACTION_ATTR_USERSPACE,    /* Nested OVS_USERSPACE_ATTR_*. */<br>
        OVS_ACTION_ATTR_SET,          /* One nested OVS_KEY_ATTR_*. */<br>
+       OVS_ACTION_ATTR_PUSH_MPLS,    /* struct ovs_action_push_mpls. */<br>
+       OVS_ACTION_ATTR_POP_MPLS,     /* __be16 ethertype. */<br>
+       OVS_ACTION_ATTR_SET_MPLS,     /* __be32 MPLS label */<br>
        OVS_ACTION_ATTR_PUSH_VLAN,    /* struct ovs_action_push_vlan. */<br>
        OVS_ACTION_ATTR_POP_VLAN,     /* No argument. */<br>
        OVS_ACTION_ATTR_SAMPLE,       /* Nested OVS_SAMPLE_ATTR_*. */<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.7.9.5<br>
<br>
_______________________________________________<br>
dev mailing list<br>
<a href="mailto:dev@openvswitch.org">dev@openvswitch.org</a><br>
<a href="http://openvswitch.org/mailman/listinfo/dev" target="_blank">http://openvswitch.org/mailman/listinfo/dev</a><br>
</font></span></blockquote></div><br></div></div>