diff mbox series

[ovs-dev,v4,1/2] Encap & Decap actions for MPLS packet type.

Message ID 20210326062118.56286-1-martinvarghesenokia@gmail.com
State Superseded
Headers show
Series Encap & Decap actions for MPLS packet type | expand

Commit Message

Martin Varghese March 26, 2021, 6:21 a.m. UTC
From: Martin Varghese <martin.varghese@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.

Signed-off-by: Martin Varghese <martin.varghese@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(-)

Comments

Eelco Chaudron March 31, 2021, 1:59 p.m. UTC | #1
On 26 Mar 2021, at 7:21, Martin Varghese wrote:

> From: Martin Varghese <martin.varghese@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


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

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.varghese@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
Martin Varghese April 1, 2021, 4:10 a.m. UTC | #2
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.varghese@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)
?

> 
> 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) ?

> 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.varghese@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
>
Eelco Chaudron April 1, 2021, 6:59 a.m. UTC | #3
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.varghese@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)
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

>>
>> 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.varghese@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
>>
Eelco Chaudron April 1, 2021, 8:24 a.m. UTC | #4
On 1 Apr 2021, at 8:59, 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.varghese@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)
> 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?

Just to be sure I build the “lastest” net kernel, e43accba9b07, 
5.12.0-rc2+, and I see the same problem.

> $ 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
>
>>>
>>> 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.varghese@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
>>>
Martin Varghese April 1, 2021, 8:35 a.m. UTC | #5
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.varghese@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?

Thanks for your time.



> > > 
> > > 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.varghese@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
> > > 
>
Eelco Chaudron April 1, 2021, 8:54 a.m. UTC | #6
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.varghese@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

> 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.varghese@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
>>>>
>>
Martin Varghese April 1, 2021, 9:09 a.m. UTC | #7
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.varghese@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.


> > 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.varghese@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
> > > > > 
> > > 
>
Eelco Chaudron April 1, 2021, 9:17 a.m. UTC | #8
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.varghese@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:

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.varghese@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
>>>>>>
>>>>
>>
Eelco Chaudron April 1, 2021, 9:27 a.m. UTC | #9
On 1 Apr 2021, at 11:17, 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.varghese@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:
>
> 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

I now see that even with POP it seems to work some what, It still sent 
the ARP packet in the wrong format.
It contains a double ETHERNET header:

0000  00 00 00 00 00 02 00 00 00 00 00 01 08 06

                                                 ff ff   
................
0010  ff ff ff ff 52 54 00 88 51 38 08 06

                                           00 01 08 00   
....RT..Q8......
0020  06 04 00 01 52 54 00 88 51 38 01 01 01 65 00 00   ....RT..Q8...e..
0030  00 00 00 00 01 01 01 64 27 98 a0 47 40 41 00 c1   .......d'..G@A..
0040  b5 5f 96 78 c4 00 0c 40 00 02 f5 9a 27 f9 9a 71   ._.x...@....'..q
0050  93 5d                                             .]

So basically my question is how to make this work so that ARP and IP can 
work. Or is ARP not supported and can we only forward routed traffic, 
which seems odd?

>>>> 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.varghese@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
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Martin Varghese April 1, 2021, 9:28 a.m. UTC | #10
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.varghese@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?
> 
> 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.varghese@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
> > > > > > > 
> > > > > 
> > > 
>
Eelco Chaudron April 1, 2021, 9:32 a.m. UTC | #11
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.varghese@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.varghese@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
>>>>>>>>
>>>>>>
>>>>
>>
Eelco Chaudron April 6, 2021, 8:27 a.m. UTC | #12
On 1 Apr 2021, at 11:32, Eelco Chaudron wrote:

> 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.varghese@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.

Martin any update on the above?

>>>
>>> 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
Martin Varghese April 6, 2021, 8:27 a.m. UTC | #13
On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> 
> 
> 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.varghese@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

I could reproduce the problem. It has nothing to do with ARP or IP. Unlike my test scripts, in your test you are setting the mac address after the encap action

Ovs-vswitchd is programming a set(eth(dst) action between the  pop_mpls and  recirc as it sees a difference in mac address in flow structure and  base_flow structure.

The mac address in flow structure is not cleared in PT_ETH handling of xlate_generic_decap_action but  it is cleared in base_flow in the   decap handling of PT_ETH in commit_encap_decap_action function

Due to this difference the set(eth(dst) action will be programmed to the datapath.

Also, I see that in  commit_set_ether_action Function  “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is ethernet instead of base_flow->packet_type.

I assume check on base_flow->packet_type make more sense here ?

I tried to fix this issue in 2 different ways.

1   I have cleared the mac address in flow structure  in PT_ETH handling of xlate_generic_decap action.

2  In the  commit_set_ether action I changed the check from  “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type != htonl(PT_ETH))”.

Though both of them solves this problem, couple of NSH regression tests are failing

2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)

58022292: nsh - md2 encap over a veth link                FAILED (nsh.at:213)

I see that they are failing as they are expecting a set(eth(dst)  between the the pop_nsh and the recirc.

Set(eth) action is because of the reasons explained above –

Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)

In my understanding set(eth) here  is wrong as there is no set ethernet action in the userspace rule
- Hide quoted text -

table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344,actions=decap(),decap(),2



Could someone comment ?

> > > > > > > 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.varghese@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
> > > > > > > > > 
> > > > > > > 
> > > > > 
> > > 
>
Eelco Chaudron April 6, 2021, 8:54 a.m. UTC | #14
On 6 Apr 2021, at 10:27, Martin Varghese wrote:

