On 1 Apr 2021, at 11:28, Martin Varghese wrote:

On Thu, Apr 01, 2021 at 11:17:14AM +0200, Eelco Chaudron wrote:


On 1 Apr 2021, at 11:09, Martin Varghese wrote:

On Thu, Apr 01, 2021 at 10:54:42AM +0200, Eelco Chaudron wrote:


On 1 Apr 2021, at 10:35, Martin Varghese wrote:

On Thu, Apr 01, 2021 at 08:59:27AM +0200, Eelco Chaudron wrote:


On 1 Apr 2021, at 6:10, Martin Varghese wrote:

On Wed, Mar 31, 2021 at 03:59:40PM +0200, Eelco Chaudron wrote:


On 26 Mar 2021, at 7:21, Martin Varghese wrote:

From: Martin Varghese <martin.vargh...@nokia.com>

The encap & decap actions are extended to support MPLS
packet type.
Encap & decap actions adds and removes MPLS
header at start of the
packet.

Hi Martin,

I’m trying to do some real-life testing, and I’m running into
issues. This
might be me setting it up wrongly but just wanting to confirm…

I’m sending an MPLS packet that contains an ARP packet into a
physical port.
This is the packet:

Frame 4: 64 bytes on wire (512 bits), 64 bytes
captured (512 bits)
    Encapsulation type: Ethernet (1)
    [Protocols in frame: eth:ethertype:mpls:data]
Ethernet II, Src: 00:00:00_00:00:01 (00:00:00:00:00:01), Dst:
00:00:00_00:00:02 (00:00:00:00:00:02)
    Destination: 00:00:00_00:00:02 (00:00:00:00:00:02)
        Address: 00:00:00_00:00:02 (00:00:00:00:00:02)
        .... ..0. .... .... .... .... = LG bit: Globally unique
address
(factory default)
        .... ...0 .... .... .... .... = IG bit:
Individual address
(unicast)
    Source: 00:00:00_00:00:01 (00:00:00:00:00:01)
        Address: 00:00:00_00:00:01 (00:00:00:00:00:01)
        .... ..0. .... .... .... .... = LG bit: Globally unique
address
(factory default)
        .... ...0 .... .... .... .... = IG bit:
Individual address
(unicast)
    Type: MPLS label switched packet (0x8847)
MultiProtocol Label Switching Header, Label: 100, Exp: 0, S:
1, TTL:
64
    0000 0000 0000 0110 0100 .... .... .... = MPLS Label: 100
    .... .... .... .... .... 000. .... .... = MPLS Experimental
Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS
Bottom Of Label
Stack: 1
    .... .... .... .... .... .... 0100 0000 = MPLS TTL: 64
Data (46 bytes)

0000  ff ff ff ff ff ff 52 54 00 88 51 38 08 06 00 01
......RT..Q8....
0010  08 00 06 04 00 01 52 54 00 88 51 38 01 01 01 65
......RT..Q8...e
0020  00 00 00 00 00 00 01 01 01 64 27 98 a0 47
.........d'..G
    Data:
ffffffffffff525400885138080600010800060400015254008851380101016500000000?


I’m trying to use the following rules:

  ovs-ofctl del-flows ovs_pvp_br0
  ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
"priority=100,dl_type=0x8847,mpls_label=100
actions=decap(),decap(packet_type(ns=0,type=0x806)),resubmit(,3)"
  ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
"table=3,priority=10
actions=normal"

With these, I expect the packet to be sent to vnet0, but
it’s not.
Actually,
the datapath rule looks odd, while the userspace rules seem
to match:

  $ ovs-dpctl dump-flows
  
recirc_id(0),in_port(1),eth(),eth_type(0x8847),mpls(label=100/0xfffff,tc=0/0,ttl=0/0x0,bos=1/1),
packets:13, bytes:1118, used:0.322s,
actions:pop_eth,pop_mpls(eth_type=0x806),recirc(0x19a)
  recirc_id(0x19a),in_port(1),eth_type(0x0806), packets:13,
bytes:884,
used:0.322s, actions:drop

  $ ovs-ofctl dump-flows ovs_pvp_br0 -O OpenFlow13
  cookie=0x0, duration=85.007s, table=0, n_packets=51,
n_bytes=4386,
priority=100,mpls,mpls_label=100
actions=decap(),decap(packet_type(ns=0,type=0x806)),resubmit(,3)
  cookie=0x0, duration=84.990s, table=3, n_packets=51,
n_bytes=3468,
priority=10 actions=NORMAL

The inner packet is ethernet. So the packet type should be
(ns=0,type=0)
?

Forgot to add that I already tried that to start with, based on the
example,
but as that did not work I tried 0x806.

PS: I have this as a remark in my review notes, i.e., to
explain the
ns and
type usage here.


This resulted in packets being counted at the open flow
level, but it
results in NO data path rules. Do get an error though:

2021-04-01T06:53:36.056Z|00141|dpif(handler37)|WARN|system@ovs-system:
failed to put[create] (Invalid argument)
ufid:3d2d6f6d-5a66-4ace-8b09-7cdcfa5efc8e recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),eth(src=00:00:00:00:00:01/00:00:00:00:00:00,dst=00:00:00:00:00:02/00:00:00:00:00:00),eth_type(0x8847),mpls(label=100/0xfffff,tc=0/0,ttl=64/0x0,bos=1/1),
actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x4c)

This set(eth) before the recirc is the problem i guesss. I need
to check
2021-04-01T06:53:36.056Z|00142|dpif(handler37)|WARN|system@ovs-system:
execute pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x4c)
failed
(Invalid argument) on packet mpls,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,mpls_label=100,mpls_tc=0,mpls_ttl=64,mpls_bos=1
 with metadata skb_priority(0),skb_mark(0),in_port(1) mtu 0

Are there missing parts in my kernel that do not get properly
detected by
the feature detection?

$ ovs-appctl dpif/show-dp-features ovs_pvp_br0
Masked set action: Yes
Tunnel push pop: No
Ufid: Yes
Truncate action: Yes
Clone action: Yes
Sample nesting: 10
Conntrack eventmask: Yes
Conntrack clear: Yes
Max dp_hash algorithm: 0
Check pkt length action: Yes
Conntrack timeout policy: Yes
Explicit Drop action: No
Optimized Balance TCP mode: No
l2 MPLS tunnelling: Yes
Max VLAN headers: 2
Max MPLS depth: 3
Recirc: Yes
CT state: Yes
CT zone: Yes
CT mark: Yes
CT label: Yes
CT state NAT: Yes
CT orig tuple: Yes
CT orig tuple for IPv6: Yes
IPv6 ND Extension: No

You are good

I am not sure what is going wrong. Your test case looks same as
the unit
test i added.

I tried myself again and this is i get

ovs-ofctl -O OpenFlow13 add-flow br_mpls2
"in_port=$egress_port,dl_type=0x8847
+actions=decap(),decap(packet_type(ns=0,type=0),goto_table:1"
ovs-ofctl -O OpenFlow13 add-flow br_mpls2
+"table=1,in_port=$egress_port,dl_type=0x0800,nw_dst=1.1.1.2
+actions=set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_sr
+c output:$ingress_port"

recirc_id(0x3),in_port(6),eth(src=36:b1:ee:7c:01:03,dst=36:b1:ee:7c:01:02),eth_
+type(0x0800),ipv4(dst=1.1.1.2,frag=no), packets:3, bytes:294,
used:0.837s,
+actions:set(eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02)),4
recirc_id(0),in_port(6),eth(),eth_type(0x8847),mpls(label=0/0x0,tc=0/0,ttl=0/0x
+0,bos=1/1), packets:3, bytes:348, used:0.837s,
+actions:pop_eth,pop_mpls(eth_type=0x6558),recirc(0x3)

The packet to the ovs is
ETH|MPLS|ETH|IP ?
How it is differnt from you test case?

Mine is ETH|MPLS|ETH|ARP, which works fine with pop_mpls

I am wondering how old mpls pop  action works
could you please put down the userspace and datapath rules when you used
pop_mpls.

In my understanding it can never work as what you have after MPLS is
ethernet and not ARP.

It’s ethernet + ARP, but here are my rules:

To clarify

The test vector for Decap Test case
ETH|MPLS|ETH|ARP
The test vector for pop mpls test case
ETH|MPLS|ARP|

The above understanding correct?

Guess our emails crossed ;) I was sending in the same packet for both the test cases, so

ETH|MPLS|ETH|ARP

Which with decap() is resulting in the rules not being programmed

With popmpls I saw the packets in being received, but did not notice the incorrect use of popmpls so my packet after popmpls looks like ETH|ETH|ARP.

So I guess all that remains is why the data path rules is not accepted for ARP with the decap.


dpctl:

recirc_id(0),in_port(2),eth(),eth_type(0x8847),mpls(label=100/0xfffff,tc=0/0,ttl=0/0x0,bos=1/1),
packets:64, bytes:5504, used:0.444s,
actions:pop_mpls(eth_type=0x806),recirc(0x80d)
recirc_id(0x80d),in_port(2),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0806),
packets:64, bytes:5248, used:0.444s, actions:3,1

ofctl:

OFPST_FLOW reply (OF1.5) (xid=0x2):
cookie=0x0, duration=178.890s, table=0, n_packets=127, n_bytes=10922,
idle_age=0, priority=100,mpls,mpls_label=100
actions=pop_mpls:0x0806,resubmit(,3)
cookie=0x0, duration=178.873s, table=3, n_packets=127, n_bytes=10414,
idle_age=0, priority=10 actions=NORMAL


Thanks for your time.

Your welcome


If I use the old way, doing pop_mpls, it works fine:


ovs-ofctl del-flows ovs_pvp_br0
ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
"priority=100,dl_type=0x8847,mpls_label=100
actions=pop_mpls:0x0806,resubmit(,3)"
ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 "table=3,priority=10
actions=normal"


I also noticed (despite the test example) to make
encap work, I had
to set
the ethernet MAC addresses, or else the packets were not
getting out.
So something like:

ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0 "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),set_mpls_label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0



The packets are not going out because you are sending the packet
on a
real nic and not on a virtual inerface (veth pair) ?

So for a real NIC we need to set the MAC addresses, maybe
some where
in the
documentation we should add an example on how to use this feature?

Maybe the test case can be made more realistic? Once I
understand the
failure, I can continue with the review.


Cheers,

Eelco



Signed-off-by: Martin Varghese <martin.vargh...@nokia.com>
---
 NEWS                                          |  2 +-
 .../linux/compat/include/linux/openvswitch.h  | 35 ++++++-
 include/openvswitch/ofp-ed-props.h            | 18 ++++
 lib/dpif-netdev.c                             |  1 +
 lib/dpif.c                                    |  1 +
 lib/odp-execute.c                             | 12 +++
lib/odp-util.c | 58 +++++++++---
 lib/ofp-actions.c                             |  5 +
 lib/ofp-ed-props.c                            | 91
+++++++++++++++++++
 lib/ovs-actions.xml                           | 31 +++++--
 lib/packets.c                                 | 36 ++++++++
 lib/packets.h                                 |  2 +
 ofproto/ofproto-dpif-ipfix.c                  |  1 +
 ofproto/ofproto-dpif-sflow.c                  |  1 +
ofproto/ofproto-dpif-xlate.c | 60 ++++++++++++
 ofproto/ofproto-dpif.c                        | 39 ++++++++
 ofproto/ofproto-dpif.h                        |  5 +-
 tests/system-traffic.at                       | 36 ++++++++
 18 files changed, 410 insertions(+), 24 deletions(-)

diff --git a/NEWS b/NEWS
index 95cf922aa..4bf4e9e7b 100644
--- a/NEWS
+++ b/NEWS
@@ -120,7 +120,7 @@ v2.14.0 - 17 Aug 2020
    - GTP-U Tunnel Protocol
      * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype.
      * Only support for userspace datapath.
-
+   - Encap & Decap action support for MPLS packet type.

 v2.13.0 - 14 Feb 2020
 ---------------------
diff --git a/datapath/linux/compat/include/linux/openvswitch.h
b/datapath/linux/compat/include/linux/openvswitch.h
index 875de2025..8feea7dd4 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -810,8 +810,32 @@ struct ovs_action_push_tnl {
 };
 #endif

-/**
- * enum ovs_ct_attr - Attributes for
%OVS_ACTION_ATTR_CT action.
+/* struct ovs_action_add_mpls -
%OVS_ACTION_ATTR_ADD_MPLS action
+ * argument.
+ * @mpls_lse: MPLS label stack entry to push.
+ * @mpls_ethertype: Ethertype to set in the
encapsulating ethernet
frame.
+ * @tun_flags: MPLS tunnel attributes.
+ *
+ * The only values @mpls_ethertype should ever be given are
%ETH_P_MPLS_UC and
+ * %ETH_P_MPLS_MC, indicating MPLS unicast or
multicast. Other are
rejected.
+ */
+struct ovs_action_add_mpls {
+       __be32 mpls_lse;
+       __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or
%ETH_P_MPLS_MC */
+       __u16 tun_flags;
+};
+
+#define OVS_MPLS_L3_TUNNEL_FLAG_MASK  (1 << 0) /* Flag to
specify the
place of
+                                               * insertion of MPLS header.
+                                               * When false, the MPLS header
+                                               * will be inserted at the start
+                                               * of the packet.
+                                               * When true, the MPLS header
+                                               * will be inserted at the start
+                                               * of the l3 header.
+                                               */
+
+/* enum ovs_ct_attr - Attributes for
%OVS_ACTION_ATTR_CT action.
  * @OVS_CT_ATTR_COMMIT: If present, commits the
connection to the
conntrack
  * table. This allows future packets for the same
connection to be
identified
* as 'established' or 'related'. The flow key for the current
packet
will
@@ -1001,7 +1025,11 @@ struct check_pkt_len_arg {
* @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and
execute
a set
* of actions if greater than the specified packet length, else
execute
  * another set of actions.
- * @OVS_ACTION_ATTR_DROP: Explicit drop action.
+ * @OVS_ACTION_ATTR_ADD_MPLS: Push a new MPLS label stack entry
at the
+ * start of the packet or at the start of the l3 header
depending on
the value
+ * of l3 tunnel flag in the tun_flags field of
OVS_ACTION_ATTR_ADD_MPLS
+ * argument.
+  * @OVS_ACTION_ATTR_DROP: Explicit drop action.
  */

 enum ovs_action_attr {
@@ -1030,6 +1058,7 @@ enum ovs_action_attr {
        OVS_ACTION_ATTR_METER,        /* u32 meter number. */
OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */
        OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested
OVS_CHECK_PKT_LEN_ATTR_*. */
+       OVS_ACTION_ATTR_ADD_MPLS,     /* struct
ovs_action_add_mpls. */

 #ifndef __KERNEL__
OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
diff --git a/include/openvswitch/ofp-ed-props.h
b/include/openvswitch/ofp-ed-props.h
index 306c6fe73..c85f3c283 100644
--- a/include/openvswitch/ofp-ed-props.h
+++ b/include/openvswitch/ofp-ed-props.h
@@ -46,6 +46,11 @@ enum ofp_ed_nsh_prop_type {
     OFPPPT_PROP_NSH_TLV = 2,     /* property TLV in NSH */
 };

+enum ofp_ed_mpls_prop_type {
+    OFPPPT_PROP_MPLS_NONE = 0,    /* unused */
+    OFPPPT_PROP_MPLS_ETHERTYPE = 1,  /* MPLS Ethertype */
+};
+
 /*
  * External representation of encap/decap properties.
  * These must be padded to a multiple of 8 bytes.
@@ -72,6 +77,13 @@ struct ofp_ed_prop_nsh_tlv {
     uint8_t data[0];
 };

+struct ofp_ed_prop_mpls_ethertype {
+    struct ofp_ed_prop_header header;
+    uint16_t ether_type;         /* MPLS ethertype .*/
+    uint8_t pad[2];          /* Padding to 8 bytes. */
+};
+
+
 /*
  * Internal representation of encap/decap properties
  */
@@ -96,6 +108,12 @@ struct ofpact_ed_prop_nsh_tlv {
     /* tlv_len octets of metadata value, padded to a
multiple of 8
bytes. */
     uint8_t data[0];
 };
+
+struct ofpact_ed_prop_mpls_ethertype {
+    struct ofpact_ed_prop header;
+    uint16_t ether_type;         /* MPLS ethertype .*/
+    uint8_t pad[2];          /* Padding to 8 bytes. */
+};
 enum ofperr decode_ed_prop(const struct ofp_ed_prop_header
**ofp_prop,
                            struct ofpbuf *out, size_t
*remaining);
enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop,
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 94cc9b80c..bbdea5603 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -8044,6 +8044,7 @@ dp_execute_cb(void *aux_, struct
dp_packet_batch
*packets_,
     case OVS_ACTION_ATTR_CT_CLEAR:
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
     case OVS_ACTION_ATTR_DROP:
+    case OVS_ACTION_ATTR_ADD_MPLS:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index 56d0b4a65..bbd1296e3 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1273,6 +1273,7 @@ dpif_execute_helper_cb(void *aux_, struct
dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_UNSPEC:
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
     case OVS_ACTION_ATTR_DROP:
+    case OVS_ACTION_ATTR_ADD_MPLS:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 6eeda2a61..2f4cdd92c 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -819,6 +819,7 @@ requires_datapath_assistance(const struct
nlattr *a)
     case OVS_ACTION_ATTR_POP_NSH:
     case OVS_ACTION_ATTR_CT_CLEAR:
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+    case OVS_ACTION_ATTR_ADD_MPLS:
     case OVS_ACTION_ATTR_DROP:
         return false;

@@ -1061,6 +1062,17 @@ odp_execute_actions(void *dp, struct
dp_packet_batch *batch, bool steal,
             }
             break;

+        case OVS_ACTION_ATTR_ADD_MPLS: {
+            const struct ovs_action_add_mpls *mpls =
nl_attr_get(a);
+            bool l3_flag =  mpls->tun_flags &
OVS_MPLS_L3_TUNNEL_FLAG_MASK;
+
+            DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
+                add_mpls(packet, mpls->mpls_ethertype,
mpls->mpls_lse,
+                         l3_flag);
+            }
+            break;
+        }
+
         case OVS_ACTION_ATTR_DROP:{
             const enum xlate_error *drop_reason
= nl_attr_get(a);

diff --git a/lib/odp-util.c b/lib/odp-util.c
index a8598d52a..f24e16d08 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -142,6 +142,8 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE;
     case OVS_ACTION_ATTR_POP_NSH: return 0;
     case OVS_ACTION_ATTR_CHECK_PKT_LEN: return
ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_ADD_MPLS:
+         return sizeof(struct ovs_action_add_mpls);
     case OVS_ACTION_ATTR_DROP: return sizeof(uint32_t);

     case OVS_ACTION_ATTR_UNSPEC:
@@ -1254,6 +1256,14 @@ format_odp_action(struct ds *ds,
const struct
nlattr *a,
     case OVS_ACTION_ATTR_CHECK_PKT_LEN:
         format_odp_check_pkt_len_action(ds, a, portno_names);
         break;
+    case OVS_ACTION_ATTR_ADD_MPLS: {
+        const struct ovs_action_push_mpls *mpls
= nl_attr_get(a);
+        ds_put_cstr(ds, "add_mpls(");
+        format_mpls_lse(ds, mpls->mpls_lse);
+        ds_put_format(ds, ",eth_type=0x%"PRIx16")",
+                      ntohs(mpls->mpls_ethertype));
+        break;
+    }
     case OVS_ACTION_ATTR_DROP:
         ds_put_cstr(ds, "drop");
         break;
@@ -7876,7 +7886,8 @@ commit_vlan_action(const struct flow*
flow, struct
flow *base,
 /* Wildcarding already done at action translation time. */
 static void
commit_mpls_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, bool
pending_encap,
+                   bool pending_decap)
 {
     int base_n = flow_count_mpls_labels(base, NULL);
     int flow_n = flow_count_mpls_labels(flow, NULL);
@@ -7913,7 +7924,11 @@ commit_mpls_action(const
struct flow *flow,
struct flow *base,
             if ((!eth_type_mpls(flow->dl_type))
&& base_n > 1) {
                 dl_type = htons(ETH_TYPE_MPLS);
             } else {
-                dl_type = flow->dl_type;
+                if ((flow->packet_type == PT_ETH) &&
pending_decap) {
+                    dl_type =  htons(ETH_TYPE_TEB);
+                } else {
+                    dl_type = flow->dl_type;
+                }
             }
             nl_msg_put_be16(odp_actions,
OVS_ACTION_ATTR_POP_MPLS,
dl_type);
             ovs_assert(flow_pop_mpls(base,
base_n, flow->dl_type,
NULL));
@@ -7924,18 +7939,29 @@ commit_mpls_action(const struct
flow *flow,
struct flow *base,
/* If, after the above popping and setting, there are more
LSEs in
flow
      * than base then some LSEs need to be pushed. */
     while (base_n < flow_n) {
-        struct ovs_action_push_mpls *mpls;

-        mpls = nl_msg_put_unspec_zero(odp_actions,
-
OVS_ACTION_ATTR_PUSH_MPLS,
-                                      sizeof *mpls);
-        mpls->mpls_ethertype = flow->dl_type;
-        mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1];
+        if (pending_encap) {
+             struct ovs_action_add_mpls *mpls;
+
+             mpls = nl_msg_put_unspec_zero(odp_actions,
+
OVS_ACTION_ATTR_ADD_MPLS,
+                                           sizeof *mpls);
+             mpls->mpls_ethertype = flow->dl_type;
+             mpls->mpls_lse = flow->mpls_lse[flow_n -
base_n - 1];
+        } else {
+             struct ovs_action_push_mpls *mpls;
+
+             mpls = nl_msg_put_unspec_zero(odp_actions,
+
OVS_ACTION_ATTR_PUSH_MPLS,
+                                           sizeof *mpls);
+             mpls->mpls_ethertype = flow->dl_type;
+             mpls->mpls_lse = flow->mpls_lse[flow_n -
base_n - 1];
+        }
/* Update base flow's MPLS stack, but do not clear L3.
We need
the L3
          * headers if the flow is restored later due to
returning from
a patch
          * port or group bucket. */
-        flow_push_mpls(base, base_n,
mpls->mpls_ethertype, NULL,
false);
-        flow_set_mpls_lse(base, 0, mpls->mpls_lse);
+        flow_push_mpls(base, base_n,
flow->dl_type, NULL, false);
+        flow_set_mpls_lse(base, 0,
flow->mpls_lse[flow_n - base_n -
1]);
         base_n++;
     }
 }
@@ -8586,6 +8612,10 @@
commit_encap_decap_action(const struct flow
*flow,
             memcpy(&base_flow->dl_dst, &flow->dl_dst,
                    sizeof(*flow) - offsetof(struct
flow, dl_dst));
             break;
+        case PT_MPLS:
+            commit_mpls_action(flow, base_flow, odp_actions,
pending_encap,
+                               pending_decap);
+            break;
         default:
             /* Only the above protocols are
supported for encap.
              * The check is done at action translation. */
@@ -8608,6 +8638,10 @@
commit_encap_decap_action(const struct flow
*flow,
                 /* pop_nsh. */
                 odp_put_pop_nsh_action(odp_actions);
                 break;
+            case PT_MPLS:
+                commit_mpls_action(flow,
base_flow, odp_actions,
pending_encap,
+                                   pending_decap);
+                break;
             default:
                 /* Checks are done during translation. */
                 OVS_NOT_REACHED();
@@ -8653,7 +8687,7 @@ commit_odp_actions(const struct flow
*flow, struct
flow *base,
     /* Make packet a non-MPLS packet before committing L3/4
actions,
      * which would otherwise do nothing. */
     if (eth_type_mpls(base->dl_type) &&
!eth_type_mpls(flow->dl_type))
{
-        commit_mpls_action(flow, base, odp_actions);
+        commit_mpls_action(flow, base, odp_actions,
false, false);
         mpls_done = true;
     }
     commit_set_nsh_action(flow, base, odp_actions, wc,
use_masked);
@@ -8661,7 +8695,7 @@ commit_odp_actions(const struct flow
*flow, struct
flow *base,
     commit_set_port_action(flow, base, odp_actions, wc,
use_masked);
     slow2 = commit_set_icmp_action(flow, base,
odp_actions, wc);
     if (!mpls_done) {
-        commit_mpls_action(flow, base, odp_actions);
+        commit_mpls_action(flow, base, odp_actions,
false, false);
     }
     commit_vlan_action(flow, base, odp_actions, wc);
     commit_set_priority_action(flow, base, odp_actions, wc,
use_masked);
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 0342a228b..28a12a569 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -4441,6 +4441,7 @@ decode_NXAST_RAW_ENCAP(const struct
nx_action_encap *nae,
     switch (ntohl(nae->new_pkt_type)) {
     case PT_ETH:
     case PT_NSH:
+    case PT_MPLS:
         /* Add supported encap header types here. */
         break;
     default:
@@ -4492,6 +4493,8 @@ parse_encap_header(const
char *hdr, ovs_be32
*packet_type)
         *packet_type = htonl(PT_ETH);
     } else if (strcmp(hdr, "nsh") == 0) {
         *packet_type = htonl(PT_NSH);
+    } else if (strcmp(hdr, "mpls") == 0) {
+        *packet_type = htonl(PT_MPLS);
     } else {
         return false;
     }
@@ -4573,6 +4576,8 @@ format_encap_pkt_type(const
ovs_be32 pkt_type)
         return "ethernet";
     case PT_NSH:
         return "nsh";
+    case PT_MPLS:
+        return "mpls";
     default:
         return "UNKNOWN";
     }
diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c
index 02a9235d5..fc261e4c6 100644
--- a/lib/ofp-ed-props.c
+++ b/lib/ofp-ed-props.c
@@ -79,6 +79,27 @@ decode_ed_prop(const struct
ofp_ed_prop_header
**ofp_prop,
         }
         break;
     }
+    case OFPPPC_MPLS: {
+       switch (prop_type) {
+        case OFPPPT_PROP_MPLS_ETHERTYPE: {
+            struct ofp_ed_prop_mpls_ethertype *opnmt =
+                ALIGNED_CAST(struct
ofp_ed_prop_mpls_ethertype *,
*ofp_prop);
+            if (len > sizeof(*opnmt) || len > *remaining) {
+                return OFPERR_NXBAC_BAD_ED_PROP;
+            }
+            struct ofpact_ed_prop_mpls_ethertype *pnmt =
+                    ofpbuf_put_uninit(out, sizeof(*pnmt));
+            pnmt->header.prop_class = prop_class;
+            pnmt->header.type = prop_type;
+            pnmt->header.len = len;
+            pnmt->ether_type = opnmt->ether_type;
+            break;
+        }
+        default:
+            return OFPERR_NXBAC_UNKNOWN_ED_PROP;
+        }
+        break;
+    }
     default:
         return OFPERR_NXBAC_UNKNOWN_ED_PROP;
     }
@@ -134,6 +155,27 @@ encode_ed_prop(const struct ofpact_ed_prop
**prop,
         }
         break;
     }
+    case OFPPPC_MPLS: {
+       switch ((*prop)->type) {
+       case OFPPPT_PROP_MPLS_ETHERTYPE: {
+           struct ofpact_ed_prop_mpls_ethertype *pnmt =
+                ALIGNED_CAST(struct
ofpact_ed_prop_mpls_ethertype *,
*prop);
+            struct ofp_ed_prop_mpls_ethertype *opnmt =
+                    ofpbuf_put_uninit(out, sizeof(*opnmt));
+            opnmt->header.prop_class =
htons((*prop)->prop_class);
+            opnmt->header.type = (*prop)->type;
+            opnmt->header.len =
+                    offsetof(struct
ofpact_ed_prop_mpls_ethertype,
pad);
+            opnmt->ether_type = pnmt->ether_type;
+            prop_len = sizeof(*pnmt);
+            break;
+
+       }
+       default:
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+       }
+       break;
+    }
     default:
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
@@ -181,6 +223,13 @@ parse_ed_prop_type(uint16_t prop_class,
         } else {
             return false;
         }
+    case OFPPPC_MPLS:
+        if (!strcmp(str, "ether_type")) {
+            *type = OFPPPT_PROP_MPLS_ETHERTYPE;
+            return true;
+        } else {
+            return false;
+        }
     default:
         return false;
     }
@@ -259,6 +308,28 @@ parse_ed_prop_value(uint16_t prop_class,
uint8_t
prop_type OVS_UNUSED,
             OVS_NOT_REACHED();
         }
         break;
+    case OFPPPC_MPLS:
+        switch (prop_type) {
+        case OFPPPT_PROP_MPLS_ETHERTYPE: {
+            uint16_t ethertype;
+            error = str_to_u16(value,
"ether_type", &ethertype);
+            if (error != NULL) {
+                return error;
+            }
+            struct ofpact_ed_prop_mpls_ethertype *pnmt =
+                    ofpbuf_put_uninit(out, sizeof(*pnmt));
+            pnmt->header.prop_class = prop_class;
+            pnmt->header.type = prop_type;
+            pnmt->header.len =
+                    offsetof(struct
ofpact_ed_prop_mpls_ethertype,
pad);
+            pnmt->ether_type = ethertype;
+
+            break;
+        }
+        default:
+            break;
+      }
+      break;
     default:
         /* Unsupported property classes rejected before. */
         OVS_NOT_REACHED();
@@ -300,6 +371,14 @@ format_ed_prop_type(const struct
ofpact_ed_prop
*prop)
             OVS_NOT_REACHED();
         }
         break;
+    case OFPPPC_MPLS:
+         switch (prop->type) {
+         case OFPPPT_PROP_MPLS_ETHERTYPE:
+              return "ether_type";
+         default:
+               OVS_NOT_REACHED();
+         }
+         break;
     default:
         OVS_NOT_REACHED();
     }
@@ -332,6 +411,18 @@ format_ed_prop(struct ds *s OVS_UNUSED,
         default:
             OVS_NOT_REACHED();
         }
+     case OFPPPC_MPLS:
+        switch (prop->type) {
+        case OFPPPT_PROP_MPLS_ETHERTYPE: {
+          struct ofpact_ed_prop_mpls_ethertype *pnmt =
+                ALIGNED_CAST(struct
ofpact_ed_prop_mpls_ethertype *,
prop);
+            ds_put_format(s, "%s=%d",
format_ed_prop_type(prop),
+                          pnmt->ether_type);
+            return;
+        }
+        default:
+            OVS_NOT_REACHED();
+        }
     default:
         OVS_NOT_REACHED();
     }
diff --git a/lib/ovs-actions.xml b/lib/ovs-actions.xml
index a2778de4b..e97f818d9 100644
--- a/lib/ovs-actions.xml
+++ b/lib/ovs-actions.xml
@@ -265,13 +265,13 @@
       </p>

       <p>
- When a <code>decap</code> action decapsulates a packet,
Open
vSwitch
-        raises this error if it does not support the
type of inner
packet.
-        <code>decap</code> of an Ethernet header raises this
error if a
VLAN
-        header is present, <code>decap</code> of a NSH packet
raises
this error
- if the NSH inner packet is not Ethernet, IPv4, IPv6, or
NSH,
and
-        <code>decap</code> of other types of packets is
unsupported and
also
-        raises this error.
+        The <code>decap</code> action is supported only
for packet
types
+        ethernet, NSH and MPLS. Openvswitch raises this error
for other
+        packet types. When a <code>decap</code> action
decapsulates a
packet,
+        Open vSwitch raises this error if it does not support
the type
of inner
+ packet. <code>decap</code> of an Ethernet header raises
this
error if a
+        VLAN header is present, <code>decap</code> of a
NSH packet
raises this
+        error if the NSH inner packet is not Ethernet, IPv4,
IPv6, or
NSH.
       </p>

       <p>
@@ -1097,6 +1097,8 @@ for <var>i</var> in
[1,<var>n_members</var>]:
       <h2>The <code>encap</code> action</h2>
<syntax><code>encap(nsh(</code>[<code>md_type=<var>md_type</var></code>]<code>, </code>[<code>tlv(<var>class</var>,<var>type</var>,<var>value</var>)</code>]...<code>))</code></syntax>
       <syntax><code>encap(ethernet)</code></syntax>
+
<syntax><code>encap(mpls(ether_type=<var>ether_type</var>))</code>
+      </syntax>

       <p>
         The <code>encap</code> action encapsulates a
packet with a
specified
@@ -1135,6 +1137,12 @@ for <var>i</var> in
[1,<var>n_members</var>]:
         source and destination are initially zeroed.
       </p>

+      <p>
+        The <code>encap(mpls(ethertype=....))</code> variant
encapsulates an
+        ethernet or L3 packet with a MPLS header. The
<var>ethertype</var>
+        could be MPLS unicast (0x8847) or multicast (0x8848)
ethertypes.
+      </p>
+
       <conformance>
         This action is an Open vSwitch extension to OpenFlow
1.3 and
later,
         introduced in Open vSwitch 2.8.
@@ -1144,6 +1152,9 @@ for <var>i</var> in
[1,<var>n_members</var>]:
     <action name="DECAP">
       <h2>The <code>decap</code> action</h2>
       <syntax><code>decap</code></syntax>
+ <syntax><code>decap(packet_type(ns=<var>name_space</var>,
+      type=<var>ethertype</var>))</code></syntax> for
decapsulating
MPLS
+      packets.

       <p>
         Removes an outermost encapsulation from the packet:
@@ -1164,6 +1175,12 @@ for <var>i</var> in
[1,<var>n_members</var>]:
           packet type errors.
         </li>

+        <li>
+          Otherwise, if the packet is a MPLS packet, removes
the MPLS
header
+          and classifies the inner packet as mentioned in the
packet
type
+          argument of the decap.
+        </li>
+
         <li>
           Otherwise, raises an unsupported packet type error.
         </li>
diff --git a/lib/packets.c b/lib/packets.c
index 4a7643c5d..5e3c3900f 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -418,6 +418,38 @@ push_mpls(struct dp_packet
*packet, ovs_be16
ethtype, ovs_be32 lse)
     pkt_metadata_init_conn(&packet->md);
 }

+void
+add_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32
lse, bool
l3)
+{
+    char * header;
+
+    if (!eth_type_mpls(ethtype)) {
+        return;
+    }
+
+    if (!l3) {
+        header =  dp_packet_push_uninit(packet, MPLS_HLEN);
+        memcpy(header, &lse, sizeof lse);
+        packet->l2_5_ofs = 0;
+        packet->packet_type = htonl(PT_MPLS);
+    } else {
+        size_t len;
+
+        if (!is_mpls(packet)) {
+            /* Set MPLS label stack offset. */
+            packet->l2_5_ofs = packet->l3_ofs;
+        }
+        set_ethertype(packet, ethtype);
+
+        /* Push new MPLS shim header onto packet. */
+        len = packet->l2_5_ofs;
+        header = dp_packet_resize_l2_5(packet, MPLS_HLEN);
+        memmove(header, header + MPLS_HLEN, len);
+        memcpy(header + len, &lse, sizeof lse);
+    }
+    pkt_metadata_init_conn(&packet->md);
+}
+
 /* If 'packet' is an MPLS packet, removes its outermost
MPLS label
stack entry.
  * If the label that was removed was the only
MPLS label, changes
'packet''s
  * Ethertype to 'ethtype' (which ordinarily
should not be an MPLS
@@ -429,6 +461,10 @@ pop_mpls(struct dp_packet *packet, ovs_be16
ethtype)
         struct mpls_hdr *mh = dp_packet_l2_5(packet);
         size_t len = packet->l2_5_ofs;

+        if (ethtype == htons(ETH_TYPE_TEB)) {
+             packet->packet_type = htonl(PT_ETH);
+        }
+
         set_ethertype(packet, ethtype);
         if (get_16aligned_be32(&mh->mpls_lse) &
htonl(MPLS_BOS_MASK)) {
             dp_packet_set_l2_5(packet, NULL);
diff --git a/lib/packets.h b/lib/packets.h
index 481bc22fa..3f5862e08 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -350,6 +350,8 @@ void set_mpls_lse_label(ovs_be32
*lse, ovs_be32
label);
 void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos);
 ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc,
uint8_t bos,
                              ovs_be32 label);
+void add_mpls(struct dp_packet *packet, ovs_be16 ethtype,
ovs_be32 lse,
+              bool l3_flag);

 /* Example:
  *
diff --git a/ofproto/ofproto-dpif-ipfix.c
b/ofproto/ofproto-dpif-ipfix.c
index 796eb6f88..9280e008e 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -3018,6 +3018,7 @@ dpif_ipfix_read_actions(const struct flow
*flow,
         case OVS_ACTION_ATTR_CHECK_PKT_LEN:
         case OVS_ACTION_ATTR_UNSPEC:
         case OVS_ACTION_ATTR_DROP:
+        case OVS_ACTION_ATTR_ADD_MPLS:
         case __OVS_ACTION_ATTR_MAX:
         default:
             break;
diff --git a/ofproto/ofproto-dpif-sflow.c
b/ofproto/ofproto-dpif-sflow.c
index fdcb9eabb..ca46a9bc4 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1226,6 +1226,7 @@ dpif_sflow_read_actions(const struct flow
*flow,
         case OVS_ACTION_ATTR_UNSPEC:
         case OVS_ACTION_ATTR_CHECK_PKT_LEN:
         case OVS_ACTION_ATTR_DROP:
+        case OVS_ACTION_ATTR_ADD_MPLS:
         case __OVS_ACTION_ATTR_MAX:
         default:
             break;
diff --git a/ofproto/ofproto-dpif-xlate.c
b/ofproto/ofproto-dpif-xlate.c
index 7108c8a30..a97534233 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -6381,6 +6381,45 @@
rewrite_flow_encap_ethernet(struct xlate_ctx
*ctx,
         ctx->error = XLATE_UNSUPPORTED_PACKET_TYPE;
     }
 }
+static void
+rewrite_flow_encap_mpls(struct xlate_ctx *ctx,
+                        const struct ofpact_encap *encap,
+                        struct flow *flow,
+                        struct flow_wildcards *wc)
+{
+    int n;
+    uint32_t i;
+    uint16_t ether_type;
+    const char *ptr = (char *) encap->props;
+
+     for (i = 0; i < encap->n_props; i++) {
+        struct ofpact_ed_prop *prop_ptr =
+            ALIGNED_CAST(struct ofpact_ed_prop *, ptr);
+        if (prop_ptr->prop_class == OFPPPC_MPLS) {
+            switch (prop_ptr->type) {
+                case OFPPPT_PROP_MPLS_ETHERTYPE: {
+                     struct ofpact_ed_prop_mpls_ethertype
*prop_ether_type =
+                        ALIGNED_CAST(struct
ofpact_ed_prop_mpls_ethertype *,
+                                     prop_ptr);
+                    ether_type = prop_ether_type->ether_type;
+                    break;
+                 }
+            }
+        }
+     }
+
+    wc->masks.packet_type = OVS_BE32_MAX;
+    if (flow->packet_type != htonl(PT_MPLS)) {
+        memset(&ctx->wc->masks.mpls_lse, 0x0,
+               sizeof *wc->masks.mpls_lse *
FLOW_MAX_MPLS_LABELS);
+        memset(&flow->mpls_lse, 0x0, sizeof *flow->mpls_lse *
+               FLOW_MAX_MPLS_LABELS);
+    }
+    flow->packet_type = htonl(PT_MPLS);
+    n = flow_count_mpls_labels(flow, ctx->wc);
+ flow_push_mpls(flow, n, htons(ether_type), ctx->wc, true);
+}
+

 /* For an MD2 NSH header returns a pointer to
an ofpbuf with the
encoded
  * MD2 TLVs provided as encap properties to the encap
operation. This
@@ -6513,6 +6552,12 @@ xlate_generic_encap_action(struct
xlate_ctx *ctx,
         case PT_NSH:
             encap_data = rewrite_flow_push_nsh(ctx, encap,
flow, wc);
             break;
+        case PT_MPLS:
+            rewrite_flow_encap_mpls(ctx, encap,  flow, wc);
+            if (!ctx->xbridge->support.add_mpls) {
+                ctx->xout->slow |= SLOW_ACTION;
+            }
+            break;
         default:
/* New packet type was checked during decoding. */
             OVS_NOT_REACHED();
@@ -6582,6 +6627,21 @@ xlate_generic_decap_action(struct
xlate_ctx *ctx,
             ctx->pending_decap = true;
             /* Trigger recirculation. */
             return true;
+        case PT_MPLS: {
+             int n;
+             ovs_be16 ethertype;
+
+             flow->packet_type = decap->new_pkt_type;
+             ethertype = pt_ns_type_be(flow->packet_type);
+
+             n = flow_count_mpls_labels(flow, ctx->wc);
+             flow_pop_mpls(flow, n, ethertype, ctx->wc);
+             if (!ctx->xbridge->support.add_mpls) {
+                ctx->xout->slow |= SLOW_ACTION;
+             }
+             ctx->pending_decap = true;
+             return true;
+        }
         default:
             /* Error handling: drop packet. */
             xlate_report_debug(
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index fd0b2fdea..d9a2922e7 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1520,6 +1520,44 @@ check_nd_extensions(struct dpif_backer
*backer)

     return !error;
 }
+/* Tests whether 'backer''s datapath supports the
+ * OVS_ACTION_ATTR_ADD_MPLS action. */
+static bool
+check_add_mpls(struct dpif_backer *backer)
+{
+    struct odputil_keybuf keybuf;
+    struct ofpbuf actions;
+    struct ofpbuf key;
+    struct flow flow;
+    bool supported;
+
+    struct odp_flow_key_parms odp_parms = {
+        .flow = &flow,
+        .probe = true,
+    };
+
+    memset(&flow, 0, sizeof flow);
+    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+    odp_flow_key_from_flow(&odp_parms, &key);
+    ofpbuf_init(&actions, 64);
+
+    struct ovs_action_add_mpls *mpls;
+
+    mpls = nl_msg_put_unspec_zero(&actions,
+                                  OVS_ACTION_ATTR_ADD_MPLS,
+                                  sizeof *mpls);
+    mpls->mpls_ethertype = htons(ETH_TYPE_MPLS);
+
+    supported = dpif_probe_feature(backer->dpif,
"add_mpls", &key,
+                                   &actions, NULL);
+    ofpbuf_uninit(&actions);
+    VLOG_INFO("%s: Datapath %s add_mpls action",
+              dpif_name(backer->dpif), supported ? "supports"
+                                                 : "does not
support");
+    return supported;
+
+}
+

 #define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE, ETHTYPE)
\
 static bool
\
@@ -1590,6 +1628,7 @@ check_support(struct dpif_backer *backer)
         dpif_supports_explicit_drop_action(backer->dpif);
     backer->rt_support.lb_output_action=
         dpif_supports_lb_output_action(backer->dpif);
+    backer->rt_support.add_mpls = check_add_mpls(backer);

     /* Flow fields. */
     backer->rt_support.odp.ct_state = check_ct_state(backer);
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index b41c3d82a..c04bfff8d 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -204,7 +204,10 @@ struct group_dpif *group_dpif_lookup(struct
ofproto_dpif *,
     DPIF_SUPPORT_FIELD(bool,
explicit_drop_action, "Explicit Drop
action")  \
                                                                             \
     /* True if the datapath supports
balance_tcp optimization */
\
-    DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized
Balance TCP
mode")
+    DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized
Balance TCP
mode")\
+
\
+ /* True if the datapath supports layer 2 MPLS tunnelling */
\
+    DPIF_SUPPORT_FIELD(bool, add_mpls, "l2 MPLS tunnelling")


 /* Stores the various features which the corresponding backer
supports.
*/
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index fb5b9a36d..b865b5210 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -1027,9 +1027,45 @@ NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i
0.3 -w 2
10.1.1.2 | FORMAT_PING], [0],
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
 ])

+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
+
+AT_SETUP([datapath - ptap mpls actions])
+OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])])
+
+ADD_NAMESPACES(at_ns0, at_ns1)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24")
+
+AT_CHECK([ip link add patch0 type veth peer name patch1])
+on_exit 'ip link del patch0'
+
+AT_CHECK([ip link set dev patch0 up])
+AT_CHECK([ip link set dev patch1 up])
+AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0
ofport_request=100])
+AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1
ofport_request=100])
+
+AT_DATA([flows.txt], [dnl
+table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),output:100
+table=0,priority=100,dl_type=0x8847,mpls_label=2
actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)
+table=0,priority=10 actions=resubmit(,3)
+table=3,priority=10 actions=normal
+])
+
+AT_CHECK([ovs-ofctl  -Oopenflow13 add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl  -Oopenflow13 add-flows br1 flows.txt])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 |
FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+
 NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 |
FORMAT_PING], [0], [dnl
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
 ])
+
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP

--
2.18.4





_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to