[ovs-dev] [PATCH v4 1/2] Encap & Decap actions for MPLS packet type.
Martin Varghese
martinvarghesenokia at gmail.com
Thu Apr 1 09:09:39 UTC 2021
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 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
>
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 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", ðertype);
> > > > > > + 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