> On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
>>
>>
>> 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.varghese@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
>
> I could reproduce the problem. It has nothing to do with ARP or IP. 
> Unlike my test scripts, in your test you are setting the mac address 
> after the encap action
>
> Ovs-vswitchd is programming a set(eth(dst) action between the  
> pop_mpls and  recirc as it sees a difference in mac address in flow 
> structure and  base_flow structure.
>
> The mac address in flow structure is not cleared in PT_ETH handling of 
> xlate_generic_decap_action but  it is cleared in base_flow in the   
> decap handling of PT_ETH in commit_encap_decap_action function
>
> Due to this difference the set(eth(dst) action will be programmed to 
> the datapath.
>
> Also, I see that in  commit_set_ether_action Function  
> “flow->packet_type != htonl(PT_ETH)” is used to check if the 
> packet is ethernet instead of base_flow->packet_type.
>
> I assume check on base_flow->packet_type make more sense here ?
>
> I tried to fix this issue in 2 different ways.
>
> 1   I have cleared the mac address in flow structure  in PT_ETH 
> handling of xlate_generic_decap action.
>
> 2  In the  commit_set_ether action I changed the check from  
> “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type 
> != htonl(PT_ETH))”.
>
> Though both of them solves this problem, couple of NSH regression 
> tests are failing
>
> 2291: nsh - md1 encap over a veth link                FAILED 
> (nsh.at:85)
>
> 58022292: nsh - md2 encap over a veth link                FAILED 
> (nsh.at:213)
>
> I see that they are failing as they are expecting a set(eth(dst)  
> between the the pop_nsh and the recirc.
>
> Set(eth) action is because of the reasons explained above –
>
> Datapath actions: 
> push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
>
> In my understanding set(eth) here  is wrong as there is no set 
> ethernet action in the userspace rule
> - Hide quoted text -
>
> table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344,actions=decap(),decap(),2
>
>
>
> Could someone comment ?

Maybe Jan can answer as he did the NSH implementation, however, what 
would be of interest if you can give me an example of how the encap() 
decap() for this would be used in real life so I’m sure I’m testing 
it correctly?

What I did so far was to encapsulate all traffic going from a VM to the 
physical port in MPLS using the flows 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_ds
t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"

Then I would capture this traffic and sent it back over the same port, 
hoping it would come out as plane traffic with the following rule:

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

If this is correct, let me know, and if Jan does not reply, I’ll try 
to understand the code in this area and see if I can find out some 
details…

//Eelco

>>>>>>>> 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
Jan Scheurich April 6, 2021, 9 a.m. UTC | #15
Hi,

Thanks for the heads up. The interaction with MPLS push/pop is a use case that was likely not tested during the NSH and generic encap/decap design. It's complex code and a long time ago. I'm willing to help, but I will need some time to go back and have a look.

It would definitely help, if you could provide a minimal example for reproducing the problem.

BR, Jan

> -----Original Message-----
> From: Eelco Chaudron <echaudro@redhat.com>
> Sent: Tuesday, 6 April, 2021 10:55
> To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
> <jan.scheurich@ericsson.com>
> Cc: dev@openvswitch.org; pshelar@ovn.org; martin.varghese@nokia.com
> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> 
> 
> 
> On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> 
> > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> >>
> >>
> >> 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.varghese@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:
> >>>>>>>>>>
> ffffffffffff525400885138080600010800060400015254008851380101016500000
> 000?
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>> 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(0x4
> >>>>>>>> c)
> >>>>>>>
> >>>>>>> This set(eth) before the recirc is the problem i guesss. I need
> >>>>>>> to check
> >
> > I could reproduce the problem. It has nothing to do with ARP or IP.
> > Unlike my test scripts, in your test you are setting the mac address
> > after the encap action
> >
> > Ovs-vswitchd is programming a set(eth(dst) action between the pop_mpls
> > and  recirc as it sees a difference in mac address in flow structure
> > and  base_flow structure.
> >
> > The mac address in flow structure is not cleared in PT_ETH handling of
> > xlate_generic_decap_action but  it is cleared in base_flow in the
> > decap handling of PT_ETH in commit_encap_decap_action function
> >
> > Due to this difference the set(eth(dst) action will be programmed to
> > the datapath.
> >
> > Also, I see that in  commit_set_ether_action Function
> > “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is
> > ethernet instead of base_flow->packet_type.
> >
> > I assume check on base_flow->packet_type make more sense here ?
> >
> > I tried to fix this issue in 2 different ways.
> >
> > 1   I have cleared the mac address in flow structure  in PT_ETH
> > handling of xlate_generic_decap action.
> >
> > 2  In the  commit_set_ether action I changed the check from
> > “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > htonl(PT_ETH))”.
> >
> > Though both of them solves this problem, couple of NSH regression
> > tests are failing
> >
> > 2291: nsh - md1 encap over a veth link                FAILED
> > (nsh.at:85)
> >
> > 58022292: nsh - md2 encap over a veth link                FAILED
> > (nsh.at:213)
> >
> > I see that they are failing as they are expecting a set(eth(dst)
> > between the the pop_nsh and the recirc.
> >
> > Set(eth) action is because of the reasons explained above –
> >
> > Datapath actions:
> > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,
> > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:5
> > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> >
> > In my understanding set(eth) here  is wrong as there is no set
> > ethernet action in the userspace rule
> > - Hide quoted text -
> >
> > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
> > 11223344,actions=decap(),decap(),2
> >
> >
> >
> > Could someone comment ?
> 
> Maybe Jan can answer as he did the NSH implementation, however, what
> would be of interest if you can give me an example of how the encap()
> decap() for this would be used in real life so I’m sure I’m testing it correctly?
> 
> What I did so far was to encapsulate all traffic going from a VM to the physical
> port in MPLS using the flows 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_ds
> t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> 
> Then I would capture this traffic and sent it back over the same port, hoping it
> would come out as plane traffic with the following rule:
> 
> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> "priority=100,dl_type=0x8847,mpls_label=100
> actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> actions=normal"
> 
> If this is correct, let me know, and if Jan does not reply, I’ll try to understand
> the code in this area and see if I can find out some details…
> 
> //Eelco
> 
> >>>>>>>> 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:0
> >>>>>>>> 0: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/0xfff
> >>>> ff,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=0x8
> >>>>>>>>>> 847)),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:e
> >>>>>>>>>> np5s0f0
> >>>>>>>>>>
> >>>>>>>
> >>>>>>>>>
> >>>>>>>>> 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
Martin Varghese April 6, 2021, 9:02 a.m. UTC | #16
On Tue, Apr 06, 2021 at 10:54:32AM +0200, Eelco Chaudron wrote:
> 
> 
> On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> 
> > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > > 
> > > 
> > > 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.varghese@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
> > 
> > I could reproduce the problem. It has nothing to do with ARP or IP.
> > Unlike my test scripts, in your test you are setting the mac address
> > after the encap action
> > 
> > Ovs-vswitchd is programming a set(eth(dst) action between the  pop_mpls
> > and  recirc as it sees a difference in mac address in flow structure and
> > base_flow structure.
> > 
> > The mac address in flow structure is not cleared in PT_ETH handling of
> > xlate_generic_decap_action but  it is cleared in base_flow in the
> > decap handling of PT_ETH in commit_encap_decap_action function
> > 
> > Due to this difference the set(eth(dst) action will be programmed to the
> > datapath.
> > 
> > Also, I see that in  commit_set_ether_action Function
> > “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is
> > ethernet instead of base_flow->packet_type.
> > 
> > I assume check on base_flow->packet_type make more sense here ?
> > 
> > I tried to fix this issue in 2 different ways.
> > 
> > 1   I have cleared the mac address in flow structure  in PT_ETH handling
> > of xlate_generic_decap action.
> > 
> > 2  In the  commit_set_ether action I changed the check from
> > “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > htonl(PT_ETH))”.
> > 
> > Though both of them solves this problem, couple of NSH regression tests
> > are failing
> > 
> > 2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)
> > 
> > 58022292: nsh - md2 encap over a veth link                FAILED
> > (nsh.at:213)
> > 
> > I see that they are failing as they are expecting a set(eth(dst)
> > between the the pop_nsh and the recirc.
> > 
> > Set(eth) action is because of the reasons explained above –
> > 
> > Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > 
> > In my understanding set(eth) here  is wrong as there is no set ethernet
> > action in the userspace rule
> > - Hide quoted text -
> > 
> > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344,actions=decap(),decap(),2
> > 
> > 
> > 
> > Could someone comment ?
> 
> Maybe Jan can answer as he did the NSH implementation, however, what would
> be of interest if you can give me an example of how the encap() decap() for
> this would be used in real life so I’m sure I’m testing it correctly?
> 
> What I did so far was to encapsulate all traffic going from a VM to the
> physical port in MPLS using the flows 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_ds
> t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> 
> Then I would capture this traffic and sent it back over the same port,
> hoping it would come out as plane traffic with the following rule:
> 
> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> "priority=100,dl_type=0x8847,mpls_label=100
> actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> actions=normal"
> 
> If this is correct, let me know, and if Jan does not reply, I’ll try to
> understand the code in this area and see if I can find out some details…
>

You are correct.
> //Eelco
> 
> > > > > > > > > 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
>
Martin Varghese April 7, 2021, 8:43 a.m. UTC | #17
On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
> Hi,
> 
> Thanks for the heads up. The interaction with MPLS push/pop is a use case that was likely not tested during the NSH and generic encap/decap design. It's complex code and a long time ago. I'm willing to help, but I will need some time to go back and have a look.
> 
> It would definitely help, if you could provide a minimal example for reproducing the problem.
> 

Hi Jan ,

Thanks for your help.

I was trying to implement ENCAP/DECAP support for MPLS.

The programming of datapath flow for the below  userspace rule fails as there is set(eth() action between pop_mpls and recirc
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

2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-system: failed to put[create] (Invalid argument) ufid:1dddb0ba-27fe-44ea-9a99-5815764b4b9c recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1), actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)

As I pointed out in my previous mail,  in  commit_set_ether_action Function
“flow->packet_type != htonl(PT_ETH)” is used to check if the packet is ethernet instead of base_flow->packet_type.

The mac address in flow structure is not cleared in PT_ETH handling  of xlate_generic_decap_action but  it is cleared in base_flow in the
decap handling of PT_ETH in commit_encap_decap_action function
Due to this difference the set(eth(dst) action will be programmed to the datapath.

I tried to fix this issue in 2 different ways.
1   I have cleared the mac address in flow structure  in PT_ETH handling of xlate_generic_decap action.
2  In the  commit_set_ether action I changed the check from  “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type != htonl(PT_ETH))”.

Though both of them solves this problem, couple of NSH regression tests are failing

2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)
58022292: nsh - md2 encap over a veth link                FAILED (nsh.at:213)

I see that they are failing as they are expecting a set(eth(dst)  between the the pop_nsh and the recirc.
Set(eth) action is because of the reasons explained above –
Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)

In my understanding set(eth) here  is wrong as there is no set ethernet action in the userspace rule
table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344,actions=decap(),decap(),2.

To reproduce the mpls problem you could use the same test what I have added in the patch. But you have to modify the test script like below
-  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=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2, encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"


> BR, Jan
> 
> > -----Original Message-----
> > From: Eelco Chaudron <echaudro@redhat.com>
> > Sent: Tuesday, 6 April, 2021 10:55
> > To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
> > <jan.scheurich@ericsson.com>
> > Cc: dev@openvswitch.org; pshelar@ovn.org; martin.varghese@nokia.com
> > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > 
> > 
> > 
> > On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> > 
> > > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > >>
> > >>
> > >> 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.varghese@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:
> > >>>>>>>>>>
> > ffffffffffff525400885138080600010800060400015254008851380101016500000
> > 000?
> > >>>>>>>>>>
> > >>>>>>>>>>
> > >>>>>>>>>> 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(0x4
> > >>>>>>>> c)
> > >>>>>>>
> > >>>>>>> This set(eth) before the recirc is the problem i guesss. I need
> > >>>>>>> to check
> > >
> > > I could reproduce the problem. It has nothing to do with ARP or IP.
> > > Unlike my test scripts, in your test you are setting the mac address
> > > after the encap action
> > >
> > > Ovs-vswitchd is programming a set(eth(dst) action between the pop_mpls
> > > and  recirc as it sees a difference in mac address in flow structure
> > > and  base_flow structure.
> > >
> > > The mac address in flow structure is not cleared in PT_ETH handling of
> > > xlate_generic_decap_action but  it is cleared in base_flow in the
> > > decap handling of PT_ETH in commit_encap_decap_action function
> > >
> > > Due to this difference the set(eth(dst) action will be programmed to
> > > the datapath.
> > >
> > > Also, I see that in  commit_set_ether_action Function
> > > “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is
> > > ethernet instead of base_flow->packet_type.
> > >
> > > I assume check on base_flow->packet_type make more sense here ?
> > >
> > > I tried to fix this issue in 2 different ways.
> > >
> > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > handling of xlate_generic_decap action.
> > >
> > > 2  In the  commit_set_ether action I changed the check from
> > > “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > > htonl(PT_ETH))”.
> > >
> > > Though both of them solves this problem, couple of NSH regression
> > > tests are failing
> > >
> > > 2291: nsh - md1 encap over a veth link                FAILED
> > > (nsh.at:85)
> > >
> > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > (nsh.at:213)
> > >
> > > I see that they are failing as they are expecting a set(eth(dst)
> > > between the the pop_nsh and the recirc.
> > >
> > > Set(eth) action is because of the reasons explained above –
> > >
> > > Datapath actions:
> > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,
> > > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:5
> > > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > >
> > > In my understanding set(eth) here  is wrong as there is no set
> > > ethernet action in the userspace rule
> > > - Hide quoted text -
> > >
> > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
> > > 11223344,actions=decap(),decap(),2
> > >
> > >
> > >
> > > Could someone comment ?
> > 
> > Maybe Jan can answer as he did the NSH implementation, however, what
> > would be of interest if you can give me an example of how the encap()
> > decap() for this would be used in real life so I’m sure I’m testing it correctly?
> > 
> > What I did so far was to encapsulate all traffic going from a VM to the physical
> > port in MPLS using the flows 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_ds
> > t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> > 
> > Then I would capture this traffic and sent it back over the same port, hoping it
> > would come out as plane traffic with the following rule:
> > 
> > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > "priority=100,dl_type=0x8847,mpls_label=100
> > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> > actions=normal"
> > 
> > If this is correct, let me know, and if Jan does not reply, I’ll try to understand
> > the code in this area and see if I can find out some details…
> > 
> > //Eelco
> > 
> > >>>>>>>> 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:0
> > >>>>>>>> 0: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/0xfff
> > >>>> ff,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=0x8
> > >>>>>>>>>> 847)),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:e
> > >>>>>>>>>> np5s0f0
> > >>>>>>>>>>
> > >>>>>>>
> > >>>>>>>>>
> > >>>>>>>>> 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
>
Jan Scheurich April 7, 2021, 3:49 p.m. UTC | #18
Hi Martin,

I guess you are aware of the original design document we wrote for generic encap/decap and NSH support:
https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit#

It is no longer 100% aligned with the final implementation in OVS but still a good reference for understanding the design principles behind the implementation and some specifics for Ethernet and NSH encap/decap use cases.

Please find some more answers/comments below.

BR, Jan 

> -----Original Message-----
> From: Martin Varghese <martinvarghesenokia@gmail.com>
> Sent: Wednesday, 7 April, 2021 10:43
> To: Jan Scheurich <jan.scheurich@ericsson.com>
> Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
> pshelar@ovn.org; martin.varghese@nokia.com
> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> 
> On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
> > Hi,
> >
> > Thanks for the heads up. The interaction with MPLS push/pop is a use case
> that was likely not tested during the NSH and generic encap/decap design. It's
> complex code and a long time ago. I'm willing to help, but I will need some
> time to go back and have a look.
> >
> > It would definitely help, if you could provide a minimal example for
> reproducing the problem.
> >
> 
> Hi Jan ,
> 
> Thanks for your help.
> 
> I was trying to implement ENCAP/DECAP support for MPLS.
> 
> The programming of datapath flow for the below  userspace rule fails as there
> is set(eth() action between pop_mpls and recirc 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
> 
> 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
> system: failed to put[create] (Invalid argument) ufid:1dddb0ba-27fe-44ea-
> 9a99-5815764b4b9c
> recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
> actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
> 

Conceptually, what should happen in this scenario is that, after the second decap(packet_type(ns=0,type=0) action, OVS processes the unchanged inner packet as packet type PT_ETH, i.e. as L2 Ethernet frame. Overwriting the existing Ethernet header with zero values  through set(eth()) is clearly incorrect. That is a logical error inside the ofproto-dpif-xlate module (see below).

I believe the netdev userspace datapath would still have accepted the incorrect datapath flow. I have too little experience with the kernel datapath to explain why that rejects the datapath flow as invalid.

Unlike in the Ethernet and NSH cases, the MPLS header does not contain any indication about the inner packet type. That is why the packet_type must be provided by the SDN controller as part of the decap() action.  And the ofproto-dpif-xlate module must consider the specified inner packet type when continuing the translation. In the general case, a decap() action should trigger recirculation for reparsing of the inner packet, so the new packet type must be set before recirculation. (Exceptions to the general recirculation rule are those where OVS has already parsed further into the packet and ofproto can modify the flow on the fly: decap(Ethernet) and possibly decap(MPLS) for all but the last bottom of stack label).

I have had a look at your new code for encap/decap of MPLS headers, but I must admit I cannot fully judge in how far re-using the existing translation functions for MPLS label stacks written for the legacy push/pop_mpls case (i.e. manipulating a label stack between the L2 and the L3 headers of a PT_ETH Packet) are possible to re-use in the new context. 

BTW: Do you support multiple MPLS label encap or decap actions with your patch? Have you tested that? 

I am uncertain about the handling of the ethertype of the decapsulated inner packet. In the design base, the ethertype that is set in the existing L2 header of the packet after pop_mpls of the last label is coming from the pop_mpls action, while in the decap(packet_type(0,0)) case the entire inner packet should be recirculated as is with packet_type PT_ETH. 

        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;

In the example scenario the new_pkt_type is PT_ETH, so the ethertype and hence flow->dl_type will be also be set to zero. Is that intentional? For target packet types of form PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you would set the correct flow->dl_type but does this matter just before recirculation?

I don't see any logic to check if the popped label was the BoS label. Only when popping the BoS label, we should recirculate the packet for parsing the inner packet. 

Conversely, in the encap(mpls) action, the new PT should be PT_MPLS and the all other flow layers (L2, L3, L4) should be cleared. I think the reused flow_push_mpls() function doesn't take care of clearing the L2 flow fields.

Have you tested with other packet_types for inner packets (e.g. PT_IP or PT_ARP). If this is supposed to work, it would be nice to test the combination of 

   decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst

on one side against the equivalent legacy action pop_mpls on the other end and vice versa.


> As I pointed out in my previous mail,  in  commit_set_ether_action Function
> “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is ethernet
> instead of base_flow->packet_type.
> 
> The mac address in flow structure is not cleared in PT_ETH handling  of
> xlate_generic_decap_action but  it is cleared in base_flow in the decap
> handling of PT_ETH in commit_encap_decap_action function Due to this
> difference the set(eth(dst) action will be programmed to the datapath.
> 
> I tried to fix this issue in 2 different ways.
> 1   I have cleared the mac address in flow structure  in PT_ETH handling of
> xlate_generic_decap action.
> 2  In the  commit_set_ether action I changed the check from  “flow-
> >packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> htonl(PT_ETH))”.

In the light of the discussion, I think you should implement both fixes as follows:

1. Clear the mac addresses in struct flow in the PT_ETH clause of xlate_generic_decap_action().

This is correct as the flow struct is modified on the fly here and should accurately reflect the current packet headers.

2. Change the guard in commit_set_ether_action() to 

    if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type != htonl(PT_ETH)) {
        return;
    }

In either case setting the MAC header based on differences between base_flow and flow doesn't make sense.

> 
> Though both of them solves this problem, couple of NSH regression tests are
> failing
> 
> 2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)
> 58022292: nsh - md2 encap over a veth link                FAILED (nsh.at:213)
> 
> I see that they are failing as they are expecting a set(eth(dst)  between the the
> pop_nsh and the recirc.
> Set(eth) action is because of the reasons explained above – Datapath actions:
> push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c
> 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:6
> 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> 
> In my understanding set(eth) here  is wrong as there is no set ethernet action
> in the userspace rule
> table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
> 11223344,actions=decap(),decap(),2.

I think you are right here. The extra set(eth(dst)) action is a bad translation artefact triggered by the above bugs. 

The failing basic NSH test cases carry Ethernet packets inside the NSH tunnel encap, so the decap() action should just recirculate the unchanged packets with PT_ETH.

These test cases should be corrected as part of the bug-fix commit for the set(eth()) issue.

> 
> To reproduce the mpls problem you could use the same test what I have added
> in the patch. But you have to modify the test script like below
> -  table=0,priority=100,dl_type=0x0800
> actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),o
> utput:100
> + table=0,priority=100,dl_type=0x0800
> actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
> encap(ethernet),set_field:00:00:00:00:00:02-
> >dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
> 
> 
> > BR, Jan
> >
> > > -----Original Message-----
> > > From: Eelco Chaudron <echaudro@redhat.com>
> > > Sent: Tuesday, 6 April, 2021 10:55
> > > To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
> > > <jan.scheurich@ericsson.com>
> > > Cc: dev@openvswitch.org; pshelar@ovn.org; martin.varghese@nokia.com
> > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > >
> > >
> > >
> > > On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> > >
> > > > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > > >>
> > > >>
> > > >> 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.varghese@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:
> > > >>>>>>>>>>
> > >
> ffffffffffff52540088513808060001080006040001525400885138010101650
> 000
> > > 0
> > > 000?
> > > >>>>>>>>>>
> > > >>>>>>>>>>
> > > >>>>>>>>>> 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
> > > >>>>>>>> (0x4
> > > >>>>>>>> c)
> > > >>>>>>>
> > > >>>>>>> This set(eth) before the recirc is the problem i guesss. I
> > > >>>>>>> need to check
> > > >
> > > > I could reproduce the problem. It has nothing to do with ARP or IP.
> > > > Unlike my test scripts, in your test you are setting the mac
> > > > address after the encap action
> > > >
> > > > Ovs-vswitchd is programming a set(eth(dst) action between the
> > > > pop_mpls and  recirc as it sees a difference in mac address in
> > > > flow structure and  base_flow structure.
> > > >
> > > > The mac address in flow structure is not cleared in PT_ETH
> > > > handling of xlate_generic_decap_action but  it is cleared in
> > > > base_flow in the decap handling of PT_ETH in
> > > > commit_encap_decap_action function
> > > >
> > > > Due to this difference the set(eth(dst) action will be programmed
> > > > to the datapath.
> > > >
> > > > Also, I see that in  commit_set_ether_action Function
> > > > “flow->packet_type != htonl(PT_ETH)” is used to check if the
> > > > packet is ethernet instead of base_flow->packet_type.
> > > >
> > > > I assume check on base_flow->packet_type make more sense here ?
> > > >
> > > > I tried to fix this issue in 2 different ways.
> > > >
> > > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > > handling of xlate_generic_decap action.
> > > >
> > > > 2  In the  commit_set_ether action I changed the check from
> > > > “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type
> > > > != htonl(PT_ETH))”.
> > > >
> > > > Though both of them solves this problem, couple of NSH regression
> > > > tests are failing
> > > >
> > > > 2291: nsh - md1 encap over a veth link                FAILED
> > > > (nsh.at:85)
> > > >
> > > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > > (nsh.at:213)
> > > >
> > > > I see that they are failing as they are expecting a set(eth(dst)
> > > > between the the pop_nsh and the recirc.
> > > >
> > > > Set(eth) action is because of the reasons explained above –
> > > >
> > > > Datapath actions:
> > > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223
> > > > 344,
> > > > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
> > > > 44:5
> > > > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1
> > > > )
> > > >
> > > > In my understanding set(eth) here  is wrong as there is no set
> > > > ethernet action in the userspace rule
> > > > - Hide quoted text -
> > > >
> > > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c
> > > > 1=0x
> > > > 11223344,actions=decap(),decap(),2
> > > >
> > > >
> > > >
> > > > Could someone comment ?
> > >
> > > Maybe Jan can answer as he did the NSH implementation, however, what
> > > would be of interest if you can give me an example of how the
> > > encap()
> > > decap() for this would be used in real life so I’m sure I’m testing it
> correctly?
> > >
> > > What I did so far was to encapsulate all traffic going from a VM to
> > > the physical port in MPLS using the flows like:
> > >
> > > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
> > > "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),s
> > > et_mpls
> > > _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
> > > t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> > >
> > > Then I would capture this traffic and sent it back over the same
> > > port, hoping it would come out as plane traffic with the following rule:
> > >
> > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > > "priority=100,dl_type=0x8847,mpls_label=100
> > > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> > > actions=normal"
> > >
> > > If this is correct, let me know, and if Jan does not reply, I’ll try
> > > to understand the code in this area and see if I can find out some
> > > details…
> > >
> > > //Eelco
> > >
> > > >>>>>>>> 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:0
> > > >>>>>>>> 0: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/0xfff
> > > >>>> ff,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=0x8
> > > >>>>>>>>>> 847)),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:e
> > > >>>>>>>>>> np5s0f0
> > > >>>>>>>>>>
> > > >>>>>>>
> > > >>>>>>>>>
> > > >>>>>>>>> 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
> >
Martin Varghese April 8, 2021, 12:05 p.m. UTC | #19
On Wed, Apr 07, 2021 at 03:49:07PM +0000, Jan Scheurich wrote:
> Hi Martin,
> 
> I guess you are aware of the original design document we wrote for generic encap/decap and NSH support:
> https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit#
> 
> It is no longer 100% aligned with the final implementation in OVS but still a good reference for understanding the design principles behind the implementation and some specifics for Ethernet and NSH encap/decap use cases.
> 
> Please find some more answers/comments below.
> 
> BR, Jan 
> 
> > -----Original Message-----
> > From: Martin Varghese <martinvarghesenokia@gmail.com>
> > Sent: Wednesday, 7 April, 2021 10:43
> > To: Jan Scheurich <jan.scheurich@ericsson.com>
> > Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
> > pshelar@ovn.org; martin.varghese@nokia.com
> > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > 
> > On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
> > > Hi,
> > >
> > > Thanks for the heads up. The interaction with MPLS push/pop is a use case
> > that was likely not tested during the NSH and generic encap/decap design. It's
> > complex code and a long time ago. I'm willing to help, but I will need some
> > time to go back and have a look.
> > >
> > > It would definitely help, if you could provide a minimal example for
> > reproducing the problem.
> > >
> > 
> > Hi Jan ,
> > 
> > Thanks for your help.
> > 
> > I was trying to implement ENCAP/DECAP support for MPLS.
> > 
> > The programming of datapath flow for the below  userspace rule fails as there
> > is set(eth() action between pop_mpls and recirc 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
> > 
> > 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
> > system: failed to put[create] (Invalid argument) ufid:1dddb0ba-27fe-44ea-
> > 9a99-5815764b4b9c
> > recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
> > actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
> > 
> 
> Conceptually, what should happen in this scenario is that, after the second decap(packet_type(ns=0,type=0) action, OVS processes the unchanged inner packet as packet type PT_ETH, i.e. as L2 Ethernet frame. Overwriting the existing Ethernet header with zero values  through set(eth()) is clearly incorrect. That is a logical error inside the ofproto-dpif-xlate module (see below).
> 
> I believe the netdev userspace datapath would still have accepted the incorrect datapath flow. I have too little experience with the kernel datapath to explain why that rejects the datapath flow as invalid.
> 
> Unlike in the Ethernet and NSH cases, the MPLS header does not contain any indication about the inner packet type. That is why the packet_type must be provided by the SDN controller as part of the decap() action.  And the ofproto-dpif-xlate module must consider the specified inner packet type when continuing the translation. In the general case, a decap() action should trigger recirculation for reparsing of the inner packet, so the new packet type must be set before recirculation. (Exceptions to the general recirculation rule are those where OVS has already parsed further into the packet and ofproto can modify the flow on the fly: decap(Ethernet) and possibly decap(MPLS) for all but the last bottom of stack label).
> 
> I have had a look at your new code for encap/decap of MPLS headers, but I must admit I cannot fully judge in how far re-using the existing translation functions for MPLS label stacks written for the legacy push/pop_mpls case (i.e. manipulating a label stack between the L2 and the L3 headers of a PT_ETH Packet) are possible to re-use in the new context. 
> 
> BTW: Do you support multiple MPLS label encap or decap actions with your patch? Have you tested that? 
>
Yes, I will add those tests too.
> I am uncertain about the handling of the ethertype of the decapsulated inner packet. In the design base, the ethertype that is set in the existing L2 header of the packet after pop_mpls of the last label is coming from the pop_mpls action, while in the decap(packet_type(0,0)) case the entire inner packet should be recirculated as is with packet_type PT_ETH. 
> 
>         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;
> 
> In the example scenario the new_pkt_type is PT_ETH, so the ethertype and hence flow->dl_type will be also be set to zero. Is that intentional? For target packet types of form PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you would set the correct flow->dl_type but does this matter just before recirculation?
>
Yes, that was intentional.
But i am thinking now to store dl_type as 0x6558 when the new
packet_type is PT_ETH. In that way it is consistent with the datapath dl
type. I could also avoid the special handling done now  for decap MPLS in commit_mpls_action.
Setting flow->dl_type matters as this value is used as the argument for
datapath pop_mpls action
> I don't see any logic to check if the popped label was the BoS label. Only when popping the BoS label, we should recirculate the packet for parsing the inner packet. 
> 
I missed that. I will fix it
> Conversely, in the encap(mpls) action, the new PT should be PT_MPLS and the all other flow layers (L2, L3, L4) should be cleared. I think the reused flow_push_mpls() function doesn't take care of clearing the L2 flow fields.
> 
I will add that outside the flow_push_mpls
> Have you tested with other packet_types for inner packets (e.g. PT_IP or PT_ARP). If this is supposed to work, it would be nice to test the combination of 
> 
>    decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst
> 
> on one side against the equivalent legacy action pop_mpls on the other end and vice versa.
> 
Noted
> 
> > As I pointed out in my previous mail,  in  commit_set_ether_action Function
> > “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is ethernet
> > instead of base_flow->packet_type.
> > 
> > The mac address in flow structure is not cleared in PT_ETH handling  of
> > xlate_generic_decap_action but  it is cleared in base_flow in the decap
> > handling of PT_ETH in commit_encap_decap_action function Due to this
> > difference the set(eth(dst) action will be programmed to the datapath.
> > 
> > I tried to fix this issue in 2 different ways.
> > 1   I have cleared the mac address in flow structure  in PT_ETH handling of
> > xlate_generic_decap action.
> > 2  In the  commit_set_ether action I changed the check from  “flow-
> > >packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > htonl(PT_ETH))”.
> 
> In the light of the discussion, I think you should implement both fixes as follows:
> 
> 1. Clear the mac addresses in struct flow in the PT_ETH clause of xlate_generic_decap_action().
> 
> This is correct as the flow struct is modified on the fly here and should accurately reflect the current packet headers.
> 
> 2. Change the guard in commit_set_ether_action() to 
> 
>     if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type != htonl(PT_ETH)) {
>         return;
>     }
> 
> In either case setting the MAC header based on differences between base_flow and flow doesn't make sense.
> 
> > 
> > Though both of them solves this problem, couple of NSH regression tests are
> > failing
> > 
> > 2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)
> > 58022292: nsh - md2 encap over a veth link                FAILED (nsh.at:213)
> > 
> > I see that they are failing as they are expecting a set(eth(dst)  between the the
> > pop_nsh and the recirc.
> > Set(eth) action is because of the reasons explained above – Datapath actions:
> > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c
> > 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:6
> > 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > 
> > In my understanding set(eth) here  is wrong as there is no set ethernet action
> > in the userspace rule
> > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
> > 11223344,actions=decap(),decap(),2.
> 
> I think you are right here. The extra set(eth(dst)) action is a bad translation artefact triggered by the above bugs. 
> 
> The failing basic NSH test cases carry Ethernet packets inside the NSH tunnel encap, so the decap() action should just recirculate the unchanged packets with PT_ETH.
> 
> These test cases should be corrected as part of the bug-fix commit for the set(eth()) issue.
> 
I will fix it.

Thanks for your time.
> > 
> > To reproduce the mpls problem you could use the same test what I have added
> > in the patch. But you have to modify the test script like below
> > -  table=0,priority=100,dl_type=0x0800
> > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),o
> > utput:100
> > + table=0,priority=100,dl_type=0x0800
> > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
> > encap(ethernet),set_field:00:00:00:00:00:02-
> > >dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
> > 
> > 
> > > BR, Jan
> > >
> > > > -----Original Message-----
> > > > From: Eelco Chaudron <echaudro@redhat.com>
> > > > Sent: Tuesday, 6 April, 2021 10:55
> > > > To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
> > > > <jan.scheurich@ericsson.com>
> > > > Cc: dev@openvswitch.org; pshelar@ovn.org; martin.varghese@nokia.com
> > > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > > >
> > > >
> > > >
> > > > On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> > > >
> > > > > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > > > >>
> > > > >>
> > > > >> 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.varghese@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:
> > > > >>>>>>>>>>
> > > >
> > ffffffffffff52540088513808060001080006040001525400885138010101650
> > 000
> > > > 0
> > > > 000?
> > > > >>>>>>>>>>
> > > > >>>>>>>>>>
> > > > >>>>>>>>>> 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
> > > > >>>>>>>> (0x4
> > > > >>>>>>>> c)
> > > > >>>>>>>
> > > > >>>>>>> This set(eth) before the recirc is the problem i guesss. I
> > > > >>>>>>> need to check
> > > > >
> > > > > I could reproduce the problem. It has nothing to do with ARP or IP.
> > > > > Unlike my test scripts, in your test you are setting the mac
> > > > > address after the encap action
> > > > >
> > > > > Ovs-vswitchd is programming a set(eth(dst) action between the
> > > > > pop_mpls and  recirc as it sees a difference in mac address in
> > > > > flow structure and  base_flow structure.
> > > > >
> > > > > The mac address in flow structure is not cleared in PT_ETH
> > > > > handling of xlate_generic_decap_action but  it is cleared in
> > > > > base_flow in the decap handling of PT_ETH in
> > > > > commit_encap_decap_action function
> > > > >
> > > > > Due to this difference the set(eth(dst) action will be programmed
> > > > > to the datapath.
> > > > >
> > > > > Also, I see that in  commit_set_ether_action Function
> > > > > “flow->packet_type != htonl(PT_ETH)” is used to check if the
> > > > > packet is ethernet instead of base_flow->packet_type.
> > > > >
> > > > > I assume check on base_flow->packet_type make more sense here ?
> > > > >
> > > > > I tried to fix this issue in 2 different ways.
> > > > >
> > > > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > > > handling of xlate_generic_decap action.
> > > > >
> > > > > 2  In the  commit_set_ether action I changed the check from
> > > > > “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type
> > > > > != htonl(PT_ETH))”.
> > > > >
> > > > > Though both of them solves this problem, couple of NSH regression
> > > > > tests are failing
> > > > >
> > > > > 2291: nsh - md1 encap over a veth link                FAILED
> > > > > (nsh.at:85)
> > > > >
> > > > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > > > (nsh.at:213)
> > > > >
> > > > > I see that they are failing as they are expecting a set(eth(dst)
> > > > > between the the pop_nsh and the recirc.
> > > > >
> > > > > Set(eth) action is because of the reasons explained above –
> > > > >
> > > > > Datapath actions:
> > > > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223
> > > > > 344,
> > > > > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
> > > > > 44:5
> > > > > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1
> > > > > )
> > > > >
> > > > > In my understanding set(eth) here  is wrong as there is no set
> > > > > ethernet action in the userspace rule
> > > > > - Hide quoted text -
> > > > >
> > > > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c
> > > > > 1=0x
> > > > > 11223344,actions=decap(),decap(),2
> > > > >
> > > > >
> > > > >
> > > > > Could someone comment ?
> > > >
> > > > Maybe Jan can answer as he did the NSH implementation, however, what
> > > > would be of interest if you can give me an example of how the
> > > > encap()
> > > > decap() for this would be used in real life so I’m sure I’m testing it
> > correctly?
> > > >
> > > > What I did so far was to encapsulate all traffic going from a VM to
> > > > the physical port in MPLS using the flows like:
> > > >
> > > > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
> > > > "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),s
> > > > et_mpls
> > > > _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
> > > > t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> > > >
> > > > Then I would capture this traffic and sent it back over the same
> > > > port, hoping it would come out as plane traffic with the following rule:
> > > >
> > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > > > "priority=100,dl_type=0x8847,mpls_label=100
> > > > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> > > > actions=normal"
> > > >
> > > > If this is correct, let me know, and if Jan does not reply, I’ll try
> > > > to understand the code in this area and see if I can find out some
> > > > details…
> > > >
> > > > //Eelco
> > > >
> > > > >>>>>>>> 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:0
> > > > >>>>>>>> 0: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/0xfff
> > > > >>>> ff,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=0x8
> > > > >>>>>>>>>> 847)),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:e
> > > > >>>>>>>>>> np5s0f0
> > > > >>>>>>>>>>
> > > > >>>>>>>
> > > > >>>>>>>>>
> > > > >>>>>>>>> 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
> > >
Eelco Chaudron April 8, 2021, 1:31 p.m. UTC | #20
On 8 Apr 2021, at 14:05, Martin Varghese wrote:

