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

Eelco Chaudron echaudro at redhat.com
Thu Apr 1 08:54:42 UTC 2021



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 at 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 at 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 at 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 at 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
>>>>
>>




More information about the dev mailing list