[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", ð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