> On Wed, Apr 07, 2021 at 03:49:07PM +0000, Jan Scheurich wrote:
>> Hi Martin,
>>
>> I guess you are aware of the original design document we wrote for 
>> generic encap/decap and NSH support:
>> https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit#
>>
>> It is no longer 100% aligned with the final implementation in OVS but 
>> still a good reference for understanding the design principles behind 
>> the implementation and some specifics for Ethernet and NSH 
>> encap/decap use cases.
>>
>> Please find some more answers/comments below.
>>
>> BR, Jan
>>
>>> -----Original Message-----
>>> From: Martin Varghese <martinvarghesenokia@gmail.com>
>>> Sent: Wednesday, 7 April, 2021 10:43
>>> To: Jan Scheurich <jan.scheurich@ericsson.com>
>>> Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
>>> pshelar@ovn.org; martin.varghese@nokia.com
>>> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet 
>>> type.
>>>
>>> On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
>>>> Hi,
>>>>
>>>> Thanks for the heads up. The interaction with MPLS push/pop is a 
>>>> use case
>>> that was likely not tested during the NSH and generic encap/decap 
>>> design. It's
>>> complex code and a long time ago. I'm willing to help, but I will 
>>> need some
>>> time to go back and have a look.
>>>>
>>>> It would definitely help, if you could provide a minimal example 
>>>> for
>>> reproducing the problem.
>>>>
>>>
>>> Hi Jan ,
>>>
>>> Thanks for your help.
>>>
>>> I was trying to implement ENCAP/DECAP support for MPLS.
>>>
>>> The programming of datapath flow for the below  userspace rule fails 
>>> as there
>>> is set(eth() action between pop_mpls and recirc 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
>>>
>>> 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
>>> system: failed to put[create] (Invalid argument) 
>>> ufid:1dddb0ba-27fe-44ea-
>>> 9a99-5815764b4b9c
>>> recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
>>> actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
>>>
>>
>> Conceptually, what should happen in this scenario is that, after the 
>> second decap(packet_type(ns=0,type=0) action, OVS processes the 
>> unchanged inner packet as packet type PT_ETH, i.e. as L2 Ethernet 
>> frame. Overwriting the existing Ethernet header with zero values  
>> through set(eth()) is clearly incorrect. That is a logical error 
>> inside the ofproto-dpif-xlate module (see below).
>>
>> I believe the netdev userspace datapath would still have accepted the 
>> incorrect datapath flow. I have too little experience with the kernel 
>> datapath to explain why that rejects the datapath flow as invalid.
>>
>> Unlike in the Ethernet and NSH cases, the MPLS header does not 
>> contain any indication about the inner packet type. That is why the 
>> packet_type must be provided by the SDN controller as part of the 
>> decap() action.  And the ofproto-dpif-xlate module must consider the 
>> specified inner packet type when continuing the translation. In the 
>> general case, a decap() action should trigger recirculation for 
>> reparsing of the inner packet, so the new packet type must be set 
>> before recirculation. (Exceptions to the general recirculation rule 
>> are those where OVS has already parsed further into the packet and 
>> ofproto can modify the flow on the fly: decap(Ethernet) and possibly 
>> decap(MPLS) for all but the last bottom of stack label).
>>
>> I have had a look at your new code for encap/decap of MPLS headers, 
>> but I must admit I cannot fully judge in how far re-using the 
>> existing translation functions for MPLS label stacks written for the 
>> legacy push/pop_mpls case (i.e. manipulating a label stack between 
>> the L2 and the L3 headers of a PT_ETH Packet) are possible to re-use 
>> in the new context.
>>
>> BTW: Do you support multiple MPLS label encap or decap actions with 
>> your patch? Have you tested that?
>>
> Yes, I will add those tests too.

Maybe you could add some tests the same way NSH does, by sending a 
packet and verifying the modified content. The current test does encap 
than decap, so if both go wrong, or are skipped we are not actually 
testing anything.

>> I am uncertain about the handling of the ethertype of the 
>> decapsulated inner packet. In the design base, the ethertype that is 
>> set in the existing L2 header of the packet after pop_mpls of the 
>> last label is coming from the pop_mpls action, while in the 
>> decap(packet_type(0,0)) case the entire inner packet should be 
>> recirculated as is with packet_type PT_ETH.
>>
>>         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;
>>
>> In the example scenario the new_pkt_type is PT_ETH, so the ethertype 
>> and hence flow->dl_type will be also be set to zero. Is that 
>> intentional? For target packet types of form 
>> PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you would set the correct 
>> flow->dl_type but does this matter just before recirculation?
>>
> Yes, that was intentional.
> But i am thinking now to store dl_type as 0x6558 when the new
> packet_type is PT_ETH. In that way it is consistent with the datapath 
> dl
> type. I could also avoid the special handling done now  for decap MPLS 
> in commit_mpls_action.
> Setting flow->dl_type matters as this value is used as the argument 
> for
> datapath pop_mpls action
>> I don't see any logic to check if the popped label was the BoS label. 
>> Only when popping the BoS label, we should recirculate the packet for 
>> parsing the inner packet.
>>
> I missed that. I will fix it
>> Conversely, in the encap(mpls) action, the new PT should be PT_MPLS 
>> and the all other flow layers (L2, L3, L4) should be cleared. I think 
>> the reused flow_push_mpls() function doesn't take care of clearing 
>> the L2 flow fields.
>>
> I will add that outside the flow_push_mpls
>> Have you tested with other packet_types for inner packets (e.g. PT_IP 
>> or PT_ARP). If this is supposed to work, it would be nice to test the 
>> combination of
>>
>>    decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst
>>
>> on one side against the equivalent legacy action pop_mpls on the 
>> other end and vice versa.
>>
> Noted
>>
>>> As I pointed out in my previous mail,  in  commit_set_ether_action 
>>> Function
>>> “flow->packet_type != htonl(PT_ETH)” is used to check if the 
>>> packet is ethernet
>>> instead of base_flow->packet_type.
>>>
>>> The mac address in flow structure is not cleared in PT_ETH handling  
>>> of
>>> xlate_generic_decap_action but  it is cleared in base_flow in the 
>>> decap
>>> handling of PT_ETH in commit_encap_decap_action function Due to this
>>> difference the set(eth(dst) action will be programmed to the 
>>> datapath.
>>>
>>> I tried to fix this issue in 2 different ways.
>>> 1   I have cleared the mac address in flow structure  in PT_ETH 
>>> handling of
>>> xlate_generic_decap action.
>>> 2  In the  commit_set_ether action I changed the check from  
>>> “flow-
>>>> packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
>>> htonl(PT_ETH))”.
>>
>> In the light of the discussion, I think you should implement both 
>> fixes as follows:
>>
>> 1. Clear the mac addresses in struct flow in the PT_ETH clause of 
>> xlate_generic_decap_action().
>>
>> This is correct as the flow struct is modified on the fly here and 
>> should accurately reflect the current packet headers.
>>
>> 2. Change the guard in commit_set_ether_action() to
>>
>>     if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type 
>> != htonl(PT_ETH)) {
>>         return;
>>     }
>>
>> In either case setting the MAC header based on differences between 
>> base_flow and flow doesn't make sense.
>>
>>>
>>> Though both of them solves this problem, couple of NSH regression 
>>> tests are
>>> failing
>>>
>>> 2291: nsh - md1 encap over a veth link                FAILED 
>>> (nsh.at:85)
>>> 58022292: nsh - md2 encap over a veth link                FAILED 
>>> (nsh.at:213)
>>>
>>> I see that they are failing as they are expecting a set(eth(dst)  
>>> between the the
>>> pop_nsh and the recirc.
>>> Set(eth) action is because of the reasons explained above – 
>>> Datapath actions:
>>> push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c
>>> 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:6
>>> 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
>>>
>>> In my understanding set(eth) here  is wrong as there is no set 
>>> ethernet action
>>> in the userspace rule
>>> table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
>>> 11223344,actions=decap(),decap(),2.
>>
>> I think you are right here. The extra set(eth(dst)) action is a bad 
>> translation artefact triggered by the above bugs.
>>
>> The failing basic NSH test cases carry Ethernet packets inside the 
>> NSH tunnel encap, so the decap() action should just recirculate the 
>> unchanged packets with PT_ETH.
>>
>> These test cases should be corrected as part of the bug-fix commit 
>> for the set(eth()) issue.
>>
> I will fix it.
>
> Thanks for your time.

Copy me on the new version and I continue my review.
Here are some small nits you might be able to fix in your next revision:

> +++ 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);

Guess this can be on one line like all the other case statements as it 
fits the 80 character limit.

> 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 @@
...
> @@ -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.

Can you add an explanation for name_space and type? As it’s not clear 
to me how these values are used.

>>>
>>> To reproduce the mpls problem you could use the same test what I 
>>> have added
>>> in the patch. But you have to modify the test script like below
>>> -  table=0,priority=100,dl_type=0x0800
>>> actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),o
>>> utput:100
>>> + table=0,priority=100,dl_type=0x0800
>>> actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
>>> encap(ethernet),set_field:00:00:00:00:00:02-
>>>> dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
>>>
>>>
>>>> BR, Jan
>>>>
>>>>> -----Original Message-----
>>>>> From: Eelco Chaudron <echaudro@redhat.com>
>>>>> Sent: Tuesday, 6 April, 2021 10:55
>>>>> To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
>>>>> <jan.scheurich@ericsson.com>
>>>>> Cc: dev@openvswitch.org; pshelar@ovn.org; 
>>>>> martin.varghese@nokia.com
>>>>> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet 
>>>>> type.
>>>>>
>>>>>
>>>>>
>>>>> On 6 Apr 2021, at 10:27, Martin Varghese wrote:
>>>>>
>>>>>> On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
>>>>>>>
>>>>>>>
>>>>>>> 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.varghese@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:
>>>>>>>>>>>>>>>
>>>>>
>>> ffffffffffff52540088513808060001080006040001525400885138010101650
>>> 000
>>>>> 0
>>>>> 000?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> 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
>>>>>>>>>>>>> (0x4
>>>>>>>>>>>>> c)
>>>>>>>>>>>>
>>>>>>>>>>>> This set(eth) before the recirc is the problem i guesss. I
>>>>>>>>>>>> need to check
>>>>>>
>>>>>> I could reproduce the problem. It has nothing to do with ARP or 
>>>>>> IP.
>>>>>> Unlike my test scripts, in your test you are setting the mac
>>>>>> address after the encap action
>>>>>>
>>>>>> Ovs-vswitchd is programming a set(eth(dst) action between the
>>>>>> pop_mpls and  recirc as it sees a difference in mac address in
>>>>>> flow structure and  base_flow structure.
>>>>>>
>>>>>> The mac address in flow structure is not cleared in PT_ETH
>>>>>> handling of xlate_generic_decap_action but  it is cleared in
>>>>>> base_flow in the decap handling of PT_ETH in
>>>>>> commit_encap_decap_action function
>>>>>>
>>>>>> Due to this difference the set(eth(dst) action will be programmed
>>>>>> to the datapath.
>>>>>>
>>>>>> Also, I see that in  commit_set_ether_action Function
>>>>>> “flow->packet_type != htonl(PT_ETH)” is used to check if the
>>>>>> packet is ethernet instead of base_flow->packet_type.
>>>>>>
>>>>>> I assume check on base_flow->packet_type make more sense here ?
>>>>>>
>>>>>> I tried to fix this issue in 2 different ways.
>>>>>>
>>>>>> 1   I have cleared the mac address in flow structure  in PT_ETH
>>>>>> handling of xlate_generic_decap action.
>>>>>>
>>>>>> 2  In the  commit_set_ether action I changed the check from
>>>>>> “flow->packet_type != htonl(PT_ETH)” to  
>>>>>> “base_flow->packet_type
>>>>>> != htonl(PT_ETH))”.
>>>>>>
>>>>>> Though both of them solves this problem, couple of NSH regression
>>>>>> tests are failing
>>>>>>
>>>>>> 2291: nsh - md1 encap over a veth link                FAILED
>>>>>> (nsh.at:85)
>>>>>>
>>>>>> 58022292: nsh - md2 encap over a veth link                FAILED
>>>>>> (nsh.at:213)
>>>>>>
>>>>>> I see that they are failing as they are expecting a set(eth(dst)
>>>>>> between the the pop_nsh and the recirc.
>>>>>>
>>>>>> Set(eth) action is because of the reasons explained above –
>>>>>>
>>>>>> Datapath actions:
>>>>>> push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223
>>>>>> 344,
>>>>>> c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
>>>>>> 44:5
>>>>>> 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1
>>>>>> )
>>>>>>
>>>>>> In my understanding set(eth) here  is wrong as there is no set
>>>>>> ethernet action in the userspace rule
>>>>>> - Hide quoted text -
>>>>>>
>>>>>> table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c
>>>>>> 1=0x
>>>>>> 11223344,actions=decap(),decap(),2
>>>>>>
>>>>>>
>>>>>>
>>>>>> Could someone comment ?
>>>>>
>>>>> Maybe Jan can answer as he did the NSH implementation, however, 
>>>>> what
>>>>> would be of interest if you can give me an example of how the
>>>>> encap()
>>>>> decap() for this would be used in real life so I’m sure I’m 
>>>>> testing it
>>> correctly?
>>>>>
>>>>> What I did so far was to encapsulate all traffic going from a VM 
>>>>> to
>>>>> the physical port in MPLS using the flows like:
>>>>>
>>>>> ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
>>>>> "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),s
>>>>> et_mpls
>>>>> _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
>>>>> t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
>>>>>
>>>>> Then I would capture this traffic and sent it back over the same
>>>>> port, hoping it would come out as plane traffic with the following 
>>>>> rule:
>>>>>
>>>>> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
>>>>> "priority=100,dl_type=0x8847,mpls_label=100
>>>>> actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
>>>>> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
>>>>> actions=normal"
>>>>>
>>>>> If this is correct, let me know, and if Jan does not reply, I’ll 
>>>>> try
>>>>> to understand the code in this area and see if I can find out some
>>>>> details…
>>>>>
>>>>> //Eelco
>>>>>
>>>>>>>>>>>>> 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:0
>>>>>>>>>>>>> 0: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/0xfff
>>>>>>>>> ff,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=0x8
>>>>>>>>>>>>>>> 847)),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:e
>>>>>>>>>>>>>>> np5s0f0
>>>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> 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
>>>>
Martin Varghese April 13, 2021, 2:19 p.m. UTC | #21
On Wed, Apr 07, 2021 at 03:49:07PM +0000, Jan Scheurich wrote:
> Hi Martin,
> 
> I guess you are aware of the original design document we wrote for generic encap/decap and NSH support:
> https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit#
> 
> It is no longer 100% aligned with the final implementation in OVS but still a good reference for understanding the design principles behind the implementation and some specifics for Ethernet and NSH encap/decap use cases.
> 
> Please find some more answers/comments below.
> 
> BR, Jan 
> 
> > -----Original Message-----
> > From: Martin Varghese <martinvarghesenokia@gmail.com>
> > Sent: Wednesday, 7 April, 2021 10:43
> > To: Jan Scheurich <jan.scheurich@ericsson.com>
> > Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
> > pshelar@ovn.org; martin.varghese@nokia.com
> > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > 
> > On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
> > > Hi,
> > >
> > > Thanks for the heads up. The interaction with MPLS push/pop is a use case
> > that was likely not tested during the NSH and generic encap/decap design. It's
> > complex code and a long time ago. I'm willing to help, but I will need some
> > time to go back and have a look.
> > >
> > > It would definitely help, if you could provide a minimal example for
> > reproducing the problem.
> > >
> > 
> > Hi Jan ,
> > 
> > Thanks for your help.
> > 
> > I was trying to implement ENCAP/DECAP support for MPLS.
> > 
> > The programming of datapath flow for the below  userspace rule fails as there
> > is set(eth() action between pop_mpls and recirc 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
> > 
> > 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
> > system: failed to put[create] (Invalid argument) ufid:1dddb0ba-27fe-44ea-
> > 9a99-5815764b4b9c
> > recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
> > actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
> > 
> 
> Conceptually, what should happen in this scenario is that, after the second decap(packet_type(ns=0,type=0) action, OVS processes the unchanged inner packet as packet type PT_ETH, i.e. as L2 Ethernet frame. Overwriting the existing Ethernet header with zero values  through set(eth()) is clearly incorrect. That is a logical error inside the ofproto-dpif-xlate module (see below).
> 
> I believe the netdev userspace datapath would still have accepted the incorrect datapath flow. I have too little experience with the kernel datapath to explain why that rejects the datapath flow as invalid.
> 
> Unlike in the Ethernet and NSH cases, the MPLS header does not contain any indication about the inner packet type. That is why the packet_type must be provided by the SDN controller as part of the decap() action.  And the ofproto-dpif-xlate module must consider the specified inner packet type when continuing the translation. In the general case, a decap() action should trigger recirculation for reparsing of the inner packet, so the new packet type must be set before recirculation. (Exceptions to the general recirculation rule are those where OVS has already parsed further into the packet and ofproto can modify the flow on the fly: decap(Ethernet) and possibly decap(MPLS) for all but the last bottom of stack label).
> 
> I have had a look at your new code for encap/decap of MPLS headers, but I must admit I cannot fully judge in how far re-using the existing translation functions for MPLS label stacks written for the legacy push/pop_mpls case (i.e. manipulating a label stack between the L2 and the L3 headers of a PT_ETH Packet) are possible to re-use in the new context. 
> 
> BTW: Do you support multiple MPLS label encap or decap actions with your patch? Have you tested that? 
> 
> I am uncertain about the handling of the ethertype of the decapsulated inner packet. In the design base, the ethertype that is set in the existing L2 header of the packet after pop_mpls of the last label is coming from the pop_mpls action, while in the decap(packet_type(0,0)) case the entire inner packet should be recirculated as is with packet_type PT_ETH. 
> 
>         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;
> 
> In the example scenario the new_pkt_type is PT_ETH, so the ethertype and hence flow->dl_type will be also be set to zero. Is that intentional? For target packet types of form PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you would set the correct flow->dl_type but does this matter just before recirculation?
> 
> I don't see any logic to check if the popped label was the BoS label. Only when popping the BoS label, we should recirculate the packet for parsing the inner packet. 
> 
> Conversely, in the encap(mpls) action, the new PT should be PT_MPLS and the all other flow layers (L2, L3, L4) should be cleared. I think the reused flow_push_mpls() function doesn't take care of clearing the L2 flow fields.
> 
> Have you tested with other packet_types for inner packets (e.g. PT_IP or PT_ARP). If this is supposed to work, it would be nice to test the combination of 
> 
>    decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst
> 
> on one side against the equivalent legacy action pop_mpls on the other end and vice versa.
> 
> 
> > As I pointed out in my previous mail,  in  commit_set_ether_action Function
> > “flow->packet_type != htonl(PT_ETH)” is used to check if the packet is ethernet
> > instead of base_flow->packet_type.
> > 
> > The mac address in flow structure is not cleared in PT_ETH handling  of
> > xlate_generic_decap_action but  it is cleared in base_flow in the decap
> > handling of PT_ETH in commit_encap_decap_action function Due to this
> > difference the set(eth(dst) action will be programmed to the datapath.
> > 
> > I tried to fix this issue in 2 different ways.
> > 1   I have cleared the mac address in flow structure  in PT_ETH handling of
> > xlate_generic_decap action.
> > 2  In the  commit_set_ether action I changed the check from  “flow-
> > >packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > htonl(PT_ETH))”.
> 
> In the light of the discussion, I think you should implement both fixes as follows:
> 
> 1. Clear the mac addresses in struct flow in the PT_ETH clause of xlate_generic_decap_action().
> 
> This is correct as the flow struct is modified on the fly here and should accurately reflect the current packet headers.
> 
> 2. Change the guard in commit_set_ether_action() to 
> 
>     if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type != htonl(PT_ETH)) {
>         return;
>     }
>
Do we need to check for both flow->packet_type & base_flow->packet_type?
I understand we just need to  check only for  "base_flow->packet_type != htonl(PT_ETH))". Could you please check again 
> In either case setting the MAC header based on differences between base_flow and flow doesn't make sense.
> 
> > 
> > Though both of them solves this problem, couple of NSH regression tests are
> > failing
> > 
> > 2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)
> > 58022292: nsh - md2 encap over a veth link                FAILED (nsh.at:213)
> > 
> > I see that they are failing as they are expecting a set(eth(dst)  between the the
> > pop_nsh and the recirc.
> > Set(eth) action is because of the reasons explained above – Datapath actions:
> > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c
> > 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:6
> > 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > 
> > In my understanding set(eth) here  is wrong as there is no set ethernet action
> > in the userspace rule
> > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
> > 11223344,actions=decap(),decap(),2.
> 
> I think you are right here. The extra set(eth(dst)) action is a bad translation artefact triggered by the above bugs. 
> 
> The failing basic NSH test cases carry Ethernet packets inside the NSH tunnel encap, so the decap() action should just recirculate the unchanged packets with PT_ETH.
> 
> These test cases should be corrected as part of the bug-fix commit for the set(eth()) issue.
> 
> > 
> > To reproduce the mpls problem you could use the same test what I have added
> > in the patch. But you have to modify the test script like below
> > -  table=0,priority=100,dl_type=0x0800
> > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),o
> > utput:100
> > + table=0,priority=100,dl_type=0x0800
> > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
> > encap(ethernet),set_field:00:00:00:00:00:02-
> > >dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
> > 
> > 
> > > BR, Jan
> > >
> > > > -----Original Message-----
> > > > From: Eelco Chaudron <echaudro@redhat.com>
> > > > Sent: Tuesday, 6 April, 2021 10:55
> > > > To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
> > > > <jan.scheurich@ericsson.com>
> > > > Cc: dev@openvswitch.org; pshelar@ovn.org; martin.varghese@nokia.com
> > > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > > >
> > > >
> > > >
> > > > On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> > > >
> > > > > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > > > >>
> > > > >>
> > > > >> 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.varghese@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:
> > > > >>>>>>>>>>
> > > >
> > ffffffffffff52540088513808060001080006040001525400885138010101650
> > 000
> > > > 0
> > > > 000?
> > > > >>>>>>>>>>
> > > > >>>>>>>>>>
> > > > >>>>>>>>>> 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
> > > > >>>>>>>> (0x4
> > > > >>>>>>>> c)
> > > > >>>>>>>
> > > > >>>>>>> This set(eth) before the recirc is the problem i guesss. I
> > > > >>>>>>> need to check
> > > > >
> > > > > I could reproduce the problem. It has nothing to do with ARP or IP.
> > > > > Unlike my test scripts, in your test you are setting the mac
> > > > > address after the encap action
> > > > >
> > > > > Ovs-vswitchd is programming a set(eth(dst) action between the
> > > > > pop_mpls and  recirc as it sees a difference in mac address in
> > > > > flow structure and  base_flow structure.
> > > > >
> > > > > The mac address in flow structure is not cleared in PT_ETH
> > > > > handling of xlate_generic_decap_action but  it is cleared in
> > > > > base_flow in the decap handling of PT_ETH in
> > > > > commit_encap_decap_action function
> > > > >
> > > > > Due to this difference the set(eth(dst) action will be programmed
> > > > > to the datapath.
> > > > >
> > > > > Also, I see that in  commit_set_ether_action Function
> > > > > “flow->packet_type != htonl(PT_ETH)” is used to check if the
> > > > > packet is ethernet instead of base_flow->packet_type.
> > > > >
> > > > > I assume check on base_flow->packet_type make more sense here ?
> > > > >
> > > > > I tried to fix this issue in 2 different ways.
> > > > >
> > > > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > > > handling of xlate_generic_decap action.
> > > > >
> > > > > 2  In the  commit_set_ether action I changed the check from
> > > > > “flow->packet_type != htonl(PT_ETH)” to  “base_flow->packet_type
> > > > > != htonl(PT_ETH))”.
> > > > >
> > > > > Though both of them solves this problem, couple of NSH regression
> > > > > tests are failing
> > > > >
> > > > > 2291: nsh - md1 encap over a veth link                FAILED
> > > > > (nsh.at:85)
> > > > >
> > > > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > > > (nsh.at:213)
> > > > >
> > > > > I see that they are failing as they are expecting a set(eth(dst)
> > > > > between the the pop_nsh and the recirc.
> > > > >
> > > > > Set(eth) action is because of the reasons explained above –
> > > > >
> > > > > Datapath actions:
> > > > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223
> > > > > 344,
> > > > > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
> > > > > 44:5
> > > > > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1
> > > > > )
> > > > >
> > > > > In my understanding set(eth) here  is wrong as there is no set
> > > > > ethernet action in the userspace rule
> > > > > - Hide quoted text -
> > > > >
> > > > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c
> > > > > 1=0x
> > > > > 11223344,actions=decap(),decap(),2
> > > > >
> > > > >
> > > > >
> > > > > Could someone comment ?
> > > >
> > > > Maybe Jan can answer as he did the NSH implementation, however, what
> > > > would be of interest if you can give me an example of how the
> > > > encap()
> > > > decap() for this would be used in real life so I’m sure I’m testing it
> > correctly?
> > > >
> > > > What I did so far was to encapsulate all traffic going from a VM to
> > > > the physical port in MPLS using the flows like:
> > > >
> > > > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
> > > > "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),s
> > > > et_mpls
> > > > _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
> > > > t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> > > >
> > > > Then I would capture this traffic and sent it back over the same
> > > > port, hoping it would come out as plane traffic with the following rule:
> > > >
> > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > > > "priority=100,dl_type=0x8847,mpls_label=100
> > > > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> > > > actions=normal"
> > > >
> > > > If this is correct, let me know, and if Jan does not reply, I’ll try
> > > > to understand the code in this area and see if I can find out some
> > > > details…
> > > >
> > > > //Eelco
> > > >
> > > > >>>>>>>> 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:0
> > > > >>>>>>>> 0: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/0xfff
> > > > >>>> ff,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=0x8
> > > > >>>>>>>>>> 847)),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:e
> > > > >>>>>>>>>> np5s0f0
> > > > >>>>>>>>>>
> > > > >>>>>>>
> > > > >>>>>>>>>
> > > > >>>>>>>>> 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
> > >
Jan Scheurich April 17, 2021, 10:11 p.m. UTC | #22
> -----Original Message-----
> From: Martin Varghese <martinvarghesenokia@gmail.com>
> Sent: Tuesday, 13 April, 2021 16:20
> To: Jan Scheurich <jan.scheurich@ericsson.com>
> Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
> pshelar@ovn.org; martin.varghese@nokia.com
> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> 
> On Wed, Apr 07, 2021 at 03:49:07PM +0000, Jan Scheurich wrote:
> > Hi Martin,
> >
> > I guess you are aware of the original design document we wrote for generic
> encap/decap and NSH support:
> > https://protect2.fireeye.com/v1/url?k=993ba795-c6a09d8c-993be70e-8682a
> > aa22bc0-3c9b4464027ca7bf&q=1&e=f89fc25e-8dc0-45bf-bdd9-
> 2d0ca03b5686&u=
> >
> https%3A%2F%2Fdocs.google.com%2Fdocument%2Fd%2F1oWMYUH8sjZJzWa72
> o2q9kU
> > 0N6pNE-rwZcLH3-kbbDR8%2Fedit%23
> >
> > It is no longer 100% aligned with the final implementation in OVS but still a
> good reference for understanding the design principles behind the
> implementation and some specifics for Ethernet and NSH encap/decap use
> cases.
> >
> > Please find some more answers/comments below.
> >
> > BR, Jan
> >
> > > -----Original Message-----
> > > From: Martin Varghese <martinvarghesenokia@gmail.com>
> > > Sent: Wednesday, 7 April, 2021 10:43
> > > To: Jan Scheurich <jan.scheurich@ericsson.com>
> > > Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
> > > pshelar@ovn.org; martin.varghese@nokia.com
> > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
> > >
> > > On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
> > > > Hi,
> > > >
> > > > Thanks for the heads up. The interaction with MPLS push/pop is a
> > > > use case
> > > that was likely not tested during the NSH and generic encap/decap
> > > design. It's complex code and a long time ago. I'm willing to help,
> > > but I will need some time to go back and have a look.
> > > >
> > > > It would definitely help, if you could provide a minimal example
> > > > for
> > > reproducing the problem.
> > > >
> > >
> > > Hi Jan ,
> > >
> > > Thanks for your help.
> > >
> > > I was trying to implement ENCAP/DECAP support for MPLS.
> > >
> > > The programming of datapath flow for the below  userspace rule fails
> > > as there is set(eth() action between pop_mpls and recirc 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
> > >
> > > 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
> > > system: failed to put[create] (Invalid argument)
> > > ufid:1dddb0ba-27fe-44ea- 9a99-5815764b4b9c
> > > recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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(0x8
> > > 847) ,mpls(label=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
> > > actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
> > >
> >
> > Conceptually, what should happen in this scenario is that, after the second
> decap(packet_type(ns=0,type=0) action, OVS processes the unchanged inner
> packet as packet type PT_ETH, i.e. as L2 Ethernet frame. Overwriting the
> existing Ethernet header with zero values  through set(eth()) is clearly
> incorrect. That is a logical error inside the ofproto-dpif-xlate module (see
> below).
> >
> > I believe the netdev userspace datapath would still have accepted the
> incorrect datapath flow. I have too little experience with the kernel datapath to
> explain why that rejects the datapath flow as invalid.
> >
> > Unlike in the Ethernet and NSH cases, the MPLS header does not contain any
> indication about the inner packet type. That is why the packet_type must be
> provided by the SDN controller as part of the decap() action.  And the ofproto-
> dpif-xlate module must consider the specified inner packet type when
> continuing the translation. In the general case, a decap() action should trigger
> recirculation for reparsing of the inner packet, so the new packet type must be
> set before recirculation. (Exceptions to the general recirculation rule are those
> where OVS has already parsed further into the packet and ofproto can modify
> the flow on the fly: decap(Ethernet) and possibly decap(MPLS) for all but the
> last bottom of stack label).
> >
> > I have had a look at your new code for encap/decap of MPLS headers, but I
> must admit I cannot fully judge in how far re-using the existing translation
> functions for MPLS label stacks written for the legacy push/pop_mpls case (i.e.
> manipulating a label stack between the L2 and the L3 headers of a PT_ETH
> Packet) are possible to re-use in the new context.
> >
> > BTW: Do you support multiple MPLS label encap or decap actions with your
> patch? Have you tested that?
> >
> > I am uncertain about the handling of the ethertype of the decapsulated inner
> packet. In the design base, the ethertype that is set in the existing L2 header of
> the packet after pop_mpls of the last label is coming from the pop_mpls action,
> while in the decap(packet_type(0,0)) case the entire inner packet should be
> recirculated as is with packet_type PT_ETH.
> >
> >         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;
> >
> > In the example scenario the new_pkt_type is PT_ETH, so the ethertype and
> hence flow->dl_type will be also be set to zero. Is that intentional? For target
> packet types of form PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you
> would set the correct flow->dl_type but does this matter just before
> recirculation?
> >
> > I don't see any logic to check if the popped label was the BoS label. Only
> when popping the BoS label, we should recirculate the packet for parsing the
> inner packet.
> >
> > Conversely, in the encap(mpls) action, the new PT should be PT_MPLS and the
> all other flow layers (L2, L3, L4) should be cleared. I think the reused
> flow_push_mpls() function doesn't take care of clearing the L2 flow fields.
> >
> > Have you tested with other packet_types for inner packets (e.g. PT_IP
> > or PT_ARP). If this is supposed to work, it would be nice to test the
> > combination of
> >
> >    decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst
> >
> > on one side against the equivalent legacy action pop_mpls on the other end
> and vice versa.
> >
> >
> > > As I pointed out in my previous mail,  in  commit_set_ether_action
> > > Function “flow->packet_type != htonl(PT_ETH)” is used to check if
> > > the packet is ethernet instead of base_flow->packet_type.
> > >
> > > The mac address in flow structure is not cleared in PT_ETH handling
> > > of xlate_generic_decap_action but  it is cleared in base_flow in the
> > > decap handling of PT_ETH in commit_encap_decap_action function Due
> > > to this difference the set(eth(dst) action will be programmed to the
> datapath.
> > >
> > > I tried to fix this issue in 2 different ways.
> > > 1   I have cleared the mac address in flow structure  in PT_ETH handling of
> > > xlate_generic_decap action.
> > > 2  In the  commit_set_ether action I changed the check from  “flow-
> > > >packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > > htonl(PT_ETH))”.
> >
> > In the light of the discussion, I think you should implement both fixes as
> follows:
> >
> > 1. Clear the mac addresses in struct flow in the PT_ETH clause of
> xlate_generic_decap_action().
> >
> > This is correct as the flow struct is modified on the fly here and should
> accurately reflect the current packet headers.
> >
> > 2. Change the guard in commit_set_ether_action() to
> >
> >     if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type !=
> htonl(PT_ETH)) {
> >         return;
> >     }
> >
> Do we need to check for both flow->packet_type & base_flow->packet_type?
> I understand we just need to  check only for  "base_flow->packet_type !=
> htonl(PT_ETH))". Could you please check again

In my view the most logical guard is to skip committing changes to the Ethernet header if either the base packet type or the current is not PT_ETH. Can you guarantee that flow->packet_type must be PT_ETH when base->packet_type is? I am not sure, looking at the code behind commit_encap_decap_action() that happens just before.

> > In either case setting the MAC header based on differences between
> base_flow and flow doesn't make sense.
> >
> > >
> > > Though both of them solves this problem, couple of NSH regression
> > > tests are failing
> > >
> > > 2291: nsh - md1 encap over a veth link                FAILED (nsh.at:85)
> > > 58022292: nsh - md2 encap over a veth link                FAILED (nsh.at:213)
> > >
> > > I see that they are failing as they are expecting a set(eth(dst)
> > > between the the pop_nsh and the recirc.
> > > Set(eth) action is because of the reasons explained above – Datapath
> actions:
> > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x1122334
> > > 4,c
> > > 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:
> > > 55:6
> > > 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > >
> > > In my understanding set(eth) here  is wrong as there is no set
> > > ethernet action in the userspace rule
> > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=
> > > 0x
> > > 11223344,actions=decap(),decap(),2.
> >
> > I think you are right here. The extra set(eth(dst)) action is a bad translation
> artefact triggered by the above bugs.
> >
> > The failing basic NSH test cases carry Ethernet packets inside the NSH tunnel
> encap, so the decap() action should just recirculate the unchanged packets with
> PT_ETH.
> >
> > These test cases should be corrected as part of the bug-fix commit for the
> set(eth()) issue.
> >
> > >
> > > To reproduce the mpls problem you could use the same test what I
> > > have added in the patch. But you have to modify the test script like
> > > below
> > > -  table=0,priority=100,dl_type=0x0800
> > > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethern
> > > et),o
> > > utput:100
> > > + table=0,priority=100,dl_type=0x0800
> > > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
> > > encap(ethernet),set_field:00:00:00:00:00:02-
> > > >dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
> > >
> > >
> > > > BR, Jan
> > > >
> > > > > -----Original Message-----
> > > > > From: Eelco Chaudron <echaudro@redhat.com>
> > > > > Sent: Tuesday, 6 April, 2021 10:55
> > > > > To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan
> > > > > Scheurich <jan.scheurich@ericsson.com>
> > > > > Cc: dev@openvswitch.org; pshelar@ovn.org;
> > > > > martin.varghese@nokia.com
> > > > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS packet
> type.
> > > > >
> > > > >
> > > > >
> > > > > On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> > > > >
> > > > > > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > > > > >>
> > > > > >>
> > > > > >> 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.varghese@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:
> > > > > >>>>>>>>>>
> > > > >
> > > ffffffffffff52540088513808060001080006040001525400885138010101650
> > > 000
> > > > > 0
> > > > > 000?
> > > > > >>>>>>>>>>
> > > > > >>>>>>>>>>
> > > > > >>>>>>>>>> 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(l
> > > > > >>>>>>>>>> abel
> > > > > >>>>>>>>>> =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:0
> > > > > >>>>>>>> 0:00
> > > > > >>>>>>>> :02/
> > > > > >>>>>>>> 00:00:00:00:00:00),eth_type(0x8847),mpls(label=100/0xff
> > > > > >>>>>>>> fff,
> > > > > >>>>>>>> tc=0
> > > > > >>>>>>>> /0,ttl=64/0x0,bos=1/1),
> > > > > >>>>>>>> actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),re
> > > > > >>>>>>>> circ
> > > > > >>>>>>>> (0x4
> > > > > >>>>>>>> c)
> > > > > >>>>>>>
> > > > > >>>>>>> This set(eth) before the recirc is the problem i guesss.
> > > > > >>>>>>> I need to check
> > > > > >
> > > > > > I could reproduce the problem. It has nothing to do with ARP or IP.
> > > > > > Unlike my test scripts, in your test you are setting the mac
> > > > > > address after the encap action
> > > > > >
> > > > > > Ovs-vswitchd is programming a set(eth(dst) action between the
> > > > > > pop_mpls and  recirc as it sees a difference in mac address in
> > > > > > flow structure and  base_flow structure.
> > > > > >
> > > > > > The mac address in flow structure is not cleared in PT_ETH
> > > > > > handling of xlate_generic_decap_action but  it is cleared in
> > > > > > base_flow in the decap handling of PT_ETH in
> > > > > > commit_encap_decap_action function
> > > > > >
> > > > > > Due to this difference the set(eth(dst) action will be
> > > > > > programmed to the datapath.
> > > > > >
> > > > > > Also, I see that in  commit_set_ether_action Function
> > > > > > “flow->packet_type != htonl(PT_ETH)” is used to check if the
> > > > > > packet is ethernet instead of base_flow->packet_type.
> > > > > >
> > > > > > I assume check on base_flow->packet_type make more sense here ?
> > > > > >
> > > > > > I tried to fix this issue in 2 different ways.
> > > > > >
> > > > > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > > > > handling of xlate_generic_decap action.
> > > > > >
> > > > > > 2  In the  commit_set_ether action I changed the check from
> > > > > > “flow->packet_type != htonl(PT_ETH)” to
> > > > > > “base_flow->packet_type != htonl(PT_ETH))”.
> > > > > >
> > > > > > Though both of them solves this problem, couple of NSH
> > > > > > regression tests are failing
> > > > > >
> > > > > > 2291: nsh - md1 encap over a veth link                FAILED
> > > > > > (nsh.at:85)
> > > > > >
> > > > > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > > > > (nsh.at:213)
> > > > > >
> > > > > > I see that they are failing as they are expecting a
> > > > > > set(eth(dst) between the the pop_nsh and the recirc.
> > > > > >
> > > > > > Set(eth) action is because of the reasons explained above –
> > > > > >
> > > > > > Datapath actions:
> > > > > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x1
> > > > > > 1223
> > > > > > 344,
> > > > > > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
> > > > > > 44:5
> > > > > > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc
> > > > > > (0x1
> > > > > > )
> > > > > >
> > > > > > In my understanding set(eth) here  is wrong as there is no set
> > > > > > ethernet action in the userspace rule
> > > > > > - Hide quoted text -
> > > > > >
> > > > > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,n
> > > > > > sh_c
> > > > > > 1=0x
> > > > > > 11223344,actions=decap(),decap(),2
> > > > > >
> > > > > >
> > > > > >
> > > > > > Could someone comment ?
> > > > >
> > > > > Maybe Jan can answer as he did the NSH implementation, however,
> > > > > what would be of interest if you can give me an example of how
> > > > > the
> > > > > encap()
> > > > > decap() for this would be used in real life so I’m sure I’m
> > > > > testing it
> > > correctly?
> > > > >
> > > > > What I did so far was to encapsulate all traffic going from a VM
> > > > > to the physical port in MPLS using the flows like:
> > > > >
> > > > > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
> > > > > "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847
> > > > > )),s
> > > > > et_mpls
> > > > > _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
> > > > > t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> > > > >
> > > > > Then I would capture this traffic and sent it back over the same
> > > > > port, hoping it would come out as plane traffic with the following rule:
> > > > >
> > > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > > > > "priority=100,dl_type=0x8847,mpls_label=100
> > > > > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> > > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > > > > "table=3,priority=10 actions=normal"
> > > > >
> > > > > If this is correct, let me know, and if Jan does not reply, I’ll
> > > > > try to understand the code in this area and see if I can find
> > > > > out some details…
> > > > >
> > > > > //Eelco
> > > > >
> > > > > >>>>>>>> 2021-04-
> > > > > 01T06:53:36.056Z|00142|dpif(handler37)|WARN|system@ovs-system:
> > > > > >>>>>>>> execute
> > > > > >>>>>>>> pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x4
> > > > > >>>>>>>> c)
> > > > > >>>>>>>> failed
> > > > > >>>>>>>> (Invalid argument) on packet
> > > > > >>>>>>>> mpls,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:
> > > > > >>>>>>>> 00:0
> > > > > >>>>>>>> 0: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(labe
> > > > > >>>>>>> l=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=1
> > > > > >>>> 00/0xfff ff,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=0x8
> > > > > >>>>>>>>>>
> 847)),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:e
> > > > > >>>>>>>>>> np5s0f0
> > > > > >>>>>>>>>>
> > > > > >>>>>>>
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> 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
> > > >
Martin Varghese June 19, 2021, 6:06 a.m. UTC | #23
On Thu, Apr 08, 2021 at 03:31:24PM +0200, Eelco Chaudron wrote:
> 
> 
> On 8 Apr 2021, at 14:05, Martin Varghese wrote:
> 
> > On Wed, Apr 07, 2021 at 03:49:07PM +0000, Jan Scheurich wrote:
> > > Hi Martin,
> > > 
> > > I guess you are aware of the original design document we wrote for
> > > generic encap/decap and NSH support:
> > > https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit#
> > > 
> > > It is no longer 100% aligned with the final implementation in OVS
> > > but still a good reference for understanding the design principles
> > > behind the implementation and some specifics for Ethernet and NSH
> > > encap/decap use cases.
> > > 
> > > Please find some more answers/comments below.
> > > 
> > > BR, Jan
> > > 
> > > > -----Original Message-----
> > > > From: Martin Varghese <martinvarghesenokia@gmail.com>
> > > > Sent: Wednesday, 7 April, 2021 10:43
> > > > To: Jan Scheurich <jan.scheurich@ericsson.com>
> > > > Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
> > > > pshelar@ovn.org; martin.varghese@nokia.com
> > > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS
> > > > packet type.
> > > > 
> > > > On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
> > > > > Hi,
> > > > > 
> > > > > Thanks for the heads up. The interaction with MPLS push/pop
> > > > > is a use case
> > > > that was likely not tested during the NSH and generic
> > > > encap/decap design. It's
> > > > complex code and a long time ago. I'm willing to help, but I
> > > > will need some
> > > > time to go back and have a look.
> > > > > 
> > > > > It would definitely help, if you could provide a minimal
> > > > > example for
> > > > reproducing the problem.
> > > > > 
> > > > 
> > > > Hi Jan ,
> > > > 
> > > > Thanks for your help.
> > > > 
> > > > I was trying to implement ENCAP/DECAP support for MPLS.
> > > > 
> > > > The programming of datapath flow for the below  userspace rule
> > > > fails as there
> > > > is set(eth() action between pop_mpls and recirc 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
> > > > 
> > > > 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
> > > > system: failed to put[create] (Invalid argument)
> > > > ufid:1dddb0ba-27fe-44ea-
> > > > 9a99-5815764b4b9c
> > > > recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
> > > > actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
> > > > 
> > > 
> > > Conceptually, what should happen in this scenario is that, after the
> > > second decap(packet_type(ns=0,type=0) action, OVS processes the
> > > unchanged inner packet as packet type PT_ETH, i.e. as L2 Ethernet
> > > frame. Overwriting the existing Ethernet header with zero values
> > > through set(eth()) is clearly incorrect. That is a logical error
> > > inside the ofproto-dpif-xlate module (see below).
> > > 
> > > I believe the netdev userspace datapath would still have accepted
> > > the incorrect datapath flow. I have too little experience with the
> > > kernel datapath to explain why that rejects the datapath flow as
> > > invalid.
> > > 
> > > Unlike in the Ethernet and NSH cases, the MPLS header does not
> > > contain any indication about the inner packet type. That is why the
> > > packet_type must be provided by the SDN controller as part of the
> > > decap() action.  And the ofproto-dpif-xlate module must consider the
> > > specified inner packet type when continuing the translation. In the
> > > general case, a decap() action should trigger recirculation for
> > > reparsing of the inner packet, so the new packet type must be set
> > > before recirculation. (Exceptions to the general recirculation rule
> > > are those where OVS has already parsed further into the packet and
> > > ofproto can modify the flow on the fly: decap(Ethernet) and possibly
> > > decap(MPLS) for all but the last bottom of stack label).
> > > 
> > > I have had a look at your new code for encap/decap of MPLS headers,
> > > but I must admit I cannot fully judge in how far re-using the
> > > existing translation functions for MPLS label stacks written for the
> > > legacy push/pop_mpls case (i.e. manipulating a label stack between
> > > the L2 and the L3 headers of a PT_ETH Packet) are possible to re-use
> > > in the new context.
> > > 
> > > BTW: Do you support multiple MPLS label encap or decap actions with
> > > your patch? Have you tested that?
> > > 
> > Yes, I will add those tests too.
> 
> Maybe you could add some tests the same way NSH does, by sending a packet
> and verifying the modified content. The current test does encap than decap,
> so if both go wrong, or are skipped we are not actually testing anything.
> 
> > > I am uncertain about the handling of the ethertype of the
> > > decapsulated inner packet. In the design base, the ethertype that is
> > > set in the existing L2 header of the packet after pop_mpls of the
> > > last label is coming from the pop_mpls action, while in the
> > > decap(packet_type(0,0)) case the entire inner packet should be
> > > recirculated as is with packet_type PT_ETH.
> > > 
> > >         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;
> > > 
> > > In the example scenario the new_pkt_type is PT_ETH, so the ethertype
> > > and hence flow->dl_type will be also be set to zero. Is that
> > > intentional? For target packet types of form
> > > PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you would set the correct
> > > flow->dl_type but does this matter just before recirculation?
> > > 
> > Yes, that was intentional.
> > But i am thinking now to store dl_type as 0x6558 when the new
> > packet_type is PT_ETH. In that way it is consistent with the datapath dl
> > type. I could also avoid the special handling done now  for decap MPLS
> > in commit_mpls_action.
> > Setting flow->dl_type matters as this value is used as the argument for
> > datapath pop_mpls action
> > > I don't see any logic to check if the popped label was the BoS
> > > label. Only when popping the BoS label, we should recirculate the
> > > packet for parsing the inner packet.
> > > 
> > I missed that. I will fix it
> > > Conversely, in the encap(mpls) action, the new PT should be PT_MPLS
> > > and the all other flow layers (L2, L3, L4) should be cleared. I
> > > think the reused flow_push_mpls() function doesn't take care of
> > > clearing the L2 flow fields.
> > > 
> > I will add that outside the flow_push_mpls
> > > Have you tested with other packet_types for inner packets (e.g.
> > > PT_IP or PT_ARP). If this is supposed to work, it would be nice to
> > > test the combination of
> > > 
> > >    decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst
> > > 
> > > on one side against the equivalent legacy action pop_mpls on the
> > > other end and vice versa.
> > > 
> > Noted
> > > 
> > > > As I pointed out in my previous mail,  in
> > > > commit_set_ether_action Function
> > > > “flow->packet_type != htonl(PT_ETH)” is used to check if the
> > > > packet is ethernet
> > > > instead of base_flow->packet_type.
> > > > 
> > > > The mac address in flow structure is not cleared in PT_ETH
> > > > handling  of
> > > > xlate_generic_decap_action but  it is cleared in base_flow in
> > > > the decap
> > > > handling of PT_ETH in commit_encap_decap_action function Due to this
> > > > difference the set(eth(dst) action will be programmed to the
> > > > datapath.
> > > > 
> > > > I tried to fix this issue in 2 different ways.
> > > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > > handling of
> > > > xlate_generic_decap action.
> > > > 2  In the  commit_set_ether action I changed the check from
> > > > “flow-
> > > > > packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
> > > > htonl(PT_ETH))”.
> > > 
> > > In the light of the discussion, I think you should implement both
> > > fixes as follows:
> > > 
> > > 1. Clear the mac addresses in struct flow in the PT_ETH clause of
> > > xlate_generic_decap_action().
> > > 
> > > This is correct as the flow struct is modified on the fly here and
> > > should accurately reflect the current packet headers.
> > > 
> > > 2. Change the guard in commit_set_ether_action() to
> > > 
> > >     if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type
> > > != htonl(PT_ETH)) {
> > >         return;
> > >     }
> > > 
> > > In either case setting the MAC header based on differences between
> > > base_flow and flow doesn't make sense.
> > > 
> > > > 
> > > > Though both of them solves this problem, couple of NSH
> > > > regression tests are
> > > > failing
> > > > 
> > > > 2291: nsh - md1 encap over a veth link                FAILED
> > > > (nsh.at:85)
> > > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > > (nsh.at:213)
> > > > 
> > > > I see that they are failing as they are expecting a set(eth(dst)
> > > > between the the
> > > > pop_nsh and the recirc.
> > > > Set(eth) action is because of the reasons explained above –
> > > > Datapath actions:
> > > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c
> > > > 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:6
> > > > 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > > > 
> > > > In my understanding set(eth) here  is wrong as there is no set
> > > > ethernet action
> > > > in the userspace rule
> > > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
> > > > 11223344,actions=decap(),decap(),2.
> > > 
> > > I think you are right here. The extra set(eth(dst)) action is a bad
> > > translation artefact triggered by the above bugs.
> > > 
> > > The failing basic NSH test cases carry Ethernet packets inside the
> > > NSH tunnel encap, so the decap() action should just recirculate the
> > > unchanged packets with PT_ETH.
> > > 
> > > These test cases should be corrected as part of the bug-fix commit
> > > for the set(eth()) issue.
> > > 
> > I will fix it.
> > 
> > Thanks for your time.
> 
> Copy me on the new version and I continue my review.
> Here are some small nits you might be able to fix in your next revision:
> 
With a rebase to the latest. The ARP test case you have tried should work.jfyi
> > +++ 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);
> 
> Guess this can be on one line like all the other case statements as it fits
> the 80 character limit.
> 
noted
> > 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 @@
> ...
> > @@ -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.
> 
> Can you add an explanation for name_space and type? As it’s not clear to me
> how these values are used.

packet_type is already documented in ovs-fields man page. Do we need to
enhance that?
> 
> > > > 
> > > > To reproduce the mpls problem you could use the same test what I
> > > > have added
> > > > in the patch. But you have to modify the test script like below
> > > > -  table=0,priority=100,dl_type=0x0800
> > > > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),o
> > > > utput:100
> > > > + table=0,priority=100,dl_type=0x0800
> > > > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
> > > > encap(ethernet),set_field:00:00:00:00:00:02-
> > > > > dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
> > > > 
> > > > 
> > > > > BR, Jan
> > > > > 
> > > > > > -----Original Message-----
> > > > > > From: Eelco Chaudron <echaudro@redhat.com>
> > > > > > Sent: Tuesday, 6 April, 2021 10:55
> > > > > > To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
> > > > > > <jan.scheurich@ericsson.com>
> > > > > > Cc: dev@openvswitch.org; pshelar@ovn.org;
> > > > > > martin.varghese@nokia.com
> > > > > > Subject: Re: [PATCH v4 1/2] Encap & Decap actions for
> > > > > > MPLS packet type.
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > On 6 Apr 2021, at 10:27, Martin Varghese wrote:
> > > > > > 
> > > > > > > On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
> > > > > > > > 
> > > > > > > > 
> > > > > > > > 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.varghese@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:
> > > > > > > > > > > > > > > > 
> > > > > > 
> > > > ffffffffffff52540088513808060001080006040001525400885138010101650
> > > > 000
> > > > > > 0
> > > > > > 000?
> > > > > > > > > > > > > > > > 
> > > > > > > > > > > > > > > > 
> > > > > > > > > > > > > > > > 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
> > > > > > > > > > > > > > (0x4
> > > > > > > > > > > > > > c)
> > > > > > > > > > > > > 
> > > > > > > > > > > > > This set(eth) before the recirc is the problem i guesss. I
> > > > > > > > > > > > > need to check
> > > > > > > 
> > > > > > > I could reproduce the problem. It has nothing to do
> > > > > > > with ARP or IP.
> > > > > > > Unlike my test scripts, in your test you are setting the mac
> > > > > > > address after the encap action
> > > > > > > 
> > > > > > > Ovs-vswitchd is programming a set(eth(dst) action between the
> > > > > > > pop_mpls and  recirc as it sees a difference in mac address in
> > > > > > > flow structure and  base_flow structure.
> > > > > > > 
> > > > > > > The mac address in flow structure is not cleared in PT_ETH
> > > > > > > handling of xlate_generic_decap_action but  it is cleared in
> > > > > > > base_flow in the decap handling of PT_ETH in
> > > > > > > commit_encap_decap_action function
> > > > > > > 
> > > > > > > Due to this difference the set(eth(dst) action will be programmed
> > > > > > > to the datapath.
> > > > > > > 
> > > > > > > Also, I see that in  commit_set_ether_action Function
> > > > > > > “flow->packet_type != htonl(PT_ETH)” is used to check if the
> > > > > > > packet is ethernet instead of base_flow->packet_type.
> > > > > > > 
> > > > > > > I assume check on base_flow->packet_type make more sense here ?
> > > > > > > 
> > > > > > > I tried to fix this issue in 2 different ways.
> > > > > > > 
> > > > > > > 1   I have cleared the mac address in flow structure  in PT_ETH
> > > > > > > handling of xlate_generic_decap action.
> > > > > > > 
> > > > > > > 2  In the  commit_set_ether action I changed the check from
> > > > > > > “flow->packet_type != htonl(PT_ETH)” to
> > > > > > > “base_flow->packet_type
> > > > > > > != htonl(PT_ETH))”.
> > > > > > > 
> > > > > > > Though both of them solves this problem, couple of NSH regression
> > > > > > > tests are failing
> > > > > > > 
> > > > > > > 2291: nsh - md1 encap over a veth link                FAILED
> > > > > > > (nsh.at:85)
> > > > > > > 
> > > > > > > 58022292: nsh - md2 encap over a veth link                FAILED
> > > > > > > (nsh.at:213)
> > > > > > > 
> > > > > > > I see that they are failing as they are expecting a set(eth(dst)
> > > > > > > between the the pop_nsh and the recirc.
> > > > > > > 
> > > > > > > Set(eth) action is because of the reasons explained above –
> > > > > > > 
> > > > > > > Datapath actions:
> > > > > > > push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223
> > > > > > > 344,
> > > > > > > c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
> > > > > > > 44:5
> > > > > > > 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1
> > > > > > > )
> > > > > > > 
> > > > > > > In my understanding set(eth) here  is wrong as there is no set
> > > > > > > ethernet action in the userspace rule
> > > > > > > - Hide quoted text -
> > > > > > > 
> > > > > > > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c
> > > > > > > 1=0x
> > > > > > > 11223344,actions=decap(),decap(),2
> > > > > > > 
> > > > > > > 
> > > > > > > 
> > > > > > > Could someone comment ?
> > > > > > 
> > > > > > Maybe Jan can answer as he did the NSH implementation,
> > > > > > however, what
> > > > > > would be of interest if you can give me an example of how the
> > > > > > encap()
> > > > > > decap() for this would be used in real life so I’m sure
> > > > > > I’m testing it
> > > > correctly?
> > > > > > 
> > > > > > What I did so far was to encapsulate all traffic going
> > > > > > from a VM to
> > > > > > the physical port in MPLS using the flows like:
> > > > > > 
> > > > > > ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
> > > > > > "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),s
> > > > > > et_mpls
> > > > > > _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
> > > > > > t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
> > > > > > 
> > > > > > Then I would capture this traffic and sent it back over the same
> > > > > > port, hoping it would come out as plane traffic with the
> > > > > > following rule:
> > > > > > 
> > > > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
> > > > > > "priority=100,dl_type=0x8847,mpls_label=100
> > > > > > actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
> > > > > > ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
> > > > > > actions=normal"
> > > > > > 
> > > > > > If this is correct, let me know, and if Jan does not
> > > > > > reply, I’ll try
> > > > > > to understand the code in this area and see if I can find out some
> > > > > > details…
> > > > > > 
> > > > > > //Eelco
> > > > > > 
> > > > > > > > > > > > > > 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:0
> > > > > > > > > > > > > > 0: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/0xfff
> > > > > > > > > > ff,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=0x8
> > > > > > > > > > > > > > > > 847)),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:e
> > > > > > > > > > > > > > > > np5s0f0
> > > > > > > > > > > > > > > > 
> > > > > > > > > > > > > 
> > > > > > > > > > > > > > > 
> > > > > > > > > > > > > > > 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
> > > > > 
>
Eelco Chaudron June 21, 2021, 7:36 a.m. UTC | #24
On 19 Jun 2021, at 8:06, Martin Varghese wrote:

> On Thu, Apr 08, 2021 at 03:31:24PM +0200, Eelco Chaudron wrote:
>>
>>
>> On 8 Apr 2021, at 14:05, Martin Varghese wrote:
>>
>>> On Wed, Apr 07, 2021 at 03:49:07PM +0000, Jan Scheurich wrote:
>>>> Hi Martin,
>>>>
>>>> I guess you are aware of the original design document we wrote for
>>>> generic encap/decap and NSH support:
>>>> https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit#
>>>>
>>>> It is no longer 100% aligned with the final implementation in OVS
>>>> but still a good reference for understanding the design principles
>>>> behind the implementation and some specifics for Ethernet and NSH
>>>> encap/decap use cases.
>>>>
>>>> Please find some more answers/comments below.
>>>>
>>>> BR, Jan
>>>>
>>>>> -----Original Message-----
>>>>> From: Martin Varghese <martinvarghesenokia@gmail.com>
>>>>> Sent: Wednesday, 7 April, 2021 10:43
>>>>> To: Jan Scheurich <jan.scheurich@ericsson.com>
>>>>> Cc: Eelco Chaudron <echaudro@redhat.com>; dev@openvswitch.org;
>>>>> pshelar@ovn.org; martin.varghese@nokia.com
>>>>> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for MPLS
>>>>> packet type.
>>>>>
>>>>> On Tue, Apr 06, 2021 at 09:00:16AM +0000, Jan Scheurich wrote:
>>>>>> Hi,
>>>>>>
>>>>>> Thanks for the heads up. The interaction with MPLS push/pop
>>>>>> is a use case
>>>>> that was likely not tested during the NSH and generic
>>>>> encap/decap design. It's
>>>>> complex code and a long time ago. I'm willing to help, but I
>>>>> will need some
>>>>> time to go back and have a look.
>>>>>>
>>>>>> It would definitely help, if you could provide a minimal
>>>>>> example for
>>>>> reproducing the problem.
>>>>>>
>>>>>
>>>>> Hi Jan ,
>>>>>
>>>>> Thanks for your help.
>>>>>
>>>>> I was trying to implement ENCAP/DECAP support for MPLS.
>>>>>
>>>>> The programming of datapath flow for the below  userspace rule
>>>>> fails as there
>>>>> is set(eth() action between pop_mpls and recirc 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
>>>>>
>>>>> 2021-04-05T05:46:49.192Z|00068|dpif(handler51)|WARN|system@ovs-
>>>>> system: failed to put[create] (Invalid argument)
>>>>> ufid:1dddb0ba-27fe-44ea-
>>>>> 9a99-5815764b4b9c
>>>>> recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(6),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=2/0x0,tc=0/0,ttl=64/0x0,bos=1/1),
>>>>> actions:pop_eth,pop_mpls(eth_type=0x6558),set(eth()),recirc(0x45)
>>>>>
>>>>
>>>> Conceptually, what should happen in this scenario is that, after the
>>>> second decap(packet_type(ns=0,type=0) action, OVS processes the
>>>> unchanged inner packet as packet type PT_ETH, i.e. as L2 Ethernet
>>>> frame. Overwriting the existing Ethernet header with zero values
>>>> through set(eth()) is clearly incorrect. That is a logical error
>>>> inside the ofproto-dpif-xlate module (see below).
>>>>
>>>> I believe the netdev userspace datapath would still have accepted
>>>> the incorrect datapath flow. I have too little experience with the
>>>> kernel datapath to explain why that rejects the datapath flow as
>>>> invalid.
>>>>
>>>> Unlike in the Ethernet and NSH cases, the MPLS header does not
>>>> contain any indication about the inner packet type. That is why the
>>>> packet_type must be provided by the SDN controller as part of the
>>>> decap() action.  And the ofproto-dpif-xlate module must consider the
>>>> specified inner packet type when continuing the translation. In the
>>>> general case, a decap() action should trigger recirculation for
>>>> reparsing of the inner packet, so the new packet type must be set
>>>> before recirculation. (Exceptions to the general recirculation rule
>>>> are those where OVS has already parsed further into the packet and
>>>> ofproto can modify the flow on the fly: decap(Ethernet) and possibly
>>>> decap(MPLS) for all but the last bottom of stack label).
>>>>
>>>> I have had a look at your new code for encap/decap of MPLS headers,
>>>> but I must admit I cannot fully judge in how far re-using the
>>>> existing translation functions for MPLS label stacks written for the
>>>> legacy push/pop_mpls case (i.e. manipulating a label stack between
>>>> the L2 and the L3 headers of a PT_ETH Packet) are possible to re-use
>>>> in the new context.
>>>>
>>>> BTW: Do you support multiple MPLS label encap or decap actions with
>>>> your patch? Have you tested that?
>>>>
>>> Yes, I will add those tests too.
>>
>> Maybe you could add some tests the same way NSH does, by sending a packet
>> and verifying the modified content. The current test does encap than decap,
>> so if both go wrong, or are skipped we are not actually testing anything.
>>
>>>> I am uncertain about the handling of the ethertype of the
>>>> decapsulated inner packet. In the design base, the ethertype that is
>>>> set in the existing L2 header of the packet after pop_mpls of the
>>>> last label is coming from the pop_mpls action, while in the
>>>> decap(packet_type(0,0)) case the entire inner packet should be
>>>> recirculated as is with packet_type PT_ETH.
>>>>
>>>>         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;
>>>>
>>>> In the example scenario the new_pkt_type is PT_ETH, so the ethertype
>>>> and hence flow->dl_type will be also be set to zero. Is that
>>>> intentional? For target packet types of form
>>>> PACKET_TYPE(OFPHTN_ETHERTYPE, <ethertype>) you would set the correct
>>>> flow->dl_type but does this matter just before recirculation?
>>>>
>>> Yes, that was intentional.
>>> But i am thinking now to store dl_type as 0x6558 when the new
>>> packet_type is PT_ETH. In that way it is consistent with the datapath dl
>>> type. I could also avoid the special handling done now  for decap MPLS
>>> in commit_mpls_action.
>>> Setting flow->dl_type matters as this value is used as the argument for
>>> datapath pop_mpls action
>>>> I don't see any logic to check if the popped label was the BoS
>>>> label. Only when popping the BoS label, we should recirculate the
>>>> packet for parsing the inner packet.
>>>>
>>> I missed that. I will fix it
>>>> Conversely, in the encap(mpls) action, the new PT should be PT_MPLS
>>>> and the all other flow layers (L2, L3, L4) should be cleared. I
>>>> think the reused flow_push_mpls() function doesn't take care of
>>>> clearing the L2 flow fields.
>>>>
>>> I will add that outside the flow_push_mpls
>>>> Have you tested with other packet_types for inner packets (e.g.
>>>> PT_IP or PT_ARP). If this is supposed to work, it would be nice to
>>>> test the combination of
>>>>
>>>>    decap(), encap(mpls), set_mpls_label, encap(eth), set_field:dl_dst
>>>>
>>>> on one side against the equivalent legacy action pop_mpls on the
>>>> other end and vice versa.
>>>>
>>> Noted
>>>>
>>>>> As I pointed out in my previous mail,  in
>>>>> commit_set_ether_action Function
>>>>> “flow->packet_type != htonl(PT_ETH)” is used to check if the
>>>>> packet is ethernet
>>>>> instead of base_flow->packet_type.
>>>>>
>>>>> The mac address in flow structure is not cleared in PT_ETH
>>>>> handling  of
>>>>> xlate_generic_decap_action but  it is cleared in base_flow in
>>>>> the decap
>>>>> handling of PT_ETH in commit_encap_decap_action function Due to this
>>>>> difference the set(eth(dst) action will be programmed to the
>>>>> datapath.
>>>>>
>>>>> I tried to fix this issue in 2 different ways.
>>>>> 1   I have cleared the mac address in flow structure  in PT_ETH
>>>>> handling of
>>>>> xlate_generic_decap action.
>>>>> 2  In the  commit_set_ether action I changed the check from
>>>>> “flow-
>>>>>> packet_type != htonl(PT_ETH)” to  “base_flow->packet_type !=
>>>>> htonl(PT_ETH))”.
>>>>
>>>> In the light of the discussion, I think you should implement both
>>>> fixes as follows:
>>>>
>>>> 1. Clear the mac addresses in struct flow in the PT_ETH clause of
>>>> xlate_generic_decap_action().
>>>>
>>>> This is correct as the flow struct is modified on the fly here and
>>>> should accurately reflect the current packet headers.
>>>>
>>>> 2. Change the guard in commit_set_ether_action() to
>>>>
>>>>     if (flow->packet_type != htonl(PT_ETH) || base_flow->packet_type
>>>> != htonl(PT_ETH)) {
>>>>         return;
>>>>     }
>>>>
>>>> In either case setting the MAC header based on differences between
>>>> base_flow and flow doesn't make sense.
>>>>
>>>>>
>>>>> Though both of them solves this problem, couple of NSH
>>>>> regression tests are
>>>>> failing
>>>>>
>>>>> 2291: nsh - md1 encap over a veth link                FAILED
>>>>> (nsh.at:85)
>>>>> 58022292: nsh - md2 encap over a veth link                FAILED
>>>>> (nsh.at:213)
>>>>>
>>>>> I see that they are failing as they are expecting a set(eth(dst)
>>>>> between the the
>>>>> pop_nsh and the recirc.
>>>>> Set(eth) action is because of the reasons explained above –
>>>>> Datapath actions:
>>>>> push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c
>>>>> 2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:6
>>>>> 6),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
>>>>>
>>>>> In my understanding set(eth) here  is wrong as there is no set
>>>>> ethernet action
>>>>> in the userspace rule
>>>>> table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x
>>>>> 11223344,actions=decap(),decap(),2.
>>>>
>>>> I think you are right here. The extra set(eth(dst)) action is a bad
>>>> translation artefact triggered by the above bugs.
>>>>
>>>> The failing basic NSH test cases carry Ethernet packets inside the
>>>> NSH tunnel encap, so the decap() action should just recirculate the
>>>> unchanged packets with PT_ETH.
>>>>
>>>> These test cases should be corrected as part of the bug-fix commit
>>>> for the set(eth()) issue.
>>>>
>>> I will fix it.
>>>
>>> Thanks for your time.
>>
>> Copy me on the new version and I continue my review.
>> Here are some small nits you might be able to fix in your next revision:
>>
> With a rebase to the latest. The ARP test case you have tried should work.jfyi

Cool, I will continue testing/reviewing with the next revision once all depending fixes are in.

>>> +++ 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);
>>
>> Guess this can be on one line like all the other case statements as it fits
>> the 80 character limit.
>>
> noted
>>> 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 @@
>> ...
>>> @@ -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.
>>
>> Can you add an explanation for name_space and type? As it’s not clear to me
>> how these values are used.
>
> packet_type is already documented in ovs-fields man page. Do we need to
> enhance that?

I’m suggesting explaining what those two fields mean to this specific action. What happens if you specify the packet type? Are only those packets encapsulated/decapsulated, or is it used for encapsulating? Same for name_space.

>>
>>>>>
>>>>> To reproduce the mpls problem you could use the same test what I
>>>>> have added
>>>>> in the patch. But you have to modify the test script like below
>>>>> -  table=0,priority=100,dl_type=0x0800
>>>>> actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),o
>>>>> utput:100
>>>>> + table=0,priority=100,dl_type=0x0800
>>>>> actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,
>>>>> encap(ethernet),set_field:00:00:00:00:00:02-
>>>>>> dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100"
>>>>>
>>>>>
>>>>>> BR, Jan
>>>>>>
>>>>>>> -----Original Message-----
>>>>>>> From: Eelco Chaudron <echaudro@redhat.com>
>>>>>>> Sent: Tuesday, 6 April, 2021 10:55
>>>>>>> To: Martin Varghese <martinvarghesenokia@gmail.com>; Jan Scheurich
>>>>>>> <jan.scheurich@ericsson.com>
>>>>>>> Cc: dev@openvswitch.org; pshelar@ovn.org;
>>>>>>> martin.varghese@nokia.com
>>>>>>> Subject: Re: [PATCH v4 1/2] Encap & Decap actions for
>>>>>>> MPLS packet type.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 6 Apr 2021, at 10:27, Martin Varghese wrote:
>>>>>>>
>>>>>>>> On Thu, Apr 01, 2021 at 11:32:06AM +0200, Eelco Chaudron wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> 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.varghese@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:
>>>>>>>>>>>>>>>>>
>>>>>>>
>>>>> ffffffffffff52540088513808060001080006040001525400885138010101650
>>>>> 000
>>>>>>> 0
>>>>>>> 000?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> 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
>>>>>>>>>>>>>>> (0x4
>>>>>>>>>>>>>>> c)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This set(eth) before the recirc is the problem i guesss. I
>>>>>>>>>>>>>> need to check
>>>>>>>>
>>>>>>>> I could reproduce the problem. It has nothing to do
>>>>>>>> with ARP or IP.
>>>>>>>> Unlike my test scripts, in your test you are setting the mac
>>>>>>>> address after the encap action
>>>>>>>>
>>>>>>>> Ovs-vswitchd is programming a set(eth(dst) action between the
>>>>>>>> pop_mpls and  recirc as it sees a difference in mac address in
>>>>>>>> flow structure and  base_flow structure.
>>>>>>>>
>>>>>>>> The mac address in flow structure is not cleared in PT_ETH
>>>>>>>> handling of xlate_generic_decap_action but  it is cleared in
>>>>>>>> base_flow in the decap handling of PT_ETH in
>>>>>>>> commit_encap_decap_action function
>>>>>>>>
>>>>>>>> Due to this difference the set(eth(dst) action will be programmed
>>>>>>>> to the datapath.
>>>>>>>>
>>>>>>>> Also, I see that in  commit_set_ether_action Function
>>>>>>>> “flow->packet_type != htonl(PT_ETH)” is used to check if the
>>>>>>>> packet is ethernet instead of base_flow->packet_type.
>>>>>>>>
>>>>>>>> I assume check on base_flow->packet_type make more sense here ?
>>>>>>>>
>>>>>>>> I tried to fix this issue in 2 different ways.
>>>>>>>>
>>>>>>>> 1   I have cleared the mac address in flow structure  in PT_ETH
>>>>>>>> handling of xlate_generic_decap action.
>>>>>>>>
>>>>>>>> 2  In the  commit_set_ether action I changed the check from
>>>>>>>> “flow->packet_type != htonl(PT_ETH)” to
>>>>>>>> “base_flow->packet_type
>>>>>>>> != htonl(PT_ETH))”.
>>>>>>>>
>>>>>>>> Though both of them solves this problem, couple of NSH regression
>>>>>>>> tests are failing
>>>>>>>>
>>>>>>>> 2291: nsh - md1 encap over a veth link                FAILED
>>>>>>>> (nsh.at:85)
>>>>>>>>
>>>>>>>> 58022292: nsh - md2 encap over a veth link                FAILED
>>>>>>>> (nsh.at:213)
>>>>>>>>
>>>>>>>> I see that they are failing as they are expecting a set(eth(dst)
>>>>>>>> between the the pop_nsh and the recirc.
>>>>>>>>
>>>>>>>> Set(eth) action is because of the reasons explained above –
>>>>>>>>
>>>>>>>> Datapath actions:
>>>>>>>> push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223
>>>>>>>> 344,
>>>>>>>> c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:
>>>>>>>> 44:5
>>>>>>>> 5:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1
>>>>>>>> )
>>>>>>>>
>>>>>>>> In my understanding set(eth) here  is wrong as there is no set
>>>>>>>> ethernet action in the userspace rule
>>>>>>>> - Hide quoted text -
>>>>>>>>
>>>>>>>> table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c
>>>>>>>> 1=0x
>>>>>>>> 11223344,actions=decap(),decap(),2
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Could someone comment ?
>>>>>>>
>>>>>>> Maybe Jan can answer as he did the NSH implementation,
>>>>>>> however, what
>>>>>>> would be of interest if you can give me an example of how the
>>>>>>> encap()
>>>>>>> decap() for this would be used in real life so I’m sure
>>>>>>> I’m testing it
>>>>> correctly?
>>>>>>>
>>>>>>> What I did so far was to encapsulate all traffic going
>>>>>>> from a VM to
>>>>>>> the physical port in MPLS using the flows like:
>>>>>>>
>>>>>>> ovs-ofctl add-flow -O OpenFlow13 ovs_pvp_br0
>>>>>>> "priority=100,in_port=vnet0,actions=encap(mpls(ether_type=0x8847)),s
>>>>>>> et_mpls
>>>>>>> _label:100,encap(ethernet),,set_field:00:00:00:00:00:02->dl_ds
>>>>>>> t,set_field:00:00:00:00:00:01->dl_src,output:enp5s0f0"
>>>>>>>
>>>>>>> Then I would capture this traffic and sent it back over the same
>>>>>>> port, hoping it would come out as plane traffic with the
>>>>>>> following rule:
>>>>>>>
>>>>>>> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0
>>>>>>> "priority=100,dl_type=0x8847,mpls_label=100
>>>>>>> actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3)"
>>>>>>> ovs-ofctl add-flow -O OpenFlow15 ovs_pvp_br0 "table=3,priority=10
>>>>>>> actions=normal"
>>>>>>>
>>>>>>> If this is correct, let me know, and if Jan does not
>>>>>>> reply, I’ll try
>>>>>>> to understand the code in this area and see if I can find out some
>>>>>>> details…
>>>>>>>
>>>>>>> //Eelco
>>>>>>>
>>>>>>>>>>>>>>> 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:0
>>>>>>>>>>>>>>> 0: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/0xfff
>>>>>>>>>>> ff,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=0x8
>>>>>>>>>>>>>>>>> 847)),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:e
>>>>>>>>>>>>>>>>> np5s0f0
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> 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
>>>>>>
>>
diff mbox series

Patch

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