[ovs-dev] Q on sending packets from the system running ovs

Ben Pfaff blp at nicira.com
Wed Feb 15 20:46:34 UTC 2012


[resending with correct ovs-dev email address]

On Wed, Feb 15, 2012 at 08:59:49PM +0100, Ravi.Kerur at telekom.com wrote:
> Thanks for vlan pointer Ben. Attached diffs for mpls actions code. 
> 
> Currently I am debugging with integrated nicira-ext mpls label/tc match and this diffs to fix "no flow match" problem. Problem exists with just the posted diffs as well. Kindly let me know if you find anything suspicious.
> 
> Thanks
> Ravi	
> 
> -----Original Message-----
> From: Ben Pfaff [mailto:blp at nicira.com] 
> Sent: Wednesday, February 15, 2012 9:32 AM
> To: Kerur, Ravi
> Cc: jesse at nicira.com
> Subject: Re: [ovs-dev] Q on sending packets from the system running ovs
> 
> On Wed, Feb 15, 2012 at 02:56:45AM +0100, Ravi.Kerur at telekom.com wrote:
> > Currently I have separated out code changes into 2 sections mpls
> > actions for user and kernel and mpls match.
> 
> I'd encourage you to send out your code to ovs-dev for review.  Even if
> it is not 100% ready I am sure that we can provide useful early
> feedback.
> 
> > I have only patched mpls actions code into latest ovs today and
> > started testing with basic vlan match as I had described before. I
> > still see the problem of vlan packets not matching.
> > 
> > Any debugs I should enable which will help in fixing this issue or
> > pointer to code which does flow match(I can trace it from there).
> > 
> > The way I am testing is I have written a small program to send out
> > vlan/ipv4 packets. I execute it via command line and expect flow rules
> > to match and take necessary action mentioned in the flow.
> 
> Probably you're running into an upstream kernel bug that keeps VLAN
> packet sent by userspace using AF_PACKET from being interpreted
> correctly.  If that is the case then you can use this patch that I
> posted in December to work around the problem in Open vSwitch:
>         http://openvswitch.org/pipermail/dev/2011-December/013964.html


-------------- next part --------------
diff --git a/datapath/actions.c b/datapath/actions.c
index 824791d..083fe4c 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -123,6 +123,283 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla
 	return 0;
 }
 
+/* set ethertype in the header */
+static uint16_t
+get_ethertype (struct sk_buff *skb)
+{
+    struct ethhdr *eth = eth_hdr(skb);
+    if (likely(ntohs(eth->h_proto) >= 0x0600)) {
+        return eth->h_proto;
+    } else {
+        printk("Can't get ethertype\n");
+        return 0;
+    }
+}
+
+/* set ethertype in the header */
+static void
+set_ethertype (struct sk_buff *skb, uint16_t eth_type)
+{
+    struct ethhdr *eth = eth_hdr(skb);
+    if (likely(ntohs(eth->h_proto) >= 0x0600)) {
+        eth->h_proto = eth_type;
+    } else {
+        printk("Can't set ethertype\n");
+    }
+    skb->protocol = eth_type;
+}
+
+/** determine where MPLS header starts
+ * assumes mac_header is already set */
+static inline char *
+get_mpls_hdr (const struct sk_buff *skb)
+{
+    struct ethhdr *eth;
+    int nh_ofs;
+    uint16_t dl_type = 0;
+
+    eth = eth_hdr(skb);
+    nh_ofs = sizeof *eth;
+    if (likely(ntohs(eth->h_proto) >= 0x0600))
+        dl_type = eth->h_proto;
+
+    /* Check for a VLAN tag */
+    if (dl_type == htons(ETH_P_8021Q) &&
+        skb->len >= nh_ofs + sizeof(struct vlan_hdr)) {
+        struct vlan_hdr *vh = (struct vlan_hdr*)(skb->data + nh_ofs);
+        dl_type = vh->h_vlan_encapsulated_proto;
+        nh_ofs += sizeof(struct vlan_hdr);
+    }
+
+    return skb_mac_header(skb) + nh_ofs;
+}
+
+/* remove the top label in the MPLS label stack */
+static void 
+pop_mpls_lse (struct sk_buff *skb, mpls_hdr *mpls_h)
+{
+    uint32_t offset = (uintptr_t)get_mpls_hdr(skb) - (uintptr_t)skb->data;
+
+    printk("in pop_mpls_lse %x %x %x\n", get_mpls_hdr(skb), skb->data, offset);
+    /* move everything up to L2.5 up 4 bytes */
+    memmove((void *)skb->data + sizeof(mpls_hdr), skb->data, offset);
+
+    /* pull offset + size */
+    skb_pull(skb, sizeof(mpls_hdr));
+
+    /* reset poniter to L2 */
+    skb_reset_mac_header(skb);
+}
+
+
+/* add an MPLS label to the top off the label stack */
+static void 
+push_mpls_lse (struct sk_buff *skb, mpls_hdr *mpls_h)
+{
+    /* bytes before L2.5 */
+    uint32_t offset = (uintptr_t)get_mpls_hdr(skb) - (uintptr_t)skb->data;
+
+    printk("in push_mpls_lse %x %x %x\n", get_mpls_hdr(skb), skb->data, offset);
+
+    /* make sure there's room */
+    if (skb_cow_head(skb, MPLS_HLEN) < 0) {
+        printk("insufficient room in skb to push label %u\n", mpls_h->label);
+        kfree_skb(skb);
+        return;
+    }
+
+    /* make room for new label by adding 4 bytes */
+    skb_push(skb, MPLS_HLEN);
+
+    /* reset pointer to L2 */
+    skb_reset_mac_header(skb);
+
+    /*  move L2 header to make room for new label */
+    memmove((void *)skb->data, (void *)skb->data + MPLS_HLEN, offset);
+
+    *((uint32_t*)get_mpls_hdr(skb)) = htonl(mpls_h->value);
+    printk("\nin push_mpls_lse %x %x\n", mpls_h->value, htonl(mpls_h->value));
+    return;
+}
+
+static int 
+pop_mpls (struct sk_buff *skb,
+          const struct ovs_action_mpls *mpls)
+{
+    struct ethhdr *eth;
+    mpls_hdr mpls_h;
+    mpls_hdr *mpls_header;
+    struct sw_flow_key *key = &OVS_CB(skb)->flow->key;
+
+    uint16_t eth_proto = ntohs(key->eth.type);
+
+    printk("pop_mpls %x", eth_proto);
+
+    /* TODO check  multicast MPLS */
+    eth = eth_hdr(skb);
+    if (eth_proto == ETH_P_MPLS_UC ||
+        eth_proto == ETH_P_MPLS_MC) {
+
+        /* grab the MLPS label at the top of the stack */
+        mpls_header = (mpls_hdr *)get_mpls_hdr(skb);
+        mpls_h.value = ntohl(*(uint32_t*)mpls_header);
+
+        printk("\nmpls_h.value %x %x\n", mpls_h.value, mpls->ethertype);
+        if (mpls_h.s) {
+            set_ethertype(skb, mpls->ethertype);
+            key->eth.type = mpls->ethertype;
+        }
+        pop_mpls_lse(skb, &mpls_h);
+    }
+
+    return 0;
+}
+
+static int 
+push_mpls (struct sk_buff *skb, 
+           const struct ovs_action_mpls *mpls)
+{
+    struct iphdr *nh;
+    mpls_hdr mpls_h;
+    mpls_hdr *last_mpls_h;
+    uint16_t eth_proto;
+
+    /* offset = (uintptr_t)get_mpls_hdr(skb) - (char *)skb->data; */
+
+    eth_proto = htons(get_ethertype(skb));
+    last_mpls_h = (mpls_hdr *)get_mpls_hdr(skb);
+    nh = ip_hdr(skb);
+
+    /* first check whether there is another label on the stack */
+    if (eth_proto == ETH_P_8021Q ||
+        eth_proto == ETH_P_MPLS_UC ||
+        eth_proto == ETH_P_MPLS_MC) {
+        mpls_h.s = 0; /* must be 0 */
+        mpls_h.ttl = last_mpls_h->ttl;
+        mpls_h.tc = last_mpls_h->tc;
+        mpls_h.label = last_mpls_h->label;
+    } else if (eth_proto == ETH_P_IP) {
+        mpls_h.s = 1; /* bottom of stack */
+        mpls_h.ttl = nh->ttl;
+        mpls_h.tc = 0;
+        mpls_h.label = 0;
+        /* also change the Ethertype to MPLS */
+        set_ethertype(skb, mpls->ethertype);
+        printk("\npush_mpls %x", mpls_h.value);
+    }
+
+    /* push on the new label */
+    push_mpls_lse(skb, &mpls_h);
+
+    return 0;
+}
+
+static int 
+set_mpls_label (struct sk_buff *skb,
+                const struct ovs_action_set_mpls_label *mpls)
+{
+    mpls_hdr mpls_h;
+    uint32_t value_to_swap;
+    uint16_t eth_proto;
+
+    eth_proto = htons(get_ethertype(skb));
+
+    printk("mpls_label %x", eth_proto);
+
+    if (eth_proto == ETH_P_MPLS_UC ||
+        eth_proto == ETH_P_MPLS_MC) {
+        mpls_h.value = ntohl(*((uint32_t *)get_mpls_hdr(skb)));
+        value_to_swap = ntohl(mpls->mpls_lse);
+        printk("\nmpls_h.value %x %x\n", mpls_h.value, mpls->mpls_lse);
+
+        if ((value_to_swap & 0xfffff000) >> 12)
+            mpls_h.label = (value_to_swap & 0xfffff000) >> 12;
+        
+        if ((value_to_swap & 0x00000e00) >> 9)
+            mpls_h.tc = (value_to_swap & 0x00000e00) >> 9;
+
+        if ((value_to_swap & 0x000000ff) >> 0)
+            mpls_h.ttl = (value_to_swap & 0x000000ff) >> 0;
+
+        printk("\nval %x, lbl %x ttl %x tc %x", mpls_h.value, mpls_h.label, mpls_h.ttl, mpls_h.tc);
+
+        *((uint32_t *)get_mpls_hdr(skb)) = htonl(mpls_h.value);
+    }
+
+    return 0;
+}
+
+static int 
+dec_mpls_ttl (struct sk_buff *skb)
+{
+    mpls_hdr mpls_h;
+    uint16_t eth_proto;
+    uint32_t value_to_swap;
+
+    eth_proto = htons(get_ethertype(skb));
+    printk("dec_mpls_ttl %x", eth_proto);
+
+    if (eth_proto == ETH_P_MPLS_UC ||
+        eth_proto == ETH_P_MPLS_MC) {
+        mpls_h.value = ntohl(*((uint32_t *)get_mpls_hdr(skb)));
+        mpls_h.ttl = mpls_h.ttl - 1;
+        printk("\nmpls_h.value %x %x\n", mpls_h.value, mpls_h.ttl);
+        value_to_swap = htonl(mpls_h.value);
+        printk("\nvalue_to_swap %x\n", value_to_swap);
+        *((uint32_t *)get_mpls_hdr(skb)) = value_to_swap;
+    }
+
+    return 0;
+}
+
+static int
+copy_mpls_ttl_in (struct sk_buff *skb)
+{
+    mpls_hdr mpls_h;
+    struct iphdr *nh;
+    uint8_t new_ttl;
+    uint16_t eth_proto;
+
+    eth_proto = htons(get_ethertype(skb));
+
+    printk("copy_ttl_in %x", eth_proto);
+    if (eth_proto == ETH_P_MPLS_UC ||
+        eth_proto == ETH_P_MPLS_MC) {
+        mpls_h.value = ntohl(*((uint32_t *)get_mpls_hdr(skb)));
+        printk("\nmpls_h.value %x %x\n", mpls_h.value, mpls_h.ttl);
+        new_ttl =  mpls_h.ttl;
+        nh = ip_hdr(skb);
+
+        printk("\nip ttl %d\n", nh->ttl);
+        csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8));
+	nh->ttl = new_ttl;
+    }
+    return 0;
+}
+
+static int
+copy_mpls_ttl_out (struct sk_buff *skb)
+{
+    mpls_hdr mpls_h;
+    struct iphdr *nh;
+    uint32_t value_to_swap;
+    uint16_t eth_proto;
+
+    eth_proto = htons(get_ethertype(skb));
+
+    if (eth_proto == ETH_P_MPLS_UC ||
+        eth_proto == ETH_P_MPLS_MC) {
+        nh = ip_hdr(skb);
+        mpls_h.value = ntohl(*((uint32_t *)get_mpls_hdr(skb)));
+        printk("\nmpls_h.value %x %x\n", mpls_h.value, mpls_h.ttl);
+        mpls_h.ttl = nh->ttl;
+        value_to_swap = htonl(mpls_h.value);
+        printk("\nmpls_h.value %x\n", value_to_swap);
+        *((uint32_t *)get_mpls_hdr(skb)) = value_to_swap;
+    }
+    return 0;
+}
+
 static int set_eth_addr(struct sk_buff *skb,
 			const struct ovs_key_ethernet *eth_key)
 {
@@ -385,6 +662,42 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 			err = pop_vlan(skb);
 			break;
 
+                case OVS_ACTION_ATTR_PUSH_MPLS:
+			err = push_mpls(skb, nla_data(a));
+			if (unlikely(err)) /* skb already freed. */
+                            return err;
+			break;
+
+                case OVS_ACTION_ATTR_POP_MPLS:
+			err = pop_mpls(skb, nla_data(a));
+			if (unlikely(err)) /* skb already freed. */
+                            return err;
+			break;
+
+                case OVS_ACTION_ATTR_SET_MPLS_LABEL:
+			err = set_mpls_label(skb, nla_data(a));
+			if (unlikely(err)) /* skb already freed. */
+                            return err;
+			break;
+
+                case OVS_ACTION_ATTR_DEC_MPLS_TTL:
+                        err = dec_mpls_ttl(skb);
+			if (unlikely(err)) /* skb already freed. */
+                            return err;
+                        break;
+
+                case OVS_ACTION_ATTR_COPY_TTL_IN:
+                        err = copy_mpls_ttl_in(skb);
+			if (unlikely(err)) /* skb already freed. */
+                            return err;
+                        break;
+
+                case OVS_ACTION_ATTR_COPY_TTL_OUT:
+                        err = copy_mpls_ttl_out(skb);
+			if (unlikely(err)) /* skb already freed. */
+                            return err;
+                        break;
+
 		case OVS_ACTION_ATTR_SET:
 			err = execute_set_action(skb, nla_data(a));
 			break;
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 220c7dd..df8f3a3 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -638,10 +638,18 @@ static int validate_actions(const struct nlattr *attr,
 			[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
 			[OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
 			[OVS_ACTION_ATTR_POP_VLAN] = 0,
+			[OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_mpls),
+			[OVS_ACTION_ATTR_POP_MPLS] = sizeof(struct ovs_action_mpls),
+			[OVS_ACTION_ATTR_SET_MPLS_LABEL] = sizeof(struct ovs_action_set_mpls_label),
+			[OVS_ACTION_ATTR_DEC_MPLS_TTL] = 0, 
+			[OVS_ACTION_ATTR_COPY_TTL_IN] = 0, 
+			[OVS_ACTION_ATTR_COPY_TTL_OUT] = 0, 
 			[OVS_ACTION_ATTR_SET] = (u32)-1,
 			[OVS_ACTION_ATTR_SAMPLE] = (u32)-1
 		};
-		const struct ovs_action_push_vlan *vlan;
+                const struct ovs_action_push_vlan *vlan;
+                const struct ovs_action_mpls *push_mpls;
+                const struct ovs_action_set_mpls_label *mpls_label;
 		int type = nla_type(a);
 
 		if (type > OVS_ACTION_ATTR_MAX ||
@@ -676,6 +684,29 @@ static int validate_actions(const struct nlattr *attr,
 				return -EINVAL;
 			break;
 
+                case OVS_ACTION_ATTR_PUSH_MPLS:
+                        push_mpls = nla_data(a);
+                        if (push_mpls->ethertype != htons(ETH_P_MPLS_UC) ||
+                            push_mpls->ethertype != htons(ETH_P_MPLS_MC))
+                            return -EINVAL;
+                        break;
+
+                case OVS_ACTION_ATTR_POP_MPLS:
+                        break;
+
+                case OVS_ACTION_ATTR_SET_MPLS_LABEL:
+                        mpls_label = nla_data(a);
+                        if (mpls_label->mpls_lse == htonl(0)) {
+                            printk("\nmpls_lse is %x\n", mpls_label->mpls_lse);
+                            return -EINVAL;
+                        }
+                        break;
+
+                case OVS_ACTION_ATTR_DEC_MPLS_TTL:
+                case OVS_ACTION_ATTR_COPY_TTL_IN:
+                case OVS_ACTION_ATTR_COPY_TTL_OUT:
+                        break;
+
 		case OVS_ACTION_ATTR_SET:
 			err = validate_set(a, key);
 			if (err)
diff --git a/datapath/flow.c b/datapath/flow.c
index 823c6b5..f4d0a96 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -480,6 +480,29 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 	return 0;
 }
 
+static int 
+parse_mpls(struct sk_buff *skb, struct sw_flow_key *key)
+{
+	struct mtag_prefix {
+		__be32 mpls_lse;
+	};
+	struct mtag_prefix *mp;
+
+	if (unlikely(skb->len < sizeof(struct mtag_prefix) + sizeof(__be32)))
+		return 0;
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct mtag_prefix) +
+					 sizeof(__be32))))
+		return -ENOMEM;
+
+	mp = (struct mtag_prefix *) skb->data;
+	key->mpls.mpls_lse = mp->mpls_lse;
+	__skb_pull(skb, sizeof(struct mtag_prefix));
+
+        printk("\nrkk-kernel %x\n", key->mpls.mpls_lse);
+	return 0;
+}
+
 static __be16 parse_ethertype(struct sk_buff *skb)
 {
 	struct llc_snap_hdr {
@@ -650,6 +673,11 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 		if (unlikely(parse_vlan(skb, key)))
 			return -ENOMEM;
 
+	if (eth->h_proto == htons(ETH_P_MPLS_UC) ||
+            eth->h_proto == htons(ETH_P_MPLS_MC))
+		if (unlikely(parse_mpls(skb, key)))
+			return -ENOMEM;
+
 	key->eth.type = parse_ethertype(skb);
 	if (unlikely(key->eth.type == htons(0)))
 		return -ENOMEM;
diff --git a/datapath/flow.h b/datapath/flow.h
index 61310d0..2eaa1b3 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -52,6 +52,10 @@ struct sw_flow_key {
 		__be16 tci;		/* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
 		__be16 type;		/* Ethernet frame type. */
 	} eth;
+        struct {
+                __be32 mpls_lse;        /* 0 if no MPLS, otherwise MPLS Label Stack entry */
+		__be16 type;		/* Ethernet frame type. */
+        } mpls;
 	struct {
 		u8     proto;		/* IP protocol or lower 8 bits of ARP opcode. */
 		u8     tos;		/* IP ToS. */
@@ -126,6 +130,19 @@ struct arp_eth_header {
 	unsigned char       ar_tip[4];		/* target IP address        */
 } __packed;
 
+#define MPLS_HLEN 4
+union mpls_header {
+    struct {
+        uint8_t	       ttl:8;
+        uint8_t	       s:1;
+        uint8_t	       tc:3;
+        uint32_t       label:20;
+    };
+    uint32_t value;
+} __packed;
+
+typedef union mpls_header mpls_hdr;
+
 int ovs_flow_init(void);
 void ovs_flow_exit(void);
 
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 0578b5f..6b51e1b 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -452,6 +452,25 @@ struct ovs_action_push_vlan {
 	__be16 vlan_tci;	/* 802.1Q TCI (VLAN ID and priority). */
 };
 
+/** 
+ *  struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.
+ *  @ethertype: MPLS Ethertype
+ */
+struct ovs_action_mpls {
+	__be16 ethertype;	/* Ethertype */
+};
+
+/** 
+ *  struct ovs_action_set_mpls_label - %OVS_ACTION_ATTR_SET_MPLS_LABEL 
+ *  action argument.
+ *  @mpls_label: MPLS Label (20 bits valid)
+ *  @mpls_tc:    MPLS Traffic Class (3 bits valid)
+ *  @mpls_ttl:   MPLS TTL (8 bits valid)
+ */
+struct ovs_action_set_mpls_label {
+	__be32 mpls_lse;	/* MPLS LABEL Stack Entry */
+};
+
 /**
  * enum ovs_action_attr - Action types.
  *
@@ -478,7 +497,13 @@ enum ovs_action_attr {
 	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_POP_VLAN,     /* No argument. */
+        OVS_ACTION_ATTR_PUSH_MPLS,      /* struct ovs_action_push_mpls */
+        OVS_ACTION_ATTR_POP_MPLS,       /* struct ovs_action_pop_mpls */
+        OVS_ACTION_ATTR_SET_MPLS_LABEL, /* struct ovs_action_set_mpls_label */
+        OVS_ACTION_ATTR_DEC_MPLS_TTL,   /* No argument */
+        OVS_ACTION_ATTR_COPY_TTL_OUT,   /* No argument */
+        OVS_ACTION_ATTR_COPY_TTL_IN,    /* No argument */
 	OVS_ACTION_ATTR_SAMPLE,       /* Nested OVS_SAMPLE_ATTR_*. */
 	__OVS_ACTION_ATTR_MAX
 };
diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h
index a707087..a2e0431 100644
--- a/include/openflow/openflow.h
+++ b/include/openflow/openflow.h
@@ -327,6 +327,15 @@ enum ofp_action_type {
     OFPAT_SET_TP_SRC,       /* TCP/UDP source port. */
     OFPAT_SET_TP_DST,       /* TCP/UDP destination port. */
     OFPAT_ENQUEUE,          /* Output to queue. */
+                            /* MPLS related definitions*/
+    OFPAT_COPY_TTL_OUT,     /* Copy TTL outwards */
+    OFPAT_COPY_TTL_IN,      /* Copy TTL inwards */
+    OFPAT_SET_MPLS_LABEL,   /* MPLS label */
+    OFPAT_SET_MPLS_TC,      /* MPLS TC */
+    OFPAT_SET_MPLS_TTL,     /* MPLS TTL */
+    OFPAT_DEC_MPLS_TTL,     /* Decrement MPLS TTL */
+    OFPAT_PUSH_MPLS,        /* Push a new MPLS tag */
+    OFPAT_POP_MPLS,         /* Pop the outer MPLS tag */
     OFPAT_VENDOR = 0xffff
 };
 
@@ -409,6 +418,50 @@ struct ofp_action_vendor_header {
 };
 OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8);
 
+/* Action structure for OFPAT_SET_MPLS_LABEL. */
+struct ofp_action_mpls_label {
+    ovs_be16 type;                  /* OFPAT_SET_MPLS_LABEL. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be32 mpls_label;            /* MPLS label */
+};
+OFP_ASSERT(sizeof(struct ofp_action_mpls_label) == 8);
+
+/* Action structure for OFPAT_SET_MPLS_TC. */
+struct ofp_action_mpls_tc {
+    ovs_be16 type;                  /* OFPAT_SET_MPLS_TC. */
+    ovs_be16 len;                   /* Length is 8. */
+    uint8_t  mpls_tc;                /* MPLS TC */
+    uint8_t  pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_mpls_tc) == 8);
+
+/* Action structure for OFPAT_SET_MPLS_TTL. */
+struct ofp_action_mpls_ttl {
+    ovs_be16 type;                  /* OFPAT_SET_MPLS_TTL. */
+    ovs_be16 len;                   /* Length is 8. */
+    uint8_t  mpls_ttl;               /* MPLS TTL */
+    uint8_t  pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_mpls_ttl) == 8);
+
+/* Action structure for OFPAT_PUSH_MPLS. */
+struct ofp_action_push_mpls {
+    ovs_be16 type;                  /* OFPAT_PUSH_MPLS. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be16 ethertype;              /* Ethertype */
+    uint8_t  pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_push_mpls) == 8);
+
+/* Action structure for OFPAT_POP_MPLS. */
+struct ofp_action_pop_mpls {
+    ovs_be16 type;                  /* OFPAT_POP_MPLS. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be16 ethertype;              /* Ethertype */
+    uint8_t  pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_pop_mpls) == 8);
+
 /* Action header that is common to all actions.  The length includes the
  * header and any padding used to make the action 64-bit aligned.
  * NB: The length of an action *must* always be a multiple of eight. */
@@ -439,6 +492,11 @@ union ofp_action {
     struct ofp_action_header header;
     struct ofp_action_vendor_header vendor;
     struct ofp_action_output output;
+    struct ofp_action_push_mpls push_mpls;
+    struct ofp_action_pop_mpls pop_mpls;
+    struct ofp_action_mpls_label mpls_label;
+    struct ofp_action_mpls_tc mpls_tc;
+    struct ofp_action_mpls_ttl mpls_ttl;
     struct ofp_action_vlan_vid vlan_vid;
     struct ofp_action_vlan_pcp vlan_pcp;
     struct ofp_action_nw_addr nw_addr;
@@ -524,6 +582,16 @@ enum ofp_flow_wildcards {
  */
 #define OFP_VLAN_NONE      0xffff
 
+/** The MPLS label is 20-bits, so we can use the entire 24/32 bits to indicate
+  * special conditions. All ones indicate no mpls tag is set
+   */
+enum ofp_mpls_label {
+    OFP_MPLS_ANY  = 0xfffffffe, /* Indicate that a MPLS label is set but don't care
+                              about it's value. Note: only valid when
+                              specifying the MPLS tag in a match */
+    OFP_MPLS_NONE = 0xffffffff, /* No MPLS tag was set. */
+};
+
 /* Fields to match against flows */
 struct ofp_match {
     ovs_be32 wildcards;        /* Wildcard fields. */
diff --git a/lib/classifier.c b/lib/classifier.c
index 18958a8..fd1da67 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -491,7 +491,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     if (rule->priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%d,", rule->priority);
@@ -533,6 +533,9 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
             }
         } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_cstr(s, "arp,");
+        } else if (f->dl_type == htons(ETH_TYPE_MPLS) ||
+                   f->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+            ds_put_cstr(s, "mpls,");
         } else {
             skip_type = false;
         }
@@ -1164,7 +1167,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index e1dc725..faf3ff0 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1205,6 +1205,8 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
 
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
         const struct ovs_action_push_vlan *vlan;
+        const struct ovs_action_mpls *mpls;
+        const struct ovs_action_set_mpls_label *mpls_label;
         int type = nl_attr_type(a);
 
         switch ((enum ovs_action_attr) type) {
@@ -1225,6 +1227,33 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
             eth_pop_vlan(packet);
             break;
 
+        case OVS_ACTION_ATTR_PUSH_MPLS:
+             mpls = nl_attr_get(a);
+             push_mpls(packet, mpls->ethertype);
+             break;
+
+        case OVS_ACTION_ATTR_POP_MPLS:
+             mpls = nl_attr_get(a);
+             pop_mpls(packet, mpls->ethertype);
+             break;
+
+        case OVS_ACTION_ATTR_SET_MPLS_LABEL:
+             mpls_label = nl_attr_get(a);
+             set_mpls_label(packet, mpls_label->mpls_lse);
+             break;
+
+         case OVS_ACTION_ATTR_DEC_MPLS_TTL:
+             dec_mpls_ttl(packet);
+             break;
+ 
+        case OVS_ACTION_ATTR_COPY_TTL_IN:
+             copy_mpls_ttl_in(packet);
+             break;
+
+        case OVS_ACTION_ATTR_COPY_TTL_OUT:
+             copy_mpls_ttl_out(packet);
+             break;
+
         case OVS_ACTION_ATTR_SET:
             execute_set_action(packet, nl_attr_get(a));
             break;
diff --git a/lib/flow.c b/lib/flow.c
index 778e84d..0f19746 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -102,6 +102,19 @@ parse_vlan(struct ofpbuf *b, struct flow *flow)
     }
 }
 
+static void
+parse_mpls (struct ofpbuf *b, struct flow *flow)
+{
+    struct mpls_prefix {
+        ovs_be32 mpls_lse;
+    };
+
+    if (b->size >= sizeof(struct mpls_prefix) + sizeof(ovs_be32)) {
+        struct mpls_prefix *mp = ofpbuf_pull(b, sizeof *mp);
+        flow->mpls_lse = mp->mpls_lse;
+    }
+}
+
 static ovs_be16
 parse_ethertype(struct ofpbuf *b)
 {
@@ -343,10 +356,11 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
     flow->in_port = ofp_in_port;
     flow->skb_priority = skb_priority;
 
-    packet->l2 = b.data;
-    packet->l3 = NULL;
-    packet->l4 = NULL;
-    packet->l7 = NULL;
+    packet->l2   = b.data;
+    packet->l2_5 = NULL;
+    packet->l3   = NULL;
+    packet->l4   = NULL;
+    packet->l7   = NULL;
 
     if (b.size < sizeof *eth) {
         return;
@@ -359,11 +373,43 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
 
     /* dl_type, vlan_tci. */
     ofpbuf_pull(&b, ETH_ADDR_LEN * 2);
-    if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
+    if ((eth->eth_type == htons(ETH_TYPE_VLAN_8021q)) ||
+       (eth->eth_type == htons(ETH_TYPE_VLAN_8021ad))) {
         parse_vlan(&b, flow);
+
+        /* skip over any other vlan tags */
+        while (true) {
+            uint16_t next_ethtype = *((uint16_t *)b.data);
+            if ((next_ethtype == htons(ETH_TYPE_VLAN_8021q)) ||
+                (next_ethtype == htons(ETH_TYPE_VLAN_8021ad))) {
+                ofpbuf_pull(&b, sizeof(struct vlan_header));
+            } else {
+                break;
+            }
+        }
     }
+
     flow->dl_type = parse_ethertype(&b);
 
+    if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+        flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+        VLOG_DBG("lib-flow.c, rkk-2 %x %x", flow->mpls_lse, flow->dl_type);
+        packet->l2_5 = (uint32_t *)b.data;
+        flow->mpls_lse = *((uint32_t *)packet->l2_5);
+        while (true) {
+            struct mpls_hdr *mh = b.data;
+            if (ntohl(mh->mpls_lse) & MPLS_STACK_MASK) {
+                ofpbuf_pull(&b, sizeof(struct mpls_hdr));
+                break;
+            } else {
+                ofpbuf_pull(&b, sizeof(struct mpls_hdr));
+            }
+        }
+    } else {
+        /* flow->mpls_lse = htonl(OFP_MPLS_NONE); */
+        flow->mpls_lse = htonl(0);
+    }
+
     /* Network layer. */
     packet->l3 = b.data;
     if (flow->dl_type == htons(ETH_TYPE_IP)) {
@@ -444,7 +490,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         flow->regs[i] &= wildcards->reg_masks[i];
@@ -548,6 +594,17 @@ flow_format(struct ds *ds, const struct flow *flow)
                   ETH_ADDR_ARGS(flow->dl_dst),
                   ntohs(flow->dl_type));
 
+    ds_put_format(ds, ":mpls(");
+    if (flow->mpls_lse) {
+        ds_put_format(ds, "label:%"PRIx32",tc:%d,ttl:%d",
+                      mpls_lse_to_label(flow->mpls_lse),
+                      mpls_lse_to_tc(flow->mpls_lse),
+                      mpls_lse_to_ttl(flow->mpls_lse));
+    } else {
+        ds_put_char(ds, '0');
+    }
+    ds_put_char(ds, ')');
+
     if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         ds_put_format(ds, " label:%#"PRIx32" proto:%"PRIu8" tos:%#"PRIx8
                           " ttl:%"PRIu8" ipv6(",
@@ -594,7 +651,7 @@ flow_print(FILE *stream, const struct flow *flow)
 void
 flow_wildcards_init_catchall(struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     wc->wildcards = FWW_ALL;
     wc->tun_id_mask = htonll(0);
@@ -615,7 +672,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
 void
 flow_wildcards_init_exact(struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     wc->wildcards = 0;
     wc->tun_id_mask = htonll(UINT64_MAX);
@@ -638,7 +695,7 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     if (wc->wildcards
         || wc->tun_id_mask != htonll(UINT64_MAX)
@@ -669,7 +726,7 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     if (wc->wildcards != FWW_ALL
         || wc->tun_id_mask != htonll(0)
@@ -703,7 +760,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     dst->wildcards = src1->wildcards | src2->wildcards;
     dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
@@ -740,7 +797,7 @@ flow_wildcards_equal(const struct flow_wildcards *a,
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     if (a->wildcards != b->wildcards
         || a->tun_id_mask != b->tun_id_mask
@@ -772,7 +829,7 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
     int i;
     struct in6_addr ipv6_masked;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
@@ -895,6 +952,7 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
         };
         ovs_be16 eth_type;
         ovs_be16 vlan_tci;
+        ovs_be32 mpls_lse;
         ovs_be16 tp_port;
         uint8_t eth_addr[ETH_ADDR_LEN];
         uint8_t ip_proto;
@@ -907,6 +965,7 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
         fields.eth_addr[i] = flow->dl_src[i] ^ flow->dl_dst[i];
     }
     fields.vlan_tci = flow->vlan_tci & htons(VLAN_VID_MASK);
+    fields.mpls_lse = flow->mpls_lse & htonl(MPLS_LABEL_MASK);
     fields.eth_type = flow->dl_type;
 
     /* UDP source and destination port are not taken into account because they
@@ -1007,6 +1066,33 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
     flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
 }
 
+/* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted 
+ * as an OpenFlow 1.0 "mpls_label" value:
+ */
+void
+flow_set_mpls_label(struct flow *flow, ovs_be32 label)
+{
+    if (label == htonl(OFP_MPLS_NONE)) {
+        flow->mpls_lse = htonl(0);
+    } else {
+        uint32_t temp = (ntohl(label) << MPLS_LABEL_SHIFT);
+        label = htonl(temp & MPLS_LABEL_MASK);
+        flow->mpls_lse &= ~htonl(MPLS_LABEL_MASK);
+        flow->mpls_lse |= label;
+    }
+}
+
+/* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
+ * range 0...7.
+ */
+void
+flow_set_mpls_tc(struct flow *flow, uint8_t tc)
+{
+    tc &= 0x07;
+    flow->mpls_lse &= ~htonl(MPLS_TC_MASK);
+    flow->mpls_lse |= htonl(tc << MPLS_TC_SHIFT);
+}
+
 /* Puts into 'b' a packet that flow_extract() would parse as having the given
  * 'flow'.
  *
diff --git a/lib/flow.h b/lib/flow.h
index 7b001a6..b82e690 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -35,7 +35,7 @@ struct ofpbuf;
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 8
+#define FLOW_WC_SEQ 9
 
 #define FLOW_N_REGS 5
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -62,6 +62,7 @@ struct flow {
     ovs_be32 nw_src;            /* IPv4 source address. */
     ovs_be32 nw_dst;            /* IPv4 destination address. */
     ovs_be32 ipv6_label;        /* IPv6 flow label. */
+    ovs_be32 mpls_lse;          /* MPLS label stack entry*/
     uint16_t in_port;           /* OpenFlow port number of input port. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
@@ -75,7 +76,7 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     uint8_t nw_ttl;             /* IP TTL/Hop Limit. */
     uint8_t nw_frag;            /* FLOW_FRAG_* flags. */
-    uint8_t reserved[6];        /* Reserved for 64-bit packing. */
+    uint8_t reserved[2];        /* Reserved for 64-bit packing. */
 };
 
 /* Represents the metadata fields of struct flow.  The masks are used to
@@ -93,14 +94,14 @@ struct flow_metadata {
 
 /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
  * flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (110 + FLOW_N_REGS * 4)
-#define FLOW_PAD_SIZE 6
+#define FLOW_SIG_SIZE (114 + FLOW_N_REGS * 4)
+#define FLOW_PAD_SIZE 2
 BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) == FLOW_SIG_SIZE - 1);
 BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_frag) == 1);
 BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 130 && FLOW_WC_SEQ == 8);
+BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 134 && FLOW_WC_SEQ == 9);
 
 void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
                   uint16_t in_port, struct flow *);
@@ -117,6 +118,9 @@ static inline size_t flow_hash(const struct flow *, uint32_t basis);
 void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
 void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
 
+void flow_set_mpls_label(struct flow *flow, ovs_be32 label);
+void flow_set_mpls_tc(struct flow *flow, uint8_t tc);
+
 void flow_compose(struct ofpbuf *, const struct flow *);
 
 static inline int
@@ -165,7 +169,7 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
 #define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 13)) - 1))
 
 /* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
-BUILD_ASSERT_DECL(FWW_ALL == ((1 << 13) - 1) && FLOW_WC_SEQ == 8);
+BUILD_ASSERT_DECL(FWW_ALL == ((1 << 13) - 1) && FLOW_WC_SEQ == 9);
 
 /* Information on wildcards for a flow, as a supplement to "struct flow".
  *
@@ -187,7 +191,7 @@ struct flow_wildcards {
 };
 
 /* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
-BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 8);
+BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 9);
 
 void flow_wildcards_init_catchall(struct flow_wildcards *);
 void flow_wildcards_init_exact(struct flow_wildcards *);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 41c65fb..3ead9b6 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -471,7 +471,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     /* Metadata. */
     if (!(wc & FWW_IN_PORT)) {
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 8fa3359..0c33710 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -74,6 +74,12 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_USERSPACE: return -2;
     case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
     case OVS_ACTION_ATTR_POP_VLAN: return 0;
+    case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_mpls);
+    case OVS_ACTION_ATTR_POP_MPLS:  return sizeof(struct ovs_action_mpls);
+    case OVS_ACTION_ATTR_SET_MPLS_LABEL: return sizeof(struct ovs_action_set_mpls_label);
+    case OVS_ACTION_ATTR_DEC_MPLS_TTL: return 0;
+    case OVS_ACTION_ATTR_COPY_TTL_IN: return 0;
+    case OVS_ACTION_ATTR_COPY_TTL_OUT: return 0;
     case OVS_ACTION_ATTR_SET: return -2;
     case OVS_ACTION_ATTR_SAMPLE: return -2;
 
@@ -214,11 +220,22 @@ format_vlan_tci(struct ds *ds, ovs_be16 vlan_tci)
 }
 
 static void
+format_mpls_label(struct ds *ds, ovs_be32 mpls_label)
+{
+    ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d",
+                  mpls_lse_to_label(mpls_label),
+                  mpls_lse_to_tc(mpls_label),
+                  mpls_lse_to_ttl(mpls_label));
+}
+
+static void
 format_odp_action(struct ds *ds, const struct nlattr *a)
 {
     int expected_len;
     enum ovs_action_attr type = nl_attr_type(a);
     const struct ovs_action_push_vlan *vlan;
+    const struct ovs_action_mpls *mpls;
+    const struct ovs_action_set_mpls_label *mpls_label;
 
     expected_len = odp_action_len(nl_attr_type(a));
     if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
@@ -252,6 +269,29 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     case OVS_ACTION_ATTR_POP_VLAN:
         ds_put_cstr(ds, "pop_vlan");
         break;
+   case OVS_ACTION_ATTR_PUSH_MPLS:
+        mpls = nl_attr_get(a);
+        ds_put_format(ds, "push_mpls(%"PRIx16")", ntohs(mpls->ethertype));
+        break;
+    case OVS_ACTION_ATTR_POP_MPLS:
+        mpls = nl_attr_get(a);
+        ds_put_format(ds, "pop_mpls(%"PRIx16")", ntohs(mpls->ethertype));
+        break;
+    case OVS_ACTION_ATTR_SET_MPLS_LABEL:
+        mpls_label = nl_attr_get(a);
+        ds_put_cstr(ds, "set_mpls_label(");
+        format_mpls_label(ds, mpls_label->mpls_lse);
+        ds_put_char(ds, ')');
+        break;
+    case OVS_ACTION_ATTR_DEC_MPLS_TTL:
+        ds_put_format(ds, "dec_mpls_ttl");
+        break;
+    case OVS_ACTION_ATTR_COPY_TTL_IN:
+        ds_put_format(ds, "copy_ttl_in");
+        break;
+    case OVS_ACTION_ATTR_COPY_TTL_OUT:
+        ds_put_format(ds, "copy_ttl_out");
+        break;
     case OVS_ACTION_ATTR_SAMPLE:
         format_odp_sample_action(ds, a);
         break;
@@ -419,6 +459,58 @@ parse_odp_action(const char *s, const struct shash *port_names,
     }
 
     {
+        struct ovs_action_set_mpls_label mpls;
+        int label, tc, ttl;
+        int n = -1;
+
+        if ((sscanf(s, "set_mpls_label(label=%i,tc=%i,ttl=%i)%n", 
+            &label, &tc, &ttl, &n) > 0 && n > 0)) {
+            mpls.mpls_lse = htonl((label << MPLS_LABEL_SHIFT) |
+                                    (tc << MPLS_TC_SHIFT) |
+                                    (ttl << MPLS_TTL_SHIFT));
+            nl_msg_put_unspec(actions, OVS_ACTION_ATTR_SET_MPLS_LABEL,
+                              &mpls, sizeof mpls);
+
+            return n;
+        }
+    }
+
+    {
+        struct ovs_action_mpls push;
+        int tpid = ETH_TYPE_MPLS;
+        int n = -1;
+
+        if ((sscanf(s, "push_mpls(tpid=%i)%n", &tpid, &n) > 0
+             && n > 0)) {
+            push.ethertype = htons(tpid);
+            nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_MPLS,
+                              &push, sizeof push);
+
+            return n;
+        }
+    }
+
+    if (!strncmp(s, "pop_mpls", 8)) {
+        nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_MPLS);
+        return 8;
+    }
+
+    if (!strncmp(s, "dec_mpls_ttl", 12)) {
+        nl_msg_put_flag(actions, OVS_ACTION_ATTR_DEC_MPLS_TTL);
+        return 12;
+    }
+
+    if (!strncmp(s, "copy_ttl_in", 11)) {
+        nl_msg_put_flag(actions, OVS_ACTION_ATTR_COPY_TTL_IN);
+        return 11;
+    }
+
+    if (!strncmp(s, "copy_ttl_out", 12)) {
+        nl_msg_put_flag(actions, OVS_ACTION_ATTR_COPY_TTL_OUT);
+        return 12;
+    }
+
+    {
         double percentage;
         int n = -1;
 
@@ -1695,6 +1787,13 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
                                   expected_attrs, flow, key, key_len);
     }
+
+    if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+        flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+    
+        VLOG_DBG("rkk-mpls-key %x", flow->mpls_lse);
+    }
+
     return parse_l3_onward(attrs, present_attrs, out_of_range_attr,
                            expected_attrs, flow, key, key_len);
 }
@@ -1863,6 +1962,43 @@ commit_set_ipv6_action(const struct flow *flow, struct flow *base,
 }
 
 static void
+commit_mpls_action(const struct flow *flow, struct flow *base,
+                   struct ofpbuf *odp_actions)
+{
+    if (base->dl_type == flow->dl_type) {
+        return;
+    }
+
+    {
+        struct ovs_action_mpls mpls;
+
+        mpls.ethertype = flow->dl_type;
+        nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+                          &mpls, sizeof mpls);
+        base->dl_type = flow->dl_type;
+    }
+}
+
+static void
+commit_mpls_label_action(const struct flow *flow, struct flow *base,
+                         struct ofpbuf *odp_actions)
+{
+    if (base->mpls_lse == flow->mpls_lse) {
+        return;
+    }
+
+    VLOG_DBG("rkk-4 %x %x", base->mpls_lse, flow->mpls_lse);
+    {
+        struct ovs_action_set_mpls_label mpls;
+
+        mpls.mpls_lse = flow->mpls_lse;
+        nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_MPLS_LABEL,
+                          &mpls, sizeof mpls);
+        base->mpls_lse = flow->mpls_lse;
+    }
+}
+
+static void
 commit_set_nw_action(const struct flow *flow, struct flow *base,
                      struct ofpbuf *odp_actions)
 {
@@ -1934,6 +2070,8 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
     commit_set_tun_id_action(flow, base, odp_actions);
     commit_set_ether_addr_action(flow, base, odp_actions);
     commit_vlan_action(flow, base, odp_actions);
+    commit_mpls_action(flow, base, odp_actions);
+    commit_mpls_label_action(flow, base, odp_actions);
     commit_set_nw_action(flow, base, odp_actions);
     commit_set_port_action(flow, base, odp_actions);
     commit_set_priority_action(flow, base, odp_actions);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index f96f817..a64dadd 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -258,6 +258,12 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     struct ofp_action_vlan_vid *oavv;
     struct ofp_action_nw_addr *oana;
     struct ofp_action_tp_port *oata;
+    struct ofp_action_mpls_label *oaml;
+    struct ofp_action_mpls_tc *oamtc;
+    struct ofp_action_mpls_ttl *oamttl;
+    struct ofp_action_push_mpls *oampush;
+    struct ofp_action_pop_mpls *oampop;
+
 
     switch (code) {
     case OFPUTIL_OFPAT_OUTPUT:
@@ -278,6 +284,43 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         ofputil_put_OFPAT_STRIP_VLAN(b);
         break;
 
+    case OFPUTIL_OFPAT_COPY_TTL_OUT:
+        ofputil_put_OFPAT_COPY_TTL_OUT(b);
+        break;
+
+    case OFPUTIL_OFPAT_COPY_TTL_IN:
+        ofputil_put_OFPAT_COPY_TTL_IN(b);
+        break;
+
+    case OFPUTIL_OFPAT_SET_MPLS_LABEL:
+        oaml = ofputil_put_OFPAT_SET_MPLS_LABEL(b);
+        oaml->mpls_label = htonl(str_to_u32(arg));
+        break;
+
+    case OFPUTIL_OFPAT_SET_MPLS_TC:
+        oamtc = ofputil_put_OFPAT_SET_MPLS_TC(b);
+        oamtc->mpls_tc = str_to_u32(arg);
+        break;
+
+    case OFPUTIL_OFPAT_SET_MPLS_TTL:
+        oamttl = ofputil_put_OFPAT_SET_MPLS_TTL(b);
+        oamttl->mpls_ttl = str_to_u32(arg);
+        break;
+
+    case OFPUTIL_OFPAT_DEC_MPLS_TTL:
+        ofputil_put_OFPAT_DEC_MPLS_TTL(b);
+        break;
+
+    case OFPUTIL_OFPAT_PUSH_MPLS:
+        oampush = ofputil_put_OFPAT_PUSH_MPLS(b);
+        oampush->ethertype = htons(str_to_u32(arg));
+        break;
+
+    case OFPUTIL_OFPAT_POP_MPLS:
+        oampop = ofputil_put_OFPAT_POP_MPLS(b);
+        oampop->ethertype = htons(str_to_u32(arg));
+        break;
+
     case OFPUTIL_OFPAT_SET_DL_SRC:
     case OFPUTIL_OFPAT_SET_DL_DST:
         oada = ofputil_put_action(code, b);
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 8df439d..927b45e 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -185,6 +185,9 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
 {
     const struct ofp_action_enqueue *oae;
     const struct ofp_action_dl_addr *oada;
+    const struct ofp_action_mpls_label *oaml;
+    const struct ofp_action_push_mpls *oampush;
+    const struct ofp_action_pop_mpls  *oampop;
     const struct nx_action_set_tunnel64 *nast64;
     const struct nx_action_set_tunnel *nast;
     const struct nx_action_set_queue *nasq;
@@ -234,6 +237,41 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
         ds_put_cstr(s, "strip_vlan");
         break;
 
+    case OFPUTIL_OFPAT_SET_MPLS_LABEL:
+        oaml = (const struct ofp_action_mpls_label *) a;
+        ds_put_format(s, "set_mpls_label:%"PRIu32, ntohl(oaml->mpls_label));
+        break;
+
+    case OFPUTIL_OFPAT_SET_MPLS_TC:
+        ds_put_format(s, "set_mpls_tc:%"PRIu8, a->mpls_tc.mpls_tc);
+        break;
+
+    case OFPUTIL_OFPAT_SET_MPLS_TTL:
+        ds_put_format(s, "set_mpls_ttl:%"PRIu8, a->mpls_ttl.mpls_ttl);
+        break;
+
+    case OFPUTIL_OFPAT_DEC_MPLS_TTL:
+        ds_put_cstr(s, "dec_mpls_ttl");
+        break;
+
+    case OFPUTIL_OFPAT_COPY_TTL_IN:
+        ds_put_cstr(s, "copy_ttl_in");
+        break;
+
+    case OFPUTIL_OFPAT_COPY_TTL_OUT:
+        ds_put_cstr(s, "copy_ttl_out");
+        break;
+
+   case OFPUTIL_OFPAT_PUSH_MPLS:
+        oampush = (const struct ofp_action_push_mpls *) a;
+        ds_put_format(s, "push_mpls:%"PRIx16, ntohs(oampush->ethertype));
+        break;
+
+    case OFPUTIL_OFPAT_POP_MPLS:
+        oampop = (const struct ofp_action_pop_mpls *) a;
+        ds_put_format(s, "pop_mpls:%"PRIx16, ntohs(oampop->ethertype));
+        break;
+
     case OFPUTIL_OFPAT_SET_DL_SRC:
         oada = (const struct ofp_action_dl_addr *) a;
         ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
@@ -710,6 +748,9 @@ ofp_match_to_string(const struct ofp_match *om, int verbosity)
             }
         } else if (om->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_cstr(&f, "arp,");
+        } else if (om->dl_type == htons(ETH_TYPE_MPLS) ||
+                   om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+            ds_put_cstr(&f, "mpls,");
         } else {
             skip_type = false;
         }
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index e936693..15bc43f 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -100,7 +100,7 @@ static const flow_wildcards_t WC_INVARIANTS = 0
 void
 ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     /* Initialize most of rule->wc. */
     flow_wildcards_init_catchall(wc);
@@ -924,7 +924,7 @@ ofputil_min_flow_format(const struct cls_rule *rule)
 {
     const struct flow_wildcards *wc = &rule->wc;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
 
     /* Only NXM supports separately wildcards the Ethernet multicast bit. */
     if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
@@ -2500,6 +2500,14 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
         case OFPUTIL_OFPAT_SET_TP_DST:
         case OFPUTIL_OFPAT_SET_DL_SRC:
         case OFPUTIL_OFPAT_SET_DL_DST:
+        case OFPUTIL_OFPAT_COPY_TTL_OUT:
+        case OFPUTIL_OFPAT_COPY_TTL_IN:
+        case OFPUTIL_OFPAT_SET_MPLS_LABEL:
+        case OFPUTIL_OFPAT_SET_MPLS_TC:
+        case OFPUTIL_OFPAT_SET_MPLS_TTL:
+        case OFPUTIL_OFPAT_DEC_MPLS_TTL:
+        case OFPUTIL_OFPAT_PUSH_MPLS:
+        case OFPUTIL_OFPAT_POP_MPLS:
         case OFPUTIL_NXAST_RESUBMIT:
         case OFPUTIL_NXAST_SET_TUNNEL:
         case OFPUTIL_NXAST_SET_QUEUE:
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index d05ec9d..2a64122 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -3,18 +3,26 @@
 #ifndef OFPAT_ACTION
 #define OFPAT_ACTION(ENUM, STRUCT, NAME)
 #endif
-OFPAT_ACTION(OFPAT_OUTPUT,       ofp_action_output,   "output")
-OFPAT_ACTION(OFPAT_SET_VLAN_VID, ofp_action_vlan_vid, "mod_vlan_vid")
-OFPAT_ACTION(OFPAT_SET_VLAN_PCP, ofp_action_vlan_pcp, "mod_vlan_pcp")
-OFPAT_ACTION(OFPAT_STRIP_VLAN,   ofp_action_header,   "strip_vlan")
-OFPAT_ACTION(OFPAT_SET_DL_SRC,   ofp_action_dl_addr,  "mod_dl_src")
-OFPAT_ACTION(OFPAT_SET_DL_DST,   ofp_action_dl_addr,  "mod_dl_dst")
-OFPAT_ACTION(OFPAT_SET_NW_SRC,   ofp_action_nw_addr,  "mod_nw_src")
-OFPAT_ACTION(OFPAT_SET_NW_DST,   ofp_action_nw_addr,  "mod_nw_dst")
-OFPAT_ACTION(OFPAT_SET_NW_TOS,   ofp_action_nw_tos,   "mod_nw_tos")
-OFPAT_ACTION(OFPAT_SET_TP_SRC,   ofp_action_tp_port,  "mod_tp_src")
-OFPAT_ACTION(OFPAT_SET_TP_DST,   ofp_action_tp_port,  "mod_tp_dst")
-OFPAT_ACTION(OFPAT_ENQUEUE,      ofp_action_enqueue,  "enqueue")
+OFPAT_ACTION(OFPAT_OUTPUT,       ofp_action_output,          "output")
+OFPAT_ACTION(OFPAT_SET_VLAN_VID, ofp_action_vlan_vid,        "mod_vlan_vid")
+OFPAT_ACTION(OFPAT_SET_VLAN_PCP, ofp_action_vlan_pcp,        "mod_vlan_pcp")
+OFPAT_ACTION(OFPAT_STRIP_VLAN,   ofp_action_header,          "strip_vlan")
+OFPAT_ACTION(OFPAT_SET_DL_SRC,   ofp_action_dl_addr,         "mod_dl_src")
+OFPAT_ACTION(OFPAT_SET_DL_DST,   ofp_action_dl_addr,         "mod_dl_dst")
+OFPAT_ACTION(OFPAT_SET_NW_SRC,   ofp_action_nw_addr,         "mod_nw_src")
+OFPAT_ACTION(OFPAT_SET_NW_DST,   ofp_action_nw_addr,         "mod_nw_dst")
+OFPAT_ACTION(OFPAT_SET_NW_TOS,   ofp_action_nw_tos,          "mod_nw_tos")
+OFPAT_ACTION(OFPAT_SET_TP_SRC,   ofp_action_tp_port,         "mod_tp_src")
+OFPAT_ACTION(OFPAT_SET_TP_DST,   ofp_action_tp_port,         "mod_tp_dst")
+OFPAT_ACTION(OFPAT_ENQUEUE,      ofp_action_enqueue,         "enqueue")
+OFPAT_ACTION(OFPAT_COPY_TTL_OUT,    ofp_action_header,       "copy_mpls_ttl_out")
+OFPAT_ACTION(OFPAT_COPY_TTL_IN,     ofp_action_header,       "copy_mpls_ttl_in")
+OFPAT_ACTION(OFPAT_SET_MPLS_LABEL,  ofp_action_mpls_label,   "set_mpls_label")
+OFPAT_ACTION(OFPAT_SET_MPLS_TC,     ofp_action_mpls_tc,      "set_mpls_tc")
+OFPAT_ACTION(OFPAT_SET_MPLS_TTL,    ofp_action_mpls_ttl,     "set_mpls_ttl")
+OFPAT_ACTION(OFPAT_DEC_MPLS_TTL,    ofp_action_header,       "dec_mpls_ttl")
+OFPAT_ACTION(OFPAT_PUSH_MPLS,       ofp_action_push_mpls,    "push_mpls")
+OFPAT_ACTION(OFPAT_POP_MPLS,        ofp_action_pop_mpls,     "pop_mpls")
 #undef OFPAT_ACTION
 
 #ifndef NXAST_ACTION
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 816532e..b63d3be 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -91,6 +91,20 @@ enum ofputil_msg_code {
     OFPUTIL_NXST_AGGREGATE_REPLY
 };
 
+/* MPLS related actions */
+#define OFP_ACTION_MPLS_TTL    0x1
+
+enum {
+    /* Decrement MPLS TTL action */
+    OFPA_DEC_MPLS_TTL_SHIFT,
+
+    /* Copy MPLS TTL IN action */
+    OFPA_COPY_MPLS_TTL_IN_SHIFT,
+
+    /* Copy MPLS TTL OUT action */
+    OFPA_COPY_MPLS_TTL_OUT_SHIFT
+};
+
 struct ofputil_msg_type;
 enum ofperr ofputil_decode_msg_type(const struct ofp_header *,
                                     const struct ofputil_msg_type **);
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 7f8338c..b4589c1 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -42,6 +42,7 @@ struct ofpbuf {
     size_t size;                /* Number of bytes in use. */
 
     void *l2;                   /* Link-level header. */
+    void *l2_5;                 /* MPLS label stack */
     void *l3;                   /* Network-level header. */
     void *l4;                   /* Transport-level header. */
     void *l7;                   /* Application data. */
diff --git a/lib/packets.c b/lib/packets.c
index eaf3e43..ef77e90 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -122,6 +122,251 @@ eth_pop_vlan(struct ofpbuf *packet)
     }
 }
 
+/* Set outermost MPLS ttl
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+static void
+set_mpls_lse_ttl(uint32_t *tag, uint8_t ttl)
+{
+    *tag &= ~htonl(MPLS_TTL_MASK << MPLS_TTL_SHIFT);
+    *tag |= htonl((ttl & MPLS_TTL_MASK) << MPLS_TTL_SHIFT);
+}
+
+/* Set MPLS TTL value to MPLS tag
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+set_mpls_ttl (struct ofpbuf *packet, uint8_t ttl) {
+    struct mpls_hdr *mh;
+    mh = packet->l2_5;
+
+    if (mh != NULL) {
+        set_mpls_lse_ttl(&mh->mpls_lse, ttl);
+    }
+}
+
+/* Decrement MPLS TTL from the packet.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+dec_mpls_ttl (struct ofpbuf *packet) {
+    struct mpls_hdr *mh;
+    mh = packet->l2_5;
+
+    if (mh != NULL) {
+        uint8_t ttl = (ntohl(mh->mpls_lse) >> MPLS_TTL_SHIFT) & MPLS_TTL_MASK;
+        set_mpls_lse_ttl(&mh->mpls_lse, ttl-1);
+    }
+}
+
+/* Copy MPLS TTL from the packet.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+copy_mpls_ttl_in (struct ofpbuf *packet) {
+    struct mpls_hdr *mh;
+    struct ip_header *nh;
+
+    mh = packet->l2_5;
+    nh = packet->l3;
+    if (mh != NULL) {
+        uint8_t ttl = (ntohl(mh->mpls_lse) >> MPLS_TTL_SHIFT) & MPLS_TTL_MASK;
+        /* if bottom of the stack */
+        if(ntohl(mh->mpls_lse) & (MPLS_STACK_MASK << MPLS_STACK_SHIFT)) {
+            /* change the ip checksum */
+            uint16_t field = (uint16_t) nh->ip_ttl;
+            nh->ip_csum = recalc_csum16(nh->ip_csum, field, ((uint16_t)ttl));
+            nh->ip_ttl = ttl;
+        } else {
+            struct mpls_hdr *mh2;
+            mh2 = (struct mpls_hdr *)((char *) packet->l2_5 + sizeof mh);
+            set_mpls_lse_ttl(&mh2->mpls_lse, ttl);
+        }
+    }
+}
+
+/* Copy MPLS TTL to the packet.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+copy_mpls_ttl_out (struct ofpbuf *packet) {
+    struct mpls_hdr *mh;
+    struct ip_header *nh;
+
+    mh = packet->l2_5;
+    nh = packet->l3;
+
+    if (mh != NULL) {
+        /* if bottom of the stack */
+        if (ntohl(mh->mpls_lse) & (MPLS_STACK_MASK << MPLS_STACK_SHIFT)) {
+            set_mpls_lse_ttl(&mh->mpls_lse, nh->ip_ttl);
+        } else {
+            struct mpls_hdr *mh2;
+            uint8_t ttl;
+            mh2 = (struct mpls_hdr *)((char *) packet->l2_5 + sizeof mh);
+            ttl = (ntohl(mh2->mpls_lse) >> MPLS_TTL_SHIFT) & MPLS_TTL_MASK;
+            set_mpls_lse_ttl(&mh->mpls_lse, ttl);
+        }
+    }
+}
+
+/* set MPLS TC to the tag passes */
+static void
+set_mpls_lse_tc (uint32_t *tag, uint8_t tc)
+{
+    *tag &= ~htonl(MPLS_TC_MASK << MPLS_TC_SHIFT);
+    *tag |= htonl((tc & MPLS_TC_MASK) << MPLS_TC_SHIFT);
+}
+
+/* set MPLS TC to the packet.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+set_mpls_tc (struct ofpbuf *packet, uint8_t tc) {
+    struct mpls_hdr *mh;
+    mh = packet->l2_5;
+
+    if (mh != NULL) {
+        set_mpls_lse_tc(&mh->mpls_lse, tc);
+    }
+}
+
+/* set MPLS Tag label */
+static void
+set_mpls_lse_label (uint32_t *tag, ovs_be32 label)
+{
+    *tag &= ~htonl(MPLS_LABEL_MASK) << MPLS_LABEL_SHIFT;
+    *tag |= htonl((label & MPLS_LABEL_MASK)) << MPLS_LABEL_SHIFT;
+}
+
+/* Set MPLS Label to outermost MPLS label */
+void
+set_mpls_label (struct ofpbuf *packet, ovs_be32 label) {
+    struct mpls_hdr *mh;
+    mh = packet->l2_5;
+
+    if (mh != NULL) {
+        set_mpls_lse_label(&mh->mpls_lse, label);
+    }
+}
+
+/* Set MPLS tag stack */
+static void
+set_mpls_lse_stack (uint32_t *tag, uint8_t stack)
+{
+    *tag &= ~htonl(MPLS_STACK_MASK) << MPLS_STACK_SHIFT;
+    *tag |= htonl((stack & MPLS_STACK_MASK)) << MPLS_STACK_SHIFT;
+}
+
+/* Set MPLS label, MPLS TC, MPLS ttl and MPLS stack */
+static void
+set_mpls_lse_values (uint32_t *tag, uint8_t ttl, uint8_t stack, 
+                     uint8_t tc, uint32_t label)
+{
+    set_mpls_lse_ttl(tag, ttl);
+    set_mpls_lse_tc(tag, tc);
+    set_mpls_lse_label(tag, label);
+    set_mpls_lse_stack(tag, stack);
+}
+
+/* Push MPLS label onto packet */
+void
+push_mpls (struct ofpbuf *packet, uint16_t ethtype)
+{
+    struct mpls_eth_header *meh;
+    struct eth_header *eh;
+    struct ip_header *ih;
+
+    eh = packet->l2;
+    ih = packet->l3;
+
+    if (packet->size >= sizeof(struct eth_header)
+        && eh->eth_type == htons(ETH_TYPE_IP)) {
+        /* push and copy from ip header */
+        struct mpls_eth_header tmp;
+        memcpy(tmp.eth_dst, eh->eth_dst, ETH_ADDR_LEN);
+        memcpy(tmp.eth_src, eh->eth_src, ETH_ADDR_LEN);
+        tmp.eth_type = ethtype;
+        set_mpls_lse_values(&tmp.mpls_lse, ih->ip_ttl, 1, 0, 0);
+
+        meh = ofpbuf_push_uninit(packet, MPLS_TAG_LEN);
+        memcpy(meh, &tmp, sizeof tmp);
+        packet->l2 = (char*)packet->l2 - MPLS_TAG_LEN;
+        packet->l2_5 = (char*)packet->l2 + sizeof (struct eth_header);
+            
+    } else if (packet->size >= sizeof(struct eth_header) &&
+           (eh->eth_type == htons(ETH_TYPE_VLAN) ||
+            eh->eth_type == htons(ETH_TYPE_MPLS) ||
+            eh->eth_type == htons(ETH_TYPE_MPLS_MCAST))) {
+        /* push and copy from ip within vlan header */
+
+        struct mpls_hdr mh;
+        uint16_t *next_ethtype;
+        /* if there are no mpls tags already */
+        if(packet->l2_5 == NULL) {
+            next_ethtype = (uint16_t*)((char *)packet->l3 - 2);
+            if (*next_ethtype == ntohs(ETH_TYPE_IP)) {
+                *next_ethtype = ethtype;
+                set_mpls_lse_values(&mh.mpls_lse, 0, 1, 0, 0);
+                packet->l2_5 = packet->l3;
+            } else {
+                return;
+            }
+        } else {
+            /* copy the inner label and change the stack */
+            struct mpls_hdr *inner = packet->l2_5;
+            mh.mpls_lse = inner->mpls_lse;
+            set_mpls_lse_stack(&mh.mpls_lse, 0);
+        }
+
+        { 
+            char * header;
+            size_t len;
+            header = ofpbuf_push_uninit(packet, MPLS_TAG_LEN);
+            len = (char*)packet->l2_5 - (char*)packet->l2;
+            memmove(header, packet->l2, len);
+            memcpy((char*)header + len, &mh, sizeof mh);
+            packet->l2 = (char*)packet->l2 - MPLS_TAG_LEN;
+            packet->l2_5 = (char*)packet->l2_5 - MPLS_TAG_LEN;
+            /* copy ttl from the inner packet */
+            copy_mpls_ttl_out(packet);
+        }
+    } else {
+        /* DEFUALT CASE set 0 */
+        struct mpls_eth_header tmp;
+        memcpy(tmp.eth_dst, eh->eth_dst, ETH_ADDR_LEN);
+        memcpy(tmp.eth_src, eh->eth_src, ETH_ADDR_LEN);
+        tmp.eth_type = ethtype;
+        set_mpls_lse_values(&tmp.mpls_lse, 0, 1, 0, 0);
+
+        meh = ofpbuf_push_uninit(packet, MPLS_TAG_LEN);
+        memcpy(meh, &tmp, sizeof tmp);
+        packet->l2 = (char*)packet->l2 - MPLS_TAG_LEN;
+        packet->l2_5 = (char*)packet->l2 + sizeof (struct eth_header);
+    }
+}
+
+/* Pop outermost MPLS tag from packet */
+void
+pop_mpls (struct ofpbuf *packet, uint16_t ethtype)
+{
+    if(packet->l2_5 != NULL) {
+        struct mpls_hdr *mh;
+        size_t len;
+        mh = packet->l2_5;
+        len = (char*)packet->l2_5 - (char*)packet->l2;
+        
+        /* if bottom of the stack */
+        if(ntohl(mh->mpls_lse) & (MPLS_STACK_MASK << MPLS_STACK_SHIFT)) {
+            uint16_t *next_ethtype = (char *)packet->l2_5 - 2;
+            *next_ethtype = ethtype;
+            packet->l2_5 = NULL;
+        } else {
+            packet->l2_5 = (char*)packet->l2_5 + MPLS_TAG_LEN;
+        }
+
+        /* shift the l2 header forward */
+        memmove((char*)packet->data+ MPLS_TAG_LEN, packet->data, len);
+        packet->size -= MPLS_TAG_LEN;
+        packet->data = (char*)packet->data + MPLS_TAG_LEN;
+        packet->l2 = (char*)packet->l2 + MPLS_TAG_LEN;
+    }
+}
+
 /* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'.  The
  * caller must free '*packetp'.  On success, returns NULL.  On failure, returns
  * an error message and stores NULL in '*packetp'. */
diff --git a/lib/packets.h b/lib/packets.h
index 78ccfe9..59452f9 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -134,6 +134,15 @@ void compose_benign_packet(struct ofpbuf *, const char *tag,
 void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
 void eth_pop_vlan(struct ofpbuf *);
 
+void set_mpls_ttl(struct ofpbuf *, uint8_t ttl);
+void dec_mpls_ttl(struct ofpbuf *);
+void copy_mpls_ttl_in(struct ofpbuf *);
+void copy_mpls_ttl_out(struct ofpbuf *);
+void set_mpls_tc(struct ofpbuf *, uint8_t tc);
+void set_mpls_label(struct ofpbuf *, ovs_be32 label);
+void push_mpls(struct ofpbuf *, uint16_t ethtype);
+void pop_mpls(struct ofpbuf *, uint16_t ethtype);
+
 const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
 
 /* Example:
@@ -166,9 +175,13 @@ const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
 
 #define ETH_TYPE_IP            0x0800
 #define ETH_TYPE_ARP           0x0806
-#define ETH_TYPE_VLAN          0x8100
 #define ETH_TYPE_IPV6          0x86dd
 #define ETH_TYPE_LACP          0x8809
+#define ETH_TYPE_MPLS          0x8847
+#define ETH_TYPE_MPLS_MCAST    0x8848
+#define ETH_TYPE_VLAN_8021ad   0x88a8
+#define ETH_TYPE_VLAN_8021q    0x8100
+#define ETH_TYPE_VLAN          ETH_TYPE_VLAN_8021q
 
 /* Minimum value for an Ethernet type.  Values below this are IEEE 802.2 frame
  * lengths. */
@@ -256,6 +269,59 @@ struct vlan_eth_header {
 } __attribute__((packed));
 BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));
 
+/* MPLS related definitions */
+#define MPLS_TTL_MASK       0x000000ff
+#define MPLS_TTL_SHIFT      0
+
+#define MPLS_STACK_MASK     0x00000100
+#define MPLS_STACK_SHIFT    8
+
+#define MPLS_TC_MASK        0x00000e00
+#define MPLS_TC_SHIFT       9
+
+#define MPLS_LABEL_MASK     0xfffff000
+#define MPLS_LABEL_SHIFT    12
+
+#define MPLS_TAG_LEN        4
+
+struct mpls_hdr {
+    ovs_be32 mpls_lse;
+};
+BUILD_ASSERT_DECL(MPLS_TAG_LEN == sizeof(struct mpls_hdr));
+
+#define MPLS_ETH_HEADER_LEN (ETH_HEADER_LEN + MPLS_TAG_LEN)
+struct mpls_eth_header {
+    uint8_t  eth_dst[ETH_ADDR_LEN];
+    uint8_t  eth_src[ETH_ADDR_LEN];
+    ovs_be16 eth_type;         /* Always htons(ETH_TYPE_MPLS). */
+    ovs_be32 mpls_lse;
+} __attribute__((packed));
+BUILD_ASSERT_DECL(MPLS_ETH_HEADER_LEN == sizeof(struct mpls_eth_header));
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls label */
+static inline uint32_t
+mpls_lse_to_label(ovs_be32 mpls_lse)
+{
+    return (ntohl(mpls_lse) & MPLS_LABEL_MASK) >> MPLS_LABEL_SHIFT;
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls tc */
+static inline int
+mpls_lse_to_tc(ovs_be32 mpls_lse)
+{
+    return (ntohl(mpls_lse) & MPLS_TC_MASK) >> MPLS_TC_SHIFT;
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls ttl */
+static inline int
+mpls_lse_to_ttl(ovs_be32 mpls_lse)
+{
+    return (ntohl(mpls_lse) & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT;
+}
+
 /* The "(void) (ip)[0]" below has no effect on the value, since it's the first
  * argument of a comma expression, but it makes sure that 'ip' is a pointer.
  * This is useful since a common mistake is to pass an integer instead of a
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 8903a7f..f8b3e99 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -526,6 +526,9 @@ struct ofproto_dpif {
     /* VLAN splinters. */
     struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
     struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
+
+    /* MPLS ttl related actions */
+    uint8_t mpls_ttl;            /* Action dec_mpls_ttl */
 };
 
 /* Defer flow mod completion until "ovs-appctl ofproto/unclog"?  (Useful only
@@ -910,15 +913,23 @@ get_features(struct ofproto *ofproto_ OVS_UNUSED,
     *actions = ((1u << OFPAT_OUTPUT) |
                 (1u << OFPAT_SET_VLAN_VID) |
                 (1u << OFPAT_SET_VLAN_PCP) |
-                (1u << OFPAT_STRIP_VLAN) |
-                (1u << OFPAT_SET_DL_SRC) |
-                (1u << OFPAT_SET_DL_DST) |
-                (1u << OFPAT_SET_NW_SRC) |
-                (1u << OFPAT_SET_NW_DST) |
-                (1u << OFPAT_SET_NW_TOS) |
-                (1u << OFPAT_SET_TP_SRC) |
-                (1u << OFPAT_SET_TP_DST) |
-                (1u << OFPAT_ENQUEUE));
+                (1u << OFPAT_STRIP_VLAN)   |
+                (1u << OFPAT_SET_DL_SRC)   |
+                (1u << OFPAT_SET_DL_DST)   |
+                (1u << OFPAT_SET_NW_SRC)   |
+                (1u << OFPAT_SET_NW_DST)   |
+                (1u << OFPAT_SET_NW_TOS)   |
+                (1u << OFPAT_SET_TP_SRC)   |
+                (1u << OFPAT_SET_TP_DST)   |
+                (1u << OFPAT_ENQUEUE)      |
+                (1u << OFPAT_COPY_TTL_OUT) |
+                (1u << OFPAT_COPY_TTL_IN)  |
+                (1u << OFPAT_SET_MPLS_LABEL) |
+                (1u << OFPAT_SET_MPLS_TC)    |
+                (1u << OFPAT_SET_MPLS_TTL)   |
+                (1u << OFPAT_DEC_MPLS_TTL)   |
+                (1u << OFPAT_PUSH_MPLS)      |
+                (1u << OFPAT_POP_MPLS));
 }
 
 static void
@@ -3159,6 +3170,7 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
     struct ofpbuf key;
     int error;
 
+    VLOG_DBG("ofproto:rkk-2 %x %x", flow->mpls_lse, flow->dl_type);
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
     odp_flow_key_from_flow(&key, flow);
 
@@ -3215,6 +3227,8 @@ facet_account(struct facet *facet)
     const struct nlattr *a;
     unsigned int left;
     ovs_be16 vlan_tci;
+    ovs_be16 ethertype;
+    ovs_be32 mpls_lse;
 
     if (facet->byte_count <= facet->accounted_bytes) {
         return;
@@ -3251,9 +3265,13 @@ facet_account(struct facet *facet)
     subfacet = CONTAINER_OF(list_front(&facet->subfacets),
                             struct subfacet, list_node);
     vlan_tci = facet->flow.vlan_tci;
+    vlan_tci = facet->flow.dl_type;
+    mpls_lse = facet->flow.mpls_lse;
     NL_ATTR_FOR_EACH_UNSAFE (a, left,
                              subfacet->actions, subfacet->actions_len) {
         const struct ovs_action_push_vlan *vlan;
+        const struct ovs_action_mpls *mpls;
+        const struct ovs_action_set_mpls_label *mpls_label;
         struct ofport_dpif *port;
 
         switch (nl_attr_type(a)) {
@@ -3273,6 +3291,21 @@ facet_account(struct facet *facet)
             vlan = nl_attr_get(a);
             vlan_tci = vlan->vlan_tci;
             break;
+
+        case OVS_ACTION_ATTR_PUSH_MPLS:
+            mpls = nl_attr_get(a);
+            ethertype = mpls->ethertype; 
+            break;
+
+        case OVS_ACTION_ATTR_POP_MPLS:
+            mpls = nl_attr_get(a);
+            ethertype = mpls->ethertype; 
+            break;
+
+        case OVS_ACTION_ATTR_SET_MPLS_LABEL:
+            mpls_label = nl_attr_get(a);
+            mpls_lse = mpls_label->mpls_lse; 
+            break;
         }
     }
 }
@@ -4296,12 +4329,72 @@ fix_sflow_action(struct action_xlate_ctx *ctx)
 }
 
 static void
+compose_dec_mpls_ttl (struct action_xlate_ctx *ctx)
+{
+    if (ctx == NULL || ctx->ofproto == NULL)
+        return;
+    ctx->ofproto->mpls_ttl &= 
+        ~(OFP_ACTION_MPLS_TTL << OFPA_DEC_MPLS_TTL_SHIFT);
+    ctx->ofproto->mpls_ttl |=
+        (OFP_ACTION_MPLS_TTL << OFPA_DEC_MPLS_TTL_SHIFT);
+}
+
+static void
+compose_copy_mpls_ttl_in (struct action_xlate_ctx *ctx)
+{
+    if (ctx == NULL || ctx->ofproto == NULL)
+        return;
+    ctx->ofproto->mpls_ttl &= 
+        ~(OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_IN_SHIFT);
+    ctx->ofproto->mpls_ttl |=
+        (OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_IN_SHIFT);
+}
+
+static void
+compose_copy_mpls_ttl_out (struct action_xlate_ctx *ctx)
+{
+    if (ctx == NULL || ctx->ofproto == NULL)
+        return;
+    ctx->ofproto->mpls_ttl &= 
+        ~(OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_OUT_SHIFT);
+    ctx->ofproto->mpls_ttl |=
+        (OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_OUT_SHIFT);
+}
+
+static void 
+commit_odp_mpls_ttl_actions(struct ofproto_dpif *ofproto, 
+                            struct ofpbuf *odp_actions)
+{
+    if (ofproto->mpls_ttl & 
+        (OFP_ACTION_MPLS_TTL << OFPA_DEC_MPLS_TTL_SHIFT)) {
+        ofproto->mpls_ttl &= 
+            ~(OFP_ACTION_MPLS_TTL << OFPA_DEC_MPLS_TTL_SHIFT);
+        nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_DEC_MPLS_TTL);
+    }
+        
+    if (ofproto->mpls_ttl & 
+        (OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_IN_SHIFT)) {
+        ofproto->mpls_ttl &= 
+            ~(OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_IN_SHIFT);
+        nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_COPY_TTL_IN);
+    }
+
+    if (ofproto->mpls_ttl & 
+        (OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_OUT_SHIFT)) {
+        ofproto->mpls_ttl &= 
+            ~(OFP_ACTION_MPLS_TTL << OFPA_COPY_MPLS_TTL_OUT_SHIFT);
+        nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_COPY_TTL_OUT);
+    }
+}
+
+static void
 compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
                         bool check_stp)
 {
     const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
     uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
     ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
+    ovs_be32 flow_mpls_lse = ctx->flow.mpls_lse;
     uint8_t flow_nw_tos = ctx->flow.nw_tos;
     uint16_t out_port;
 
@@ -4330,12 +4423,14 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
         ctx->flow.vlan_tci = htons(0);
     }
     commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
+    commit_odp_mpls_ttl_actions(ctx->ofproto, ctx->odp_actions);
     nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
 
     ctx->sflow_odp_port = odp_port;
     ctx->sflow_n_outputs++;
     ctx->nf_output_iface = ofp_port;
     ctx->flow.vlan_tci = flow_vlan_tci;
+    ctx->flow.mpls_lse = flow_mpls_lse;
     ctx->flow.nw_tos = flow_nw_tos;
 }
 
@@ -4761,6 +4856,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         const struct nx_action_output_reg *naor;
         enum ofputil_action_code code;
         ovs_be64 tun_id;
+        ovs_be32 mpls_label;
 
         if (ctx->exit) {
             break;
@@ -4787,6 +4883,48 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             ctx->flow.vlan_tci = htons(0);
             break;
 
+        case OFPUTIL_OFPAT_PUSH_MPLS:
+            ctx->flow.dl_type = ia->push_mpls.ethertype;
+            break;
+
+        case OFPUTIL_OFPAT_POP_MPLS:
+            ctx->flow.dl_type = ia->pop_mpls.ethertype;
+            break;
+
+        case OFPUTIL_OFPAT_SET_MPLS_LABEL:
+            ctx->flow.mpls_lse &= ~htonl(MPLS_LABEL_MASK);
+            mpls_label = htonl(
+                ntohl(ia->mpls_label.mpls_label) << MPLS_LABEL_SHIFT);
+            ctx->flow.mpls_lse |= mpls_label;
+            VLOG_DBG("rkk-10 %x %x", ctx->flow.mpls_lse, mpls_label);
+            break;
+
+        case OFPUTIL_OFPAT_SET_MPLS_TC:
+            ctx->flow.mpls_lse &= ~htonl(MPLS_TC_MASK);
+            ctx->flow.mpls_lse |= htonl(
+                (ia->mpls_tc.mpls_tc << MPLS_TC_SHIFT));
+            VLOG_DBG("rkk-11 %x", ctx->flow.mpls_lse);
+            break;
+
+        case OFPUTIL_OFPAT_SET_MPLS_TTL:
+            ctx->flow.mpls_lse &= ~htonl(MPLS_TTL_MASK);
+            ctx->flow.mpls_lse |= htonl(
+                (ia->mpls_ttl.mpls_ttl << MPLS_TTL_SHIFT));
+            VLOG_DBG("rkk-12 %x", ctx->flow.mpls_lse);
+            break;
+
+        case OFPUTIL_OFPAT_DEC_MPLS_TTL:
+            compose_dec_mpls_ttl(ctx);
+            break;
+
+        case OFPUTIL_OFPAT_COPY_TTL_IN:
+            compose_copy_mpls_ttl_in(ctx);
+            break;
+
+        case OFPUTIL_OFPAT_COPY_TTL_OUT:
+            compose_copy_mpls_ttl_out(ctx);
+            break;
+
         case OFPUTIL_OFPAT_SET_DL_SRC:
             oada = ((struct ofp_action_dl_addr *) ia);
             memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
@@ -5726,6 +5864,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
         struct ofproto_push push;
         struct ofpbuf key;
 
+        VLOG_DBG("in rkk-8 %x %x", flow->mpls_lse, flow->dl_type);
         ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
         odp_flow_key_from_flow(&key, flow);
 
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 56d648c..808046e 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -894,9 +894,35 @@ do_normalize_actions(int argc, char *argv[])
             continue;
         }
 
+        if (nl_attr_type(a) == OVS_ACTION_ATTR_PUSH_MPLS) {
+            const struct ovs_action_mpls *push;
+
+            push = nl_attr_get_unspec(a, sizeof *push);
+            flow.dl_type = push->ethertype;
+            continue;
+        }
+
+        if (nl_attr_type(a) == OVS_ACTION_ATTR_POP_MPLS) {
+            const struct ovs_action_mpls *pop;
+
+            pop = nl_attr_get_unspec(a, sizeof *pop);
+            flow.dl_type = pop->ethertype;
+            continue;
+        }
+
+        if (nl_attr_type(a) == OVS_ACTION_ATTR_SET_MPLS_LABEL) {
+            const struct ovs_action_set_mpls_label *mpls_lse;
+
+            mpls_lse = nl_attr_get_unspec(a, sizeof *mpls_lse);
+            flow.mpls_lse = mpls_lse->mpls_lse;
+            VLOG_DBG("rkk-normalize %x %x", flow.mpls_lse);
+            continue;
+        }
+
         af = get_actions_for_flow(&actions_per_flow, &flow);
         nl_msg_put_unspec(&af->actions, nl_attr_type(a),
                           nl_attr_get(a), nl_attr_get_size(a));
+
     }
 
     n_afs = hmap_count(&actions_per_flow);
@@ -922,6 +948,15 @@ do_normalize_actions(int argc, char *argv[])
             printf("no vlan: ");
         }
 
+        if (af->flow.mpls_lse != htonl(0)) {
+            printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ",
+                   mpls_lse_to_label(af->flow.mpls_lse),
+                   mpls_lse_to_tc(af->flow.mpls_lse),
+                   mpls_lse_to_ttl(af->flow.mpls_lse));
+        } else {
+            printf("no mpls: ");
+        }
+
         ds_clear(&s);
         format_odp_actions(&s, af->actions.data, af->actions.size);
         puts(ds_cstr(&s));


More information about the dev mailing list