Message ID | 20211012042349.2564-1-martinvarghesenokia@gmail.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [ovs-dev,v7] Encap & Decap actions for MPLS packet type. | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | warning | apply and check: warning |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
Bleep bloop. Greetings Martin Varghese, I am a robot and I have tried out your patch. Thanks for your contribution. I encountered some error that I wasn't expecting. See the details below. checkpatch: WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator #340 FILE: lib/odp-util.c:2616: eth_type=0x%"SCNx16")%n", &lse, &tc, &ttl, &bos, ð_type, &n)) { Lines checked: 1290, Warnings: 2, Errors: 0 Please check this out. If you feel there has been an error, please email aconole@redhat.com Thanks, 0-day Robot
On 12 Oct 2021, at 6:23, Martin Varghese wrote: > From: Martin Varghese <martin.varghese@nokia.com> > > The encap & decap actions are extended to support MPLS packet type. > Encap & decap actions adds and removes MPLS header at start of the > packet. > > The existing PUSH MPLS & POP MPLS actions inserts & removes MPLS > header between ethernet header and the IP header. Though this behaviour > is fine for L3 VPN where an IP packet is encapsulated inside a MPLS > tunnel, it does not suffice the L2 VPN requirements. In L2 VPN the > ethernet packets must be encapsulated inside MPLS tunnel. > > In this change the encap & decap actions are extended to support MPLS > packet type. The encap & decap adds and removes MPLS header at the > start of packet as depicted below. > > Encapsulation: > > Actions - encap(mpls(ether_type=0x8847)),encap(ethernet) > > Incoming packet -> | ETH | IP | Payload | > > 1 Actions - encap(mpls(ether_type=0x8847)) [Datapath action - ADD_MPLS:0x8847] > > Outgoing packet -> | MPLS | ETH | Payload| > > 2 Actions - encap(ethernet) [ Datapath action - push_eth ] > > Outgoing packet -> | ETH | MPLS | ETH | Payload| > > Decapsulation: > > Incoming packet -> | ETH | MPLS | ETH | IP | Payload | > > Actions - decap(),decap(packet_type(ns=0,type=0) > > 1 Actions - decap() [Datapath action - pop_eth) > > Outgoing packet -> | MPLS | ETH | IP | Payload| > > 2 Actions - decap(packet_type(ns=0,type=0) [Datapath action - POP_MPLS:0x6558] > > Outgoing packet -> | ETH | IP | Payload| > > Signed-off-by: Martin Varghese <martin.varghese@nokia.com> Thanks for following through! Looking forward to see the follow on patch to also include the OVS_ACTION_ATTR_ADD_MPLS in TC, i.e. netdev_tc_flow_put(). Acked-by: Eelco Chaudron <echaudro@redhat.com>
On Fri, Oct 15, 2021 at 01:40:39PM +0200, Eelco Chaudron wrote: > > > On 12 Oct 2021, at 6:23, Martin Varghese wrote: > > > From: Martin Varghese <martin.varghese@nokia.com> > > > > The encap & decap actions are extended to support MPLS packet type. > > Encap & decap actions adds and removes MPLS header at start of the > > packet. > > > > The existing PUSH MPLS & POP MPLS actions inserts & removes MPLS > > header between ethernet header and the IP header. Though this behaviour > > is fine for L3 VPN where an IP packet is encapsulated inside a MPLS > > tunnel, it does not suffice the L2 VPN requirements. In L2 VPN the > > ethernet packets must be encapsulated inside MPLS tunnel. > > > > In this change the encap & decap actions are extended to support MPLS > > packet type. The encap & decap adds and removes MPLS header at the > > start of packet as depicted below. > > > > Encapsulation: > > > > Actions - encap(mpls(ether_type=0x8847)),encap(ethernet) > > > > Incoming packet -> | ETH | IP | Payload | > > > > 1 Actions - encap(mpls(ether_type=0x8847)) [Datapath action - ADD_MPLS:0x8847] > > > > Outgoing packet -> | MPLS | ETH | Payload| > > > > 2 Actions - encap(ethernet) [ Datapath action - push_eth ] > > > > Outgoing packet -> | ETH | MPLS | ETH | Payload| > > > > Decapsulation: > > > > Incoming packet -> | ETH | MPLS | ETH | IP | Payload | > > > > Actions - decap(),decap(packet_type(ns=0,type=0) > > > > 1 Actions - decap() [Datapath action - pop_eth) > > > > Outgoing packet -> | MPLS | ETH | IP | Payload| > > > > 2 Actions - decap(packet_type(ns=0,type=0) [Datapath action - POP_MPLS:0x6558] > > > > Outgoing packet -> | ETH | IP | Payload| > > > > Signed-off-by: Martin Varghese <martin.varghese@nokia.com> > > Thanks for following through! > > Looking forward to see the follow on patch to also include the OVS_ACTION_ATTR_ADD_MPLS in TC, i.e. netdev_tc_flow_put(). > > > Acked-by: Eelco Chaudron <echaudro@redhat.com> > Thanks Eelco.
On Fri, Oct 15, 2021 at 01:40:39PM +0200, Eelco Chaudron wrote: > > > On 12 Oct 2021, at 6:23, Martin Varghese wrote: > > > From: Martin Varghese <martin.varghese@nokia.com> > > > > The encap & decap actions are extended to support MPLS packet type. > > Encap & decap actions adds and removes MPLS header at start of the > > packet. > > > > The existing PUSH MPLS & POP MPLS actions inserts & removes MPLS > > header between ethernet header and the IP header. Though this behaviour > > is fine for L3 VPN where an IP packet is encapsulated inside a MPLS > > tunnel, it does not suffice the L2 VPN requirements. In L2 VPN the > > ethernet packets must be encapsulated inside MPLS tunnel. > > > > In this change the encap & decap actions are extended to support MPLS > > packet type. The encap & decap adds and removes MPLS header at the > > start of packet as depicted below. > > > > Encapsulation: > > > > Actions - encap(mpls(ether_type=0x8847)),encap(ethernet) > > > > Incoming packet -> | ETH | IP | Payload | > > > > 1 Actions - encap(mpls(ether_type=0x8847)) [Datapath action - ADD_MPLS:0x8847] > > > > Outgoing packet -> | MPLS | ETH | Payload| > > > > 2 Actions - encap(ethernet) [ Datapath action - push_eth ] > > > > Outgoing packet -> | ETH | MPLS | ETH | Payload| > > > > Decapsulation: > > > > Incoming packet -> | ETH | MPLS | ETH | IP | Payload | > > > > Actions - decap(),decap(packet_type(ns=0,type=0) > > > > 1 Actions - decap() [Datapath action - pop_eth) > > > > Outgoing packet -> | MPLS | ETH | IP | Payload| > > > > 2 Actions - decap(packet_type(ns=0,type=0) [Datapath action - POP_MPLS:0x6558] > > > > Outgoing packet -> | ETH | IP | Payload| > > > > Signed-off-by: Martin Varghese <martin.varghese@nokia.com> > > Thanks for following through! > > Looking forward to see the follow on patch to also include the OVS_ACTION_ATTR_ADD_MPLS in TC, i.e. netdev_tc_flow_put(). > > > Acked-by: Eelco Chaudron <echaudro@redhat.com> > Hi Ilya, If no further comments from anyone can we take this patch forward. Regards, Martin
On 10/12/21 06:23, Martin Varghese wrote: > From: Martin Varghese <martin.varghese@nokia.com> Hi, Martin. Thanks for working on this! I didn't review most of the logic of the action itself, but see some comments inline. Bets regards, Ilya Maximets. > > The encap & decap actions are extended to support MPLS packet type. > Encap & decap actions adds and removes MPLS header at start of the > packet. > > The existing PUSH MPLS & POP MPLS actions inserts & removes MPLS > header between ethernet header and the IP header. From the documentation it's really hard to understand the difference between PUSH/POP_MPLS and encap/decap(mpls). Could you please, add notes about the difference to both: description of PUSH/POP_MPLS and encap/decap(mpls) in the ovs-actions.7.rst ? > Though this behaviour > is fine for L3 VPN where an IP packet is encapsulated inside a MPLS > tunnel, it does not suffice the L2 VPN requirements. In L2 VPN the > ethernet packets must be encapsulated inside MPLS tunnel. > > In this change the encap & decap actions are extended to support MPLS > packet type. The encap & decap adds and removes MPLS header at the > start of packet as depicted below. > > Encapsulation: > > Actions - encap(mpls(ether_type=0x8847)),encap(ethernet) > > Incoming packet -> | ETH | IP | Payload | > > 1 Actions - encap(mpls(ether_type=0x8847)) [Datapath action - ADD_MPLS:0x8847] > > Outgoing packet -> | MPLS | ETH | Payload| > > 2 Actions - encap(ethernet) [ Datapath action - push_eth ] > > Outgoing packet -> | ETH | MPLS | ETH | Payload| > > Decapsulation: > > Incoming packet -> | ETH | MPLS | ETH | IP | Payload | > > Actions - decap(),decap(packet_type(ns=0,type=0) > > 1 Actions - decap() [Datapath action - pop_eth) > > Outgoing packet -> | MPLS | ETH | IP | Payload| > > 2 Actions - decap(packet_type(ns=0,type=0) [Datapath action - POP_MPLS:0x6558] > > Outgoing packet -> | ETH | IP | Payload| > > Signed-off-by: Martin Varghese <martin.varghese@nokia.com> > --- > Changes in v2: > - Fixed the compilation error reported by bot. > > Changes in v3: > - Adapted the changes to allign with the kernel implementaion > > Changes in v4: > - Fixed the compilation error reported by bot. > - Added SLOW_ACTION support for no datapath support. > > Changes in v5: > - Code styling fixed. > - Given reference for packet_type field in documentation. > - Modified code to do recirc only for the last label. > - Cleaed l2,l3,l4 fields with encap. > - Fixed existing encap & decap tests to do set eth to outer header > - Added tests to verify Encap & Decap with legacy Push & Pop. > - Added xlate tests. > - Added seperate tests to inspect packet after encap & decap. > - Added tests for multiple mpls test cases. > > Changes in v6: > - Styling and variable naming fixed. > - Tests added for slow path support. > - Added comments in code. > - Added odp test for add_mpls action. > - Documentation added in ovs-actions.7.rst > > Changes in v7: > - Fixed Documentation. > - Initialized variables in odp test. > - Add test skip for no dp support. > > Documentation/ref/ovs-actions.7.rst | 17 +- > NEWS | 2 +- > .../linux/compat/include/linux/openvswitch.h | 33 +- > 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 | 73 ++++- > lib/ofp-actions.c | 5 + > lib/ofp-ed-props.c | 91 ++++++ > lib/packets.c | 45 +++ > lib/packets.h | 2 + > ofproto/ofproto-dpif-ipfix.c | 1 + > ofproto/ofproto-dpif-sflow.c | 1 + > ofproto/ofproto-dpif-xlate.c | 78 +++++ > ofproto/ofproto-dpif.c | 38 +++ > ofproto/ofproto-dpif.h | 4 +- > tests/mpls-xlate.at | 57 ++++ > tests/odp.at | 1 + > tests/system-traffic.at | 308 ++++++++++++++++++ > 20 files changed, 771 insertions(+), 17 deletions(-) > > diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst > index 7224896df..9d60bf631 100644 > --- a/Documentation/ref/ovs-actions.7.rst > +++ b/Documentation/ref/ovs-actions.7.rst > @@ -212,12 +212,13 @@ Unsupported packet type > is not an L3 packet, and ``encap(nsh)`` raises this error if the current > packet is not Ethernet, IPv4, IPv6, or NSH. > > + The ``decap`` action is supported only for packet types ethernet, NSH and > + MPLS. Openvswitch raises this error for other packet types. Please, put two spaces between sentences like it's done in the rest of the document. > When a ``decap`` action decapsulates a packet, Open vSwitch raises this error > if it does not support the type of inner packet. ``decap`` of an Ethernet > header raises this error if a VLAN header is present, ``decap`` of a NSH > packet raises this error if the NSH inner packet is not Ethernet, IPv4, IPv6, > - or NSH, and ``decap`` of other types of packets is unsupported and also > - raises this error. > + or NSH. > > This error terminates packet processing, retaining any previous side effects > (e.g. output actions). When this error arises within the execution of a > @@ -995,6 +996,7 @@ The ``encap`` action > | ``encap(nsh([md_type=``\ *md_type*\ > ``], [tlv(``\ *class*,\ *type*,\ *value*\ ``)]...))`` > | ``encap(ethernet)`` > + | ``encap(mpls(ether_type=``\ *ether_type*\) The last ')' should also be highlighted with ``. > > The ``encap`` action encapsulates a packet with a specified header. It has > variants for different kinds of encapsulation. > @@ -1018,6 +1020,10 @@ frame. The Ethernet type is initialized to the L3 packet's type, e.g. 0x0800 > if the L3 packet is IPv4. The Ethernet source and destination are initially > zeroed. > > +The ``encap(mpls(ethertype=....))`` variant encapsulates an ethernet or Hmm. So, is it 'ether_type' or 'ethertype' ? > +L3 packet with a MPLS header. The ethertype could be MPLS unicast (0x8847) or > +multicast (0x8848) ethertypes. Please, highlight the hex numbers. > + > **Conformance** > This action is an Open vSwitch extension to OpenFlow 1.3 and later, > introduced in Open vSwitch 2.8. This section, I think, should be updated in order to reflect in which OVS version mpls variant is added. > @@ -1030,6 +1036,7 @@ The ``decap`` action > > **Syntax**: > | ``decap`` > + | ``decap([packet_type(ns=namespace,type=type)])`` Please, highlight the arguments. And brackets, I guess, not needed here. It's either brackets or 2 syntax variants. > > Removes an outermost encapsulation from the packet: > > @@ -1042,6 +1049,12 @@ Removes an outermost encapsulation from the packet: > and NSH inner packet types. Other types raise unsupported packet type > errors. > > + - 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. The packet_type field specifies the type of > + the packet in the format specified in OpenFlow 1.5 chapter > + `7.2.3.11 Packet Type Match Field` And what if the inner packet is not the packet stated in the OF action? > + > - Otherwise, raises an unsupported packet type error. > > **Conformance** This section needs an update. > diff --git a/NEWS b/NEWS > index 90f4b1590..cac602d09 100644 > --- a/NEWS > +++ b/NEWS > @@ -211,7 +211,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. > - Please, keep the line. > + - Encap & Decap action support for MPLS packet type. Please, don't touch release notes for 2.14. Move to Post-2.16. > > 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 f0595eeba..88a653771 100644 > --- a/datapath/linux/compat/include/linux/openvswitch.h > +++ b/datapath/linux/compat/include/linux/openvswitch.h > @@ -817,8 +817,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. > + */ > + Above code is located in a different place in the kernel header. Please move it accordingly. > +/* 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 > @@ -1008,6 +1032,10 @@ 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_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. > */ > > @@ -1037,6 +1065,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..96ef3d951 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,12 @@ 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 +107,13 @@ 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 75f381ec1..140302a90 100644 > --- a/lib/dpif-netdev.c > +++ b/lib/dpif-netdev.c > @@ -8304,6 +8304,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 8c4aed47b..99aba3cae 100644 > --- a/lib/dpif.c > +++ b/lib/dpif.c > @@ -1274,6 +1274,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 fbdfc7ad8..709f4f31a 100644 > --- a/lib/odp-util.c > +++ b/lib/odp-util.c > @@ -142,6 +142,7 @@ 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 +1255,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_add_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; > @@ -2595,6 +2604,29 @@ parse_odp_action__(struct parse_odp_context *context, const char *s, > return retval; > } > } > + { > + struct ovs_action_add_mpls mpls; > + uint32_t lse; > + uint8_t ttl, tc, bos; > + int n = -1; > + uint16_t eth_type; > + > + if (ovs_scan(s, > + "add_mpls(label=%"SCNi32",tc=%"SCNd8",ttl=%"SCNd8",bos=%"SCNd8",\ > + eth_type=0x%"SCNx16")%n", &lse, &tc, &ttl, &bos, ð_type, &n)) { Please, don't use a line continuation here. Just close the '"' on the first line and open a new one on the second. > + > + mpls.mpls_ethertype = htons(eth_type); > + mpls.mpls_lse = htonl(lse << MPLS_LABEL_SHIFT | > + tc << MPLS_TC_SHIFT | Nit: Will look nicer with two spaces after 'tc'. > + ttl << MPLS_TTL_SHIFT | > + bos << MPLS_BOS_SHIFT); > + mpls.tun_flags = 0; > + nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ADD_MPLS, > + &mpls, sizeof mpls); > + > + return n; > + } > + } > > { > struct ovs_action_push_tnl data; > @@ -7890,7 +7922,7 @@ 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) > { > int base_n = flow_count_mpls_labels(base, NULL); > int flow_n = flow_count_mpls_labels(flow, NULL); > @@ -7938,18 +7970,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++; > } > } > @@ -8600,6 +8643,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); > + break; > default: > /* Only the above protocols are supported for encap. > * The check is done at action translation. */ > @@ -8622,6 +8669,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); > + break; > default: > /* Checks are done during translation. */ > OVS_NOT_REACHED(); > @@ -8667,7 +8718,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); > mpls_done = true; > } > commit_set_nsh_action(flow, base, odp_actions, wc, use_masked); > @@ -8675,7 +8726,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); > } > 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 ecf914eac..8b74386b1 100644 > --- a/lib/ofp-actions.c > +++ b/lib/ofp-actions.c > @@ -4468,6 +4468,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: > @@ -4519,6 +4520,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; > } > @@ -4600,6 +4603,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..14dfbae1e 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 *opmet = > + ALIGNED_CAST(struct ofp_ed_prop_mpls_ethertype *, *ofp_prop); > + if (len > sizeof(*opmet) || len > *remaining) { Please, don't parethesize the argument of a sizeof. I know that code in this file does that a lot, but it's not a reson to folow a bad pattern. Some for other places in this file. No need to touch existing code, but the new code should follow the coding style. > + return OFPERR_NXBAC_BAD_ED_PROP; > + } > + struct ofpact_ed_prop_mpls_ethertype *pmet = > + ofpbuf_put_uninit(out, sizeof(*pmet)); This structure contains an uninitialized padding. Later the ofpact will be compared with memcmp causing incorect behavior. Please, use ofpbuf_put_zero() here. See the following commit for an example of other places that needs to be fixed: 51dec40f8e9d ("ofp-ed-props: Fix using uninitialized padding for NSH encap actions.") Also, since you're adding a new OF action, please add a test to tests/ofp-actions.at. Again, see the example of such a test in the commit above. > + pmet->header.prop_class = prop_class; > + pmet->header.type = prop_type; > + pmet->header.len = len; > + pmet->ether_type = opmet->ether_type; > + break; > + } > + default: > + return OFPERR_NXBAC_UNKNOWN_ED_PROP; > + } > + break; > + } > default: > return OFPERR_NXBAC_UNKNOWN_ED_PROP; > } > @@ -134,6 +155,26 @@ 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 *pmet = > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, *prop); > + struct ofp_ed_prop_mpls_ethertype *opmet = > + ofpbuf_put_uninit(out, sizeof(*opmet)); > + opmet->header.prop_class = htons((*prop)->prop_class); > + opmet->header.type = (*prop)->type; > + opmet->header.len = > + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); > + opmet->ether_type = pmet->ether_type; > + prop_len = sizeof(*pmet); > + break; > + } > + default: > + return OFPERR_OFPBAC_BAD_ARGUMENT; > + } > + break; > + } > default: > return OFPERR_OFPBAC_BAD_ARGUMENT; > } > @@ -181,6 +222,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 +307,29 @@ 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 *pmet = > + ofpbuf_put_uninit(out, sizeof(*pmet)); > + pmet->header.prop_class = prop_class; > + pmet->header.type = prop_type; > + pmet->header.len = > + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); > + pmet->ether_type = ethertype; > + > + break; > + } > + default: > + /* Unsupported property types rejected before. */ > + OVS_NOT_REACHED(); > + } > + 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 *pmet = > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, prop); > + ds_put_format(s, "%s=%d", format_ed_prop_type(prop), > + pmet->ether_type); > + return; > + } > + default: > + OVS_NOT_REACHED(); > + } > default: > OVS_NOT_REACHED(); > } > diff --git a/lib/packets.c b/lib/packets.c > index 4a7643c5d..d373e1580 100644 > --- a/lib/packets.c > +++ b/lib/packets.c > @@ -418,6 +418,39 @@ 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_encap) > +{ > + if (!eth_type_mpls(ethtype)) { > + return; > + } > + > + if (!l3_encap) { > + ovs_be32 *header = dp_packet_push_uninit(packet, MPLS_HLEN); > + > + *header = lse; > + packet->l2_5_ofs = 0; > + packet->packet_type = htonl(PT_MPLS); > + } else { > + size_t len; > + char *header; > + > + 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 > @@ -440,6 +473,18 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype) > /* Invalidate offload flags as they are not valid after > * decapsulation of MPLS header. */ > dp_packet_reset_offload(packet); > + > + /* packet_type must be reset for the MPLS packets with no l2 header */ > + if (!len) { > + if (ethtype == htons(ETH_TYPE_TEB)) { > + /* The inner packet must be classsified as ethernet if the > + * ethtype is ETH_TYPE_TEB. */ > + packet->packet_type = htonl(PT_ETH); > + } else { > + packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, > + ntohs(ethtype)); Nit: Please, shift above line one space to the left. > + } > + } > } > } > > diff --git a/lib/packets.h b/lib/packets.h > index 515bb59b1..f522492b1 100644 > --- a/lib/packets.h > +++ b/lib/packets.h > @@ -356,6 +356,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_encap); > > /* 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 864c136b5..30e7caf54 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 8723cb4e8..0f68e281f 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -6404,6 +6404,54 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx, > } > } > > +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; Reverse xmas tree, please. :) And is it necessary to cast to 'char *' here? > + > + 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; > + } > + } > + } You're not advancing the 'ptr' here checking the same data. Please, add a unit test for this scenario. It's not obvious, how this should work. > + } > + > + wc->masks.packet_type = OVS_BE32_MAX; > + > + /* If the current packet is already a MPLS packet with ethernet header the > + * existing MPLS states must be cleared before the encap MPLS action is > + * applied */ Period at the end of a comment. > + if (flow->packet_type == htonl(PT_ETH) && > + flow->dl_type == htons(ETH_TYPE_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); > + memset(&ctx->base_flow.mpls_lse, 0x0, sizeof *ctx->base_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); I suppose, flow_push_mpls() will crash if n > FLOW_MAX_MPLS_LABELS. Should we check and stop processing in this case with XLATE_TOO_MANY_MPLS_LABELS ? Unit test for this would be great too. > + flow->dl_src = eth_addr_zero; > + flow->dl_dst = eth_addr_zero; > +} > + > /* 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 > * will be stored as encap_data in the ctx and copied into the push_nsh > @@ -6535,6 +6583,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(); > @@ -6606,6 +6660,30 @@ 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); > + if (!ethertype) { > + ethertype = htons(ETH_TYPE_TEB); > + } > + flow_pop_mpls(flow, n, ethertype, ctx->wc); Repeating myself, but what happens if the inner packet actually has a different packet type than one specified in the OF rule? > + > + if (!ctx->xbridge->support.add_mpls) { > + ctx->xout->slow |= SLOW_ACTION; > + } > + ctx->pending_decap = true; > + if (n == 1) { > + /* Trigger recirculation. */ > + return true; > + } else { > + return false; > + } > + } > default: > /* Error handling: drop packet. */ > xlate_report_debug( > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index cba49a99e..c65b992a7 100644 > --- a/ofproto/ofproto-dpif.c > +++ b/ofproto/ofproto-dpif.c > @@ -1538,6 +1538,42 @@ 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 \ > @@ -1609,6 +1645,7 @@ check_support(struct dpif_backer *backer) > backer->rt_support.lb_output_action = > dpif_supports_lb_output_action(backer->dpif); > backer->rt_support.ct_zero_snat = dpif_supports_ct_zero_snat(backer); > + backer->rt_support.add_mpls = check_add_mpls(backer); > > /* Flow fields. */ > backer->rt_support.odp.ct_state = check_ct_state(backer); > @@ -5625,6 +5662,7 @@ get_datapath_cap(const char *datapath_type, struct smap *cap) > s.explicit_drop_action ? "true" :"false"); > smap_add(cap, "lb_output_action", s.lb_output_action ? "true" : "false"); > smap_add(cap, "ct_zero_snat", s.ct_zero_snat ? "true" : "false"); > + smap_add(cap, "add_mpls", s.add_mpls ? "true" : "false"); > } > > /* Gets timeout policy name in 'backer' based on 'zone', 'dl_type' and > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h > index 191cfcb0d..a83cc42a9 100644 > --- a/ofproto/ofproto-dpif.h > +++ b/ofproto/ofproto-dpif.h > @@ -207,7 +207,9 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *, > DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode")\ > \ > /* True if the datapath supports all-zero IP SNAT. */ \ > - DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP SNAT") > + DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP SNAT") \ > + /* True if the datapath supports add_mpls action */ \ > + DPIF_SUPPORT_FIELD(bool, add_mpls, "MPLS Label add") > > > /* Stores the various features which the corresponding backer supports. */ > diff --git a/tests/mpls-xlate.at b/tests/mpls-xlate.at > index aafa89ba6..eada8501c 100644 > --- a/tests/mpls-xlate.at > +++ b/tests/mpls-xlate.at > @@ -207,3 +207,60 @@ AT_CHECK([tail -1 stdout], [0], > > OVS_VSWITCHD_STOP > AT_CLEANUP > + > +AT_SETUP([Encap Decap MPLS xlate action]) > + > +OVS_VSWITCHD_START( > + [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ > + add-port br0 p2 -- set Interface p2 type=patch \ > + options:peer=p3 ofport_request=2 -- \ > + add-br br1 -- \ > + set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ > + set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ > + fail-mode=secure -- \ > + add-port br1 p3 -- set Interface p3 type=patch \ > + options:peer=p2 ofport_request=3 -- \ > + add-port br1 p4 -- set Interface p4 type=dummy ofport_request=4]) > + > +AT_CHECK([ovs-appctl dpif/show], [0], [dnl > +dummy@ovs-dummy: hit:0 missed:0 > + br0: > + br0 65534/100: (dummy-internal) > + p1 1/1: (dummy) > + p2 2/none: (patch: peer=p3) > + br1: > + br1 65534/101: (dummy-internal) > + p3 3/none: (patch: peer=p2) > + p4 4/4: (dummy) > +]) > + > +AT_CHECK([ovs-ofctl del-flows br0]) > +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "in_port=p1,actions=encap(mpls(ether_type=0x8847)),encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:p2"]) > +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 "in_port=p3,dl_type=0x8847 actions=decap(),decap(packet_type(ns=0,type=0)),output:p4"]) > + > +# Now send two real ICMP echo request packets in on port p1 > + > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > + > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > + > +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], > +[flow-dump from the main thread: > +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:add_mpls(label=0,tc=0,ttl=64,bos=1,eth_type=0x8847),push_eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),pop_eth,pop_mpls(eth_type=0x6558),recirc(0x1) > +recirc_id(0x1),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 > +]) > + > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > + > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > + > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > + > +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], > +[flow-dump from the main thread: > +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:userspace(pid=0,slow_path(action)) > +recirc_id(0x2),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 > +]) > + > +OVS_VSWITCHD_STOP > +AT_CLEANUP > diff --git a/tests/odp.at b/tests/odp.at > index 07a5cfe39..4d08c59ca 100644 > --- a/tests/odp.at > +++ b/tests/odp.at > @@ -384,6 +384,7 @@ check_pkt_len(size=200,gt(drop),le(5)) > check_pkt_len(size=200,gt(ct(nat)),le(drop)) > check_pkt_len(size=200,gt(set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))),le(set(eth(src=00:01:02:03:04:06,dst=10:11:12:13:14:16)))) > lb_output(1) > +add_mpls(label=200,tc=7,ttl=64,bos=1,eth_type=0x8847) > ]) > AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], > [`cat actions.txt` > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > index c4442c183..ee7120ed5 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -1087,6 +1087,314 @@ NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], [0], > OVS_TRAFFIC_VSWITCHD_STOP > AT_CLEANUP > > +AT_SETUP([mpls - encap header dp-support]) > +OVS_TRAFFIC_VSWITCHD_START() > + > +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) Smae for other tests that use tcpdump. > + > +ADD_NAMESPACES(at_ns0, at_ns1) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > + > +dnl The flow will encap a mpls header to the ip packet > +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) > + > +rm -rf p1.pcap > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > +sleep 1 > + > +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp > +dnl The packet is sent from p0(at_ns0) interface directed to > +dnl p1(at_ns1) interface > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > + > +dnl Check the expected mpls encapsulated packet on the egress interface > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > +AT_SETUP([mpls - encap header slow-path]) > +OVS_TRAFFIC_VSWITCHD_START() > + > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > +ADD_NAMESPACES(at_ns0, at_ns1) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > + > +dnl The flow will encap a mpls header to the ip packet > +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) > + > +rm -rf p1.pcap > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > +sleep 1 > + > +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp > +dnl The packet is sent from p0(at_ns0) interface directed to > +dnl p1(at_ns1) interface > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > + > +dnl Check the expected mpls encapsulated packet on the egress interface > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > + > +AT_SETUP([mpls - decap header dp-support]) > +OVS_TRAFFIC_VSWITCHD_START() > + > +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) > + > +ADD_NAMESPACES(at_ns0, at_ns1) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > + > +dnl The flow will decap a mpls header which in turn carries a icmp packet > +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp > + > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) > + > +rm -rf p1.pcap > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > +sleep 1 > + > +dnl The hex dump is an mpls packet encapsulating ethernet packet. pkt=eth/mpls/eth/ip/icmp > +dnl The packet is sent from p0(at_ns0) interface directed to > +dnl p1(at_ns1) interface > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > + > +dnl Check the expected decapsulated on the egress interface > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 1>/dev/null]) > + > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > +AT_SETUP([mpls - decap header slow-path]) > +OVS_TRAFFIC_VSWITCHD_START() > + > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > +ADD_NAMESPACES(at_ns0, at_ns1) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > + > +dnl The flow will decap a mpls header which in turn carries a icmp packet > +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp > + > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) > + > +rm -rf p1.pcap > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > +sleep 1 > + > +dnl The hex dump is an mpls packet encapsulating ethernet packet. pkt=eth/mpls/eth/ip/icmp > +dnl The packet is sent from p0(at_ns0) interface directed to > +dnl p1(at_ns1) interface > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > + > +dnl Check the expected decapsulated on the egress interface > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 1>/dev/null]) > + > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > + > +AT_SETUP([datapath - Encap Decap 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,in_port=ovs-p0 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p0 > +]) > + > +AT_DATA([flows1.txt], [dnl > +table=0,priority=100,in_port=ovs-p1 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1 > +]) > + > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) > + > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > +]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > +AT_SETUP([datapath - multiple encap decap 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,in_port=ovs-p0 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p0 > +]) > + > +AT_DATA([flows1.txt], [dnl > +table=0,priority=100,in_port=ovs-p1 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p1 > +]) > + > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) > + > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > +]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > +AT_SETUP([datapath - encap mpls pop 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", 36:b1:ee:7c:01:03) > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) > + > +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=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,output:100 > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,resubmit(,3) > +table=0,priority=10 actions=resubmit(,3) > +table=3,priority=10 actions=normal > +]) > + > +AT_DATA([flows1.txt], [dnl > +table=0,priority=100,dl_type=0x0800 actions=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,output:100 > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,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 flows1.txt]) > + > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > +]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > +AT_SETUP([datapath - push mpls decap 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", 36:b1:ee:7c:01:03) > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) > + > +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=push_mpls:0x8847,set_field:2->mpls_label,output:100 > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,resubmit(,3) > +table=0,priority=10 actions=resubmit(,3) > +table=3,priority=10 actions=normal > +]) > + > +AT_DATA([flows1.txt], [dnl > +table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_field:2->mpls_label,output:100 > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,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 flows1.txt]) > + > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > +]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > AT_SETUP([datapath - basic truncate action]) > AT_SKIP_IF([test $HAVE_NC = no]) > OVS_TRAFFIC_VSWITCHD_START() >
On Fri, Nov 05, 2021 at 03:05:54PM +0100, Ilya Maximets wrote: > On 10/12/21 06:23, Martin Varghese wrote: > > From: Martin Varghese <martin.varghese@nokia.com> > > Hi, Martin. Thanks for working on this! > > I didn't review most of the logic of the action itself, but see some > comments inline. > > Bets regards, Ilya Maximets. > Hi Ilya, Thanks for your time. I will work on your comments. Meanwhile please let me know the additional comments you have. Please find my inline responses for your comments. > > > > The encap & decap actions are extended to support MPLS packet type. > > Encap & decap actions adds and removes MPLS header at start of the > > packet. > > > > The existing PUSH MPLS & POP MPLS actions inserts & removes MPLS > > header between ethernet header and the IP header. > > From the documentation it's really hard to understand the difference > between PUSH/POP_MPLS and encap/decap(mpls). Could you please, add > notes about the difference to both: description of PUSH/POP_MPLS > and encap/decap(mpls) in the ovs-actions.7.rst ? > I think the description of encap/decap MPLS is little confusing and could be made better. I think it is better to fix the description than referring ENCAP/DECAP in push_mpls/pop_mpls & vice versa. I am planning to change like below Encap The ``encap(mpls(ethertype=....))`` adds an MPLS header at the start of the packet. Decap Otherwise, if the packet is encapsulated inside a MPLS header, removes the MPLS header and classifies the inner packet as mentioned in the packet type argument of the decap. Does this make sense? > > Though this behaviour > > is fine for L3 VPN where an IP packet is encapsulated inside a MPLS > > tunnel, it does not suffice the L2 VPN requirements. In L2 VPN the > > ethernet packets must be encapsulated inside MPLS tunnel. > > > > In this change the encap & decap actions are extended to support MPLS > > packet type. The encap & decap adds and removes MPLS header at the > > start of packet as depicted below. > > > > Encapsulation: > > > > Actions - encap(mpls(ether_type=0x8847)),encap(ethernet) > > > > Incoming packet -> | ETH | IP | Payload | > > > > 1 Actions - encap(mpls(ether_type=0x8847)) [Datapath action - ADD_MPLS:0x8847] > > > > Outgoing packet -> | MPLS | ETH | Payload| > > > > 2 Actions - encap(ethernet) [ Datapath action - push_eth ] > > > > Outgoing packet -> | ETH | MPLS | ETH | Payload| > > > > Decapsulation: > > > > Incoming packet -> | ETH | MPLS | ETH | IP | Payload | > > > > Actions - decap(),decap(packet_type(ns=0,type=0) > > > > 1 Actions - decap() [Datapath action - pop_eth) > > > > Outgoing packet -> | MPLS | ETH | IP | Payload| > > > > 2 Actions - decap(packet_type(ns=0,type=0) [Datapath action - POP_MPLS:0x6558] > > > > Outgoing packet -> | ETH | IP | Payload| > > > > Signed-off-by: Martin Varghese <martin.varghese@nokia.com> > > --- > > Changes in v2: > > - Fixed the compilation error reported by bot. > > > > Changes in v3: > > - Adapted the changes to allign with the kernel implementaion > > > > Changes in v4: > > - Fixed the compilation error reported by bot. > > - Added SLOW_ACTION support for no datapath support. > > > > Changes in v5: > > - Code styling fixed. > > - Given reference for packet_type field in documentation. > > - Modified code to do recirc only for the last label. > > - Cleaed l2,l3,l4 fields with encap. > > - Fixed existing encap & decap tests to do set eth to outer header > > - Added tests to verify Encap & Decap with legacy Push & Pop. > > - Added xlate tests. > > - Added seperate tests to inspect packet after encap & decap. > > - Added tests for multiple mpls test cases. > > > > Changes in v6: > > - Styling and variable naming fixed. > > - Tests added for slow path support. > > - Added comments in code. > > - Added odp test for add_mpls action. > > - Documentation added in ovs-actions.7.rst > > > > Changes in v7: > > - Fixed Documentation. > > - Initialized variables in odp test. > > - Add test skip for no dp support. > > > > Documentation/ref/ovs-actions.7.rst | 17 +- > > NEWS | 2 +- > > .../linux/compat/include/linux/openvswitch.h | 33 +- > > 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 | 73 ++++- > > lib/ofp-actions.c | 5 + > > lib/ofp-ed-props.c | 91 ++++++ > > lib/packets.c | 45 +++ > > lib/packets.h | 2 + > > ofproto/ofproto-dpif-ipfix.c | 1 + > > ofproto/ofproto-dpif-sflow.c | 1 + > > ofproto/ofproto-dpif-xlate.c | 78 +++++ > > ofproto/ofproto-dpif.c | 38 +++ > > ofproto/ofproto-dpif.h | 4 +- > > tests/mpls-xlate.at | 57 ++++ > > tests/odp.at | 1 + > > tests/system-traffic.at | 308 ++++++++++++++++++ > > 20 files changed, 771 insertions(+), 17 deletions(-) > > > > diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst > > index 7224896df..9d60bf631 100644 > > --- a/Documentation/ref/ovs-actions.7.rst > > +++ b/Documentation/ref/ovs-actions.7.rst > > @@ -212,12 +212,13 @@ Unsupported packet type > > is not an L3 packet, and ``encap(nsh)`` raises this error if the current > > packet is not Ethernet, IPv4, IPv6, or NSH. > > > > + The ``decap`` action is supported only for packet types ethernet, NSH and > > + MPLS. Openvswitch raises this error for other packet types. > > Please, put two spaces between sentences like it's done in the rest of the document. > > > When a ``decap`` action decapsulates a packet, Open vSwitch raises this error > > if it does not support the type of inner packet. ``decap`` of an Ethernet > > header raises this error if a VLAN header is present, ``decap`` of a NSH > > packet raises this error if the NSH inner packet is not Ethernet, IPv4, IPv6, > > - or NSH, and ``decap`` of other types of packets is unsupported and also > > - raises this error. > > + or NSH. > > > > This error terminates packet processing, retaining any previous side effects > > (e.g. output actions). When this error arises within the execution of a > > @@ -995,6 +996,7 @@ The ``encap`` action > > | ``encap(nsh([md_type=``\ *md_type*\ > > ``], [tlv(``\ *class*,\ *type*,\ *value*\ ``)]...))`` > > | ``encap(ethernet)`` > > + | ``encap(mpls(ether_type=``\ *ether_type*\) > > The last ')' should also be highlighted with ``. > > > > > The ``encap`` action encapsulates a packet with a specified header. It has > > variants for different kinds of encapsulation. > > @@ -1018,6 +1020,10 @@ frame. The Ethernet type is initialized to the L3 packet's type, e.g. 0x0800 > > if the L3 packet is IPv4. The Ethernet source and destination are initially > > zeroed. > > > > +The ``encap(mpls(ethertype=....))`` variant encapsulates an ethernet or > > Hmm. So, is it 'ether_type' or 'ethertype' ? > > > +L3 packet with a MPLS header. The ethertype could be MPLS unicast (0x8847) or > > +multicast (0x8848) ethertypes. > > Please, highlight the hex numbers. > > > + > > **Conformance** > > This action is an Open vSwitch extension to OpenFlow 1.3 and later, > > introduced in Open vSwitch 2.8. > > This section, I think, should be updated in order to reflect in which OVS > version mpls variant is added. > > > @@ -1030,6 +1036,7 @@ The ``decap`` action > > > > **Syntax**: > > | ``decap`` > > + | ``decap([packet_type(ns=namespace,type=type)])`` > > Please, highlight the arguments. And brackets, I guess, > not needed here. It's either brackets or 2 syntax variants. I assume you meant the American brackets 😊 . I will remove “[]” ? > > > > > Removes an outermost encapsulation from the packet: > > > > @@ -1042,6 +1049,12 @@ Removes an outermost encapsulation from the packet: > > and NSH inner packet types. Other types raise unsupported packet type > > errors. > > > > + - 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. The packet_type field specifies the type of > > + the packet in the format specified in OpenFlow 1.5 chapter > > + `7.2.3.11 Packet Type Match Field` > > And what if the inner packet is not the packet stated in the OF action? > It will wrongly classify the inner packet. But this problem should be there for pop_mpls also. MPLS header has no next protocol field. > > + > > - Otherwise, raises an unsupported packet type error. > > > > **Conformance** > > This section needs an update. > > > diff --git a/NEWS b/NEWS > > index 90f4b1590..cac602d09 100644 > > --- a/NEWS > > +++ b/NEWS > > @@ -211,7 +211,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. > > - > > Please, keep the line. > > > + - Encap & Decap action support for MPLS packet type. > > Please, don't touch release notes for 2.14. Move to Post-2.16. > > > > > 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 f0595eeba..88a653771 100644 > > --- a/datapath/linux/compat/include/linux/openvswitch.h > > +++ b/datapath/linux/compat/include/linux/openvswitch.h > > @@ -817,8 +817,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. > > + */ > > + > > Above code is located in a different place in the kernel header. > Please move it accordingly. > > > +/* 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 > > @@ -1008,6 +1032,10 @@ 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_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. > > */ > > > > @@ -1037,6 +1065,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..96ef3d951 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,12 @@ 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 +107,13 @@ 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 75f381ec1..140302a90 100644 > > --- a/lib/dpif-netdev.c > > +++ b/lib/dpif-netdev.c > > @@ -8304,6 +8304,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 8c4aed47b..99aba3cae 100644 > > --- a/lib/dpif.c > > +++ b/lib/dpif.c > > @@ -1274,6 +1274,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 fbdfc7ad8..709f4f31a 100644 > > --- a/lib/odp-util.c > > +++ b/lib/odp-util.c > > @@ -142,6 +142,7 @@ 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 +1255,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_add_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; > > @@ -2595,6 +2604,29 @@ parse_odp_action__(struct parse_odp_context *context, const char *s, > > return retval; > > } > > } > > + { > > + struct ovs_action_add_mpls mpls; > > + uint32_t lse; > > + uint8_t ttl, tc, bos; > > + int n = -1; > > + uint16_t eth_type; > > + > > + if (ovs_scan(s, > > + "add_mpls(label=%"SCNi32",tc=%"SCNd8",ttl=%"SCNd8",bos=%"SCNd8",\ > > + eth_type=0x%"SCNx16")%n", &lse, &tc, &ttl, &bos, ð_type, &n)) { > > Please, don't use a line continuation here. Just close the '"' on the > first line and open a new one on the second. > > > + > > + mpls.mpls_ethertype = htons(eth_type); > > + mpls.mpls_lse = htonl(lse << MPLS_LABEL_SHIFT | > > + tc << MPLS_TC_SHIFT | > > Nit: Will look nicer with two spaces after 'tc'. > > > + ttl << MPLS_TTL_SHIFT | > > + bos << MPLS_BOS_SHIFT); > > + mpls.tun_flags = 0; > > + nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ADD_MPLS, > > + &mpls, sizeof mpls); > > + > > + return n; > > + } > > + } > > > > { > > struct ovs_action_push_tnl data; > > @@ -7890,7 +7922,7 @@ 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) > > { > > int base_n = flow_count_mpls_labels(base, NULL); > > int flow_n = flow_count_mpls_labels(flow, NULL); > > @@ -7938,18 +7970,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++; > > } > > } > > @@ -8600,6 +8643,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); > > + break; > > default: > > /* Only the above protocols are supported for encap. > > * The check is done at action translation. */ > > @@ -8622,6 +8669,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); > > + break; > > default: > > /* Checks are done during translation. */ > > OVS_NOT_REACHED(); > > @@ -8667,7 +8718,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); > > mpls_done = true; > > } > > commit_set_nsh_action(flow, base, odp_actions, wc, use_masked); > > @@ -8675,7 +8726,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); > > } > > 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 ecf914eac..8b74386b1 100644 > > --- a/lib/ofp-actions.c > > +++ b/lib/ofp-actions.c > > @@ -4468,6 +4468,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: > > @@ -4519,6 +4520,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; > > } > > @@ -4600,6 +4603,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..14dfbae1e 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 *opmet = > > + ALIGNED_CAST(struct ofp_ed_prop_mpls_ethertype *, *ofp_prop); > > + if (len > sizeof(*opmet) || len > *remaining) { > > Please, don't parethesize the argument of a sizeof. I know that > code in this file does that a lot, but it's not a reson to folow > a bad pattern. Some for other places in this file. > > No need to touch existing code, but the new code should follow > the coding style. > > > + return OFPERR_NXBAC_BAD_ED_PROP; > > + } > > + struct ofpact_ed_prop_mpls_ethertype *pmet = > > + ofpbuf_put_uninit(out, sizeof(*pmet)); > > This structure contains an uninitialized padding. Later the > ofpact will be compared with memcmp causing incorect behavior. > Please, use ofpbuf_put_zero() here. See the following commit > for an example of other places that needs to be fixed: > > 51dec40f8e9d ("ofp-ed-props: Fix using uninitialized padding for NSH encap actions.") > > Also, since you're adding a new OF action, please add a test > to tests/ofp-actions.at. Again, see the example of such a test > in the commit above. > > > + pmet->header.prop_class = prop_class; > > + pmet->header.type = prop_type; > > + pmet->header.len = len; > > + pmet->ether_type = opmet->ether_type; > > + break; > > + } > > + default: > > + return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > + } > > + break; > > + } > > default: > > return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > } > > @@ -134,6 +155,26 @@ 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 *pmet = > > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, *prop); > > + struct ofp_ed_prop_mpls_ethertype *opmet = > > + ofpbuf_put_uninit(out, sizeof(*opmet)); > > + opmet->header.prop_class = htons((*prop)->prop_class); > > + opmet->header.type = (*prop)->type; > > + opmet->header.len = > > + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); > > + opmet->ether_type = pmet->ether_type; > > + prop_len = sizeof(*pmet); > > + break; > > + } > > + default: > > + return OFPERR_OFPBAC_BAD_ARGUMENT; > > + } > > + break; > > + } > > default: > > return OFPERR_OFPBAC_BAD_ARGUMENT; > > } > > @@ -181,6 +222,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 +307,29 @@ 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 *pmet = > > + ofpbuf_put_uninit(out, sizeof(*pmet)); > > + pmet->header.prop_class = prop_class; > > + pmet->header.type = prop_type; > > + pmet->header.len = > > + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); > > + pmet->ether_type = ethertype; > > + > > + break; > > + } > > + default: > > + /* Unsupported property types rejected before. */ > > + OVS_NOT_REACHED(); > > + } > > + 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 *pmet = > > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, prop); > > + ds_put_format(s, "%s=%d", format_ed_prop_type(prop), > > + pmet->ether_type); > > + return; > > + } > > + default: > > + OVS_NOT_REACHED(); > > + } > > default: > > OVS_NOT_REACHED(); > > } > > diff --git a/lib/packets.c b/lib/packets.c > > index 4a7643c5d..d373e1580 100644 > > --- a/lib/packets.c > > +++ b/lib/packets.c > > @@ -418,6 +418,39 @@ 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_encap) > > +{ > > + if (!eth_type_mpls(ethtype)) { > > + return; > > + } > > + > > + if (!l3_encap) { > > + ovs_be32 *header = dp_packet_push_uninit(packet, MPLS_HLEN); > > + > > + *header = lse; > > + packet->l2_5_ofs = 0; > > + packet->packet_type = htonl(PT_MPLS); > > + } else { > > + size_t len; > > + char *header; > > + > > + 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 > > @@ -440,6 +473,18 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype) > > /* Invalidate offload flags as they are not valid after > > * decapsulation of MPLS header. */ > > dp_packet_reset_offload(packet); > > + > > + /* packet_type must be reset for the MPLS packets with no l2 header */ > > + if (!len) { > > + if (ethtype == htons(ETH_TYPE_TEB)) { > > + /* The inner packet must be classsified as ethernet if the > > + * ethtype is ETH_TYPE_TEB. */ > > + packet->packet_type = htonl(PT_ETH); > > + } else { > > + packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, > > + ntohs(ethtype)); > > Nit: Please, shift above line one space to the left. > > > + } > > + } > > } > > } > > > > diff --git a/lib/packets.h b/lib/packets.h > > index 515bb59b1..f522492b1 100644 > > --- a/lib/packets.h > > +++ b/lib/packets.h > > @@ -356,6 +356,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_encap); > > > > /* 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 864c136b5..30e7caf54 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 8723cb4e8..0f68e281f 100644 > > --- a/ofproto/ofproto-dpif-xlate.c > > +++ b/ofproto/ofproto-dpif-xlate.c > > @@ -6404,6 +6404,54 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx, > > } > > } > > > > +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; > > Reverse xmas tree, please. :) > > And is it necessary to cast to 'char *' here? > > > + > > + 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; > > + } > > + } > > + } > > You're not advancing the 'ptr' here checking the same data. > Please, add a unit test for this scenario. It's not obvious, > how this should work. > It will not work. I have tested only with n_props=1 and hence it worked for me. I took the implementation from NSH and didn’t adapt it correctly. I will rework this part. For MPLS, n_props always has to be 1. I will add a check for n_props=1 in parse_ENCAP for OFPPPC_MPLS prop_class. I Will leave a error message “Invalid Argument” if n_prop!=1. > > + } > > + > > + wc->masks.packet_type = OVS_BE32_MAX; > > + > > + /* If the current packet is already a MPLS packet with ethernet header the > > + * existing MPLS states must be cleared before the encap MPLS action is > > + * applied */ > > Period at the end of a comment. > > > + if (flow->packet_type == htonl(PT_ETH) && > > + flow->dl_type == htons(ETH_TYPE_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); > > + memset(&ctx->base_flow.mpls_lse, 0x0, sizeof *ctx->base_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); > > I suppose, flow_push_mpls() will crash if n > FLOW_MAX_MPLS_LABELS. > Should we check and stop processing in this case with > XLATE_TOO_MANY_MPLS_LABELS ? > > Unit test for this would be great too. > > > + flow->dl_src = eth_addr_zero; > > + flow->dl_dst = eth_addr_zero; > > +} > > + > > /* 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 > > * will be stored as encap_data in the ctx and copied into the push_nsh > > @@ -6535,6 +6583,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(); > > @@ -6606,6 +6660,30 @@ 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); > > + if (!ethertype) { > > + ethertype = htons(ETH_TYPE_TEB); > > + } > > + flow_pop_mpls(flow, n, ethertype, ctx->wc); > > Repeating myself, but what happens if the inner packet actually has a > different packet type than one specified in the OF rule? > > > + > > + if (!ctx->xbridge->support.add_mpls) { > > + ctx->xout->slow |= SLOW_ACTION; > > + } > > + ctx->pending_decap = true; > > + if (n == 1) { > > + /* Trigger recirculation. */ > > + return true; > > + } else { > > + return false; > > + } > > + } > > default: > > /* Error handling: drop packet. */ > > xlate_report_debug( > > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > > index cba49a99e..c65b992a7 100644 > > --- a/ofproto/ofproto-dpif.c > > +++ b/ofproto/ofproto-dpif.c > > @@ -1538,6 +1538,42 @@ 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 \ > > @@ -1609,6 +1645,7 @@ check_support(struct dpif_backer *backer) > > backer->rt_support.lb_output_action = > > dpif_supports_lb_output_action(backer->dpif); > > backer->rt_support.ct_zero_snat = dpif_supports_ct_zero_snat(backer); > > + backer->rt_support.add_mpls = check_add_mpls(backer); > > > > /* Flow fields. */ > > backer->rt_support.odp.ct_state = check_ct_state(backer); > > @@ -5625,6 +5662,7 @@ get_datapath_cap(const char *datapath_type, struct smap *cap) > > s.explicit_drop_action ? "true" :"false"); > > smap_add(cap, "lb_output_action", s.lb_output_action ? "true" : "false"); > > smap_add(cap, "ct_zero_snat", s.ct_zero_snat ? "true" : "false"); > > + smap_add(cap, "add_mpls", s.add_mpls ? "true" : "false"); > > } > > > > /* Gets timeout policy name in 'backer' based on 'zone', 'dl_type' and > > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h > > index 191cfcb0d..a83cc42a9 100644 > > --- a/ofproto/ofproto-dpif.h > > +++ b/ofproto/ofproto-dpif.h > > @@ -207,7 +207,9 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *, > > DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode")\ > > \ > > /* True if the datapath supports all-zero IP SNAT. */ \ > > - DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP SNAT") > > + DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP SNAT") \ > > + /* True if the datapath supports add_mpls action */ \ > > + DPIF_SUPPORT_FIELD(bool, add_mpls, "MPLS Label add") > > > > > > /* Stores the various features which the corresponding backer supports. */ > > diff --git a/tests/mpls-xlate.at b/tests/mpls-xlate.at > > index aafa89ba6..eada8501c 100644 > > --- a/tests/mpls-xlate.at > > +++ b/tests/mpls-xlate.at > > @@ -207,3 +207,60 @@ AT_CHECK([tail -1 stdout], [0], > > > > OVS_VSWITCHD_STOP > > AT_CLEANUP > > + > > +AT_SETUP([Encap Decap MPLS xlate action]) > > + > > +OVS_VSWITCHD_START( > > + [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ > > + add-port br0 p2 -- set Interface p2 type=patch \ > > + options:peer=p3 ofport_request=2 -- \ > > + add-br br1 -- \ > > + set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ > > + set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ > > + fail-mode=secure -- \ > > + add-port br1 p3 -- set Interface p3 type=patch \ > > + options:peer=p2 ofport_request=3 -- \ > > + add-port br1 p4 -- set Interface p4 type=dummy ofport_request=4]) > > + > > +AT_CHECK([ovs-appctl dpif/show], [0], [dnl > > +dummy@ovs-dummy: hit:0 missed:0 > > + br0: > > + br0 65534/100: (dummy-internal) > > + p1 1/1: (dummy) > > + p2 2/none: (patch: peer=p3) > > + br1: > > + br1 65534/101: (dummy-internal) > > + p3 3/none: (patch: peer=p2) > > + p4 4/4: (dummy) > > +]) > > + > > +AT_CHECK([ovs-ofctl del-flows br0]) > > +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "in_port=p1,actions=encap(mpls(ether_type=0x8847)),encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:p2"]) > > +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 "in_port=p3,dl_type=0x8847 actions=decap(),decap(packet_type(ns=0,type=0)),output:p4"]) > > + > > +# Now send two real ICMP echo request packets in on port p1 > > + > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > > + > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > > + > > +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], > > +[flow-dump from the main thread: > > +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:add_mpls(label=0,tc=0,ttl=64,bos=1,eth_type=0x8847),push_eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),pop_eth,pop_mpls(eth_type=0x6558),recirc(0x1) > > +recirc_id(0x1),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 > > +]) > > + > > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > > + > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > > + > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) > > + > > +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], > > +[flow-dump from the main thread: > > +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:userspace(pid=0,slow_path(action)) > > +recirc_id(0x2),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 > > +]) > > + > > +OVS_VSWITCHD_STOP > > +AT_CLEANUP > > diff --git a/tests/odp.at b/tests/odp.at > > index 07a5cfe39..4d08c59ca 100644 > > --- a/tests/odp.at > > +++ b/tests/odp.at > > @@ -384,6 +384,7 @@ check_pkt_len(size=200,gt(drop),le(5)) > > check_pkt_len(size=200,gt(ct(nat)),le(drop)) > > check_pkt_len(size=200,gt(set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))),le(set(eth(src=00:01:02:03:04:06,dst=10:11:12:13:14:16)))) > > lb_output(1) > > +add_mpls(label=200,tc=7,ttl=64,bos=1,eth_type=0x8847) > > ]) > > AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], > > [`cat actions.txt` > > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > > index c4442c183..ee7120ed5 100644 > > --- a/tests/system-traffic.at > > +++ b/tests/system-traffic.at > > @@ -1087,6 +1087,314 @@ NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], [0], > > OVS_TRAFFIC_VSWITCHD_STOP > > AT_CLEANUP > > > > +AT_SETUP([mpls - encap header dp-support]) > > +OVS_TRAFFIC_VSWITCHD_START() > > + > > +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) > > AT_SKIP_IF([test $HAVE_TCPDUMP = no]) > > Smae for other tests that use tcpdump. > > > + > > +ADD_NAMESPACES(at_ns0, at_ns1) > > + > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > + > > +dnl The flow will encap a mpls header to the ip packet > > +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) > > + > > +rm -rf p1.pcap > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > +sleep 1 > > + > > +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp > > +dnl The packet is sent from p0(at_ns0) interface directed to > > +dnl p1(at_ns1) interface > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > + > > +dnl Check the expected mpls encapsulated packet on the egress interface > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > +AT_SETUP([mpls - encap header slow-path]) > > +OVS_TRAFFIC_VSWITCHD_START() > > + > > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > > +ADD_NAMESPACES(at_ns0, at_ns1) > > + > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > + > > +dnl The flow will encap a mpls header to the ip packet > > +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) > > + > > +rm -rf p1.pcap > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > +sleep 1 > > + > > +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp > > +dnl The packet is sent from p0(at_ns0) interface directed to > > +dnl p1(at_ns1) interface > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > + > > +dnl Check the expected mpls encapsulated packet on the egress interface > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > + > > +AT_SETUP([mpls - decap header dp-support]) > > +OVS_TRAFFIC_VSWITCHD_START() > > + > > +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) > > + > > +ADD_NAMESPACES(at_ns0, at_ns1) > > + > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > + > > +dnl The flow will decap a mpls header which in turn carries a icmp packet > > +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp > > + > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) > > + > > +rm -rf p1.pcap > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > +sleep 1 > > + > > +dnl The hex dump is an mpls packet encapsulating ethernet packet. pkt=eth/mpls/eth/ip/icmp > > +dnl The packet is sent from p0(at_ns0) interface directed to > > +dnl p1(at_ns1) interface > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > + > > +dnl Check the expected decapsulated on the egress interface > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 1>/dev/null]) > > + > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > +AT_SETUP([mpls - decap header slow-path]) > > +OVS_TRAFFIC_VSWITCHD_START() > > + > > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > > +ADD_NAMESPACES(at_ns0, at_ns1) > > + > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > + > > +dnl The flow will decap a mpls header which in turn carries a icmp packet > > +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp > > + > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) > > + > > +rm -rf p1.pcap > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > +sleep 1 > > + > > +dnl The hex dump is an mpls packet encapsulating ethernet packet. pkt=eth/mpls/eth/ip/icmp > > +dnl The packet is sent from p0(at_ns0) interface directed to > > +dnl p1(at_ns1) interface > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > + > > +dnl Check the expected decapsulated on the egress interface > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 1>/dev/null]) > > + > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > + > > +AT_SETUP([datapath - Encap Decap 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,in_port=ovs-p0 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p0 > > +]) > > + > > +AT_DATA([flows1.txt], [dnl > > +table=0,priority=100,in_port=ovs-p1 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1 > > +]) > > + > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) > > + > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > +]) > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > +AT_SETUP([datapath - multiple encap decap 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,in_port=ovs-p0 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p0 > > +]) > > + > > +AT_DATA([flows1.txt], [dnl > > +table=0,priority=100,in_port=ovs-p1 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p1 > > +]) > > + > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) > > + > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > +]) > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > +AT_SETUP([datapath - encap mpls pop 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", 36:b1:ee:7c:01:03) > > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > + > > +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=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,output:100 > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,resubmit(,3) > > +table=0,priority=10 actions=resubmit(,3) > > +table=3,priority=10 actions=normal > > +]) > > + > > +AT_DATA([flows1.txt], [dnl > > +table=0,priority=100,dl_type=0x0800 actions=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,output:100 > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,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 flows1.txt]) > > + > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > +]) > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > +AT_SETUP([datapath - push mpls decap 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", 36:b1:ee:7c:01:03) > > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > + > > +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=push_mpls:0x8847,set_field:2->mpls_label,output:100 > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,resubmit(,3) > > +table=0,priority=10 actions=resubmit(,3) > > +table=3,priority=10 actions=normal > > +]) > > + > > +AT_DATA([flows1.txt], [dnl > > +table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_field:2->mpls_label,output:100 > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,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 flows1.txt]) > > + > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > +]) > > + > > +OVS_TRAFFIC_VSWITCHD_STOP > > +AT_CLEANUP > > + > > AT_SETUP([datapath - basic truncate action]) > > AT_SKIP_IF([test $HAVE_NC = no]) > > OVS_TRAFFIC_VSWITCHD_START() > > >
HI Ilya & Eelco, I have sent the v8 of the patch. In this version I have modified the encap action usage from "encap(mpls(ethertype=0x884*)" to "encap(mpls) and encap(mpls_mc)". MPLS unicast & MPLS multicast are 2 different packet_types. PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS), PT_MPLS_MC = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS_MCAST) Hence the new usage is more aligned with the encap action interface. struct ofpact_encap { OFPACT_PADDED_MEMBERS( struct ofpact ofpact; ovs_be32 new_pkt_type; /* Packet type of the header to add. */ uint16_t hdr_size; /* New header size in bytes. */ uint16_t n_props; /* Number of encap properties. */ ); struct ofpact_ed_prop props[]; /* Properties in internal format. */ }; With this change almost all the changes in ofp-ed-props.c are deleted and very few lines are added to accommodate PT_MPLS_MC. Please let me know your comments on v8. Thanks again for your time. Regards, Martin On Wed, Nov 10, 2021 at 9:11 PM Martin Varghese < martinvarghesenokia@gmail.com> wrote: > On Fri, Nov 05, 2021 at 03:05:54PM +0100, Ilya Maximets wrote: > > On 10/12/21 06:23, Martin Varghese wrote: > > > From: Martin Varghese <martin.varghese@nokia.com> > > > > Hi, Martin. Thanks for working on this! > > > > I didn't review most of the logic of the action itself, but see some > > comments inline. > > > > Bets regards, Ilya Maximets. > > > Hi Ilya, > > Thanks for your time. > I will work on your comments. Meanwhile please let me know the additional > comments you have. > > Please find my inline responses for your comments. > > > > > > > > > The encap & decap actions are extended to support MPLS packet type. > > > Encap & decap actions adds and removes MPLS header at start of the > > > packet. > > > > > > The existing PUSH MPLS & POP MPLS actions inserts & removes MPLS > > > header between ethernet header and the IP header. > > > > From the documentation it's really hard to understand the difference > > between PUSH/POP_MPLS and encap/decap(mpls). Could you please, add > > notes about the difference to both: description of PUSH/POP_MPLS > > and encap/decap(mpls) in the ovs-actions.7.rst ? > > > > I think the description of encap/decap MPLS is little confusing and could > be made better. > I think it is better to fix the description than referring ENCAP/DECAP in > push_mpls/pop_mpls & vice versa. > > I am planning to change like below > > Encap > > The ``encap(mpls(ethertype=....))`` adds an MPLS header at the start of > the packet. > > Decap > > Otherwise, if the packet is encapsulated inside a MPLS header, removes the > MPLS header and classifies the inner packet as mentioned in the packet type > argument of the decap. > > Does this make sense? > > > > > Though this behaviour > > > is fine for L3 VPN where an IP packet is encapsulated inside a MPLS > > > tunnel, it does not suffice the L2 VPN requirements. In L2 VPN the > > > ethernet packets must be encapsulated inside MPLS tunnel. > > > > > > In this change the encap & decap actions are extended to support MPLS > > > packet type. The encap & decap adds and removes MPLS header at the > > > start of packet as depicted below. > > > > > > Encapsulation: > > > > > > Actions - encap(mpls(ether_type=0x8847)),encap(ethernet) > > > > > > Incoming packet -> | ETH | IP | Payload | > > > > > > 1 Actions - encap(mpls(ether_type=0x8847)) [Datapath action - > ADD_MPLS:0x8847] > > > > > > Outgoing packet -> | MPLS | ETH | Payload| > > > > > > 2 Actions - encap(ethernet) [ Datapath action - push_eth ] > > > > > > Outgoing packet -> | ETH | MPLS | ETH | Payload| > > > > > > Decapsulation: > > > > > > Incoming packet -> | ETH | MPLS | ETH | IP | Payload | > > > > > > Actions - decap(),decap(packet_type(ns=0,type=0) > > > > > > 1 Actions - decap() [Datapath action - pop_eth) > > > > > > Outgoing packet -> | MPLS | ETH | IP | Payload| > > > > > > 2 Actions - decap(packet_type(ns=0,type=0) [Datapath action - > POP_MPLS:0x6558] > > > > > > Outgoing packet -> | ETH | IP | Payload| > > > > > > Signed-off-by: Martin Varghese <martin.varghese@nokia.com> > > > --- > > > Changes in v2: > > > - Fixed the compilation error reported by bot. > > > > > > Changes in v3: > > > - Adapted the changes to allign with the kernel implementaion > > > > > > Changes in v4: > > > - Fixed the compilation error reported by bot. > > > - Added SLOW_ACTION support for no datapath support. > > > > > > Changes in v5: > > > - Code styling fixed. > > > - Given reference for packet_type field in documentation. > > > - Modified code to do recirc only for the last label. > > > - Cleaed l2,l3,l4 fields with encap. > > > - Fixed existing encap & decap tests to do set eth to outer header > > > - Added tests to verify Encap & Decap with legacy Push & Pop. > > > - Added xlate tests. > > > - Added seperate tests to inspect packet after encap & decap. > > > - Added tests for multiple mpls test cases. > > > > > > Changes in v6: > > > - Styling and variable naming fixed. > > > - Tests added for slow path support. > > > - Added comments in code. > > > - Added odp test for add_mpls action. > > > - Documentation added in ovs-actions.7.rst > > > > > > Changes in v7: > > > - Fixed Documentation. > > > - Initialized variables in odp test. > > > - Add test skip for no dp support. > > > > > > Documentation/ref/ovs-actions.7.rst | 17 +- > > > NEWS | 2 +- > > > .../linux/compat/include/linux/openvswitch.h | 33 +- > > > 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 | 73 ++++- > > > lib/ofp-actions.c | 5 + > > > lib/ofp-ed-props.c | 91 ++++++ > > > lib/packets.c | 45 +++ > > > lib/packets.h | 2 + > > > ofproto/ofproto-dpif-ipfix.c | 1 + > > > ofproto/ofproto-dpif-sflow.c | 1 + > > > ofproto/ofproto-dpif-xlate.c | 78 +++++ > > > ofproto/ofproto-dpif.c | 38 +++ > > > ofproto/ofproto-dpif.h | 4 +- > > > tests/mpls-xlate.at | 57 ++++ > > > tests/odp.at | 1 + > > > tests/system-traffic.at | 308 > ++++++++++++++++++ > > > 20 files changed, 771 insertions(+), 17 deletions(-) > > > > > > diff --git a/Documentation/ref/ovs-actions.7.rst > b/Documentation/ref/ovs-actions.7.rst > > > index 7224896df..9d60bf631 100644 > > > --- a/Documentation/ref/ovs-actions.7.rst > > > +++ b/Documentation/ref/ovs-actions.7.rst > > > @@ -212,12 +212,13 @@ Unsupported packet type > > > is not an L3 packet, and ``encap(nsh)`` raises this error if the > current > > > packet is not Ethernet, IPv4, IPv6, or NSH. > > > > > > + The ``decap`` action is supported only for packet types ethernet, > NSH and > > > + MPLS. Openvswitch raises this error for other packet types. > > > > Please, put two spaces between sentences like it's done in the rest of > the document. > > > > > When a ``decap`` action decapsulates a packet, Open vSwitch raises > this error > > > if it does not support the type of inner packet. ``decap`` of an > Ethernet > > > header raises this error if a VLAN header is present, ``decap`` of > a NSH > > > packet raises this error if the NSH inner packet is not Ethernet, > IPv4, IPv6, > > > - or NSH, and ``decap`` of other types of packets is unsupported and > also > > > - raises this error. > > > + or NSH. > > > > > > This error terminates packet processing, retaining any previous > side effects > > > (e.g. output actions). When this error arises within the execution > of a > > > @@ -995,6 +996,7 @@ The ``encap`` action > > > | ``encap(nsh([md_type=``\ *md_type*\ > > > ``], [tlv(``\ *class*,\ *type*,\ *value*\ ``)]...))`` > > > | ``encap(ethernet)`` > > > + | ``encap(mpls(ether_type=``\ *ether_type*\) > > > > The last ')' should also be highlighted with ``. > > > > > > > > The ``encap`` action encapsulates a packet with a specified header. > It has > > > variants for different kinds of encapsulation. > > > @@ -1018,6 +1020,10 @@ frame. The Ethernet type is initialized to the > L3 packet's type, e.g. 0x0800 > > > if the L3 packet is IPv4. The Ethernet source and destination are > initially > > > zeroed. > > > > > > +The ``encap(mpls(ethertype=....))`` variant encapsulates an ethernet > or > > > > Hmm. So, is it 'ether_type' or 'ethertype' ? > > > > > +L3 packet with a MPLS header. The ethertype could be MPLS unicast > (0x8847) or > > > +multicast (0x8848) ethertypes. > > > > Please, highlight the hex numbers. > > > > > + > > > **Conformance** > > > This action is an Open vSwitch extension to OpenFlow 1.3 and later, > > > introduced in Open vSwitch 2.8. > > > > This section, I think, should be updated in order to reflect in which OVS > > version mpls variant is added. > > > > > @@ -1030,6 +1036,7 @@ The ``decap`` action > > > > > > **Syntax**: > > > | ``decap`` > > > + | ``decap([packet_type(ns=namespace,type=type)])`` > > > > Please, highlight the arguments. And brackets, I guess, > > not needed here. It's either brackets or 2 syntax variants. > > I assume you meant the American brackets 😊 . I will remove “[]” ? > > > > > > > > > Removes an outermost encapsulation from the packet: > > > > > > @@ -1042,6 +1049,12 @@ Removes an outermost encapsulation from the > packet: > > > and NSH inner packet types. Other types raise unsupported packet > type > > > errors. > > > > > > + - 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. The packet_type field specifies the type of > > > + the packet in the format specified in OpenFlow 1.5 chapter > > > + `7.2.3.11 Packet Type Match Field` > > > > And what if the inner packet is not the packet stated in the OF action? > > > It will wrongly classify the inner packet. But this problem should be > there for pop_mpls also. MPLS header has no next protocol field. > > > > > + > > > - Otherwise, raises an unsupported packet type error. > > > > > > **Conformance** > > > > This section needs an update. > > > > > diff --git a/NEWS b/NEWS > > > index 90f4b1590..cac602d09 100644 > > > --- a/NEWS > > > +++ b/NEWS > > > @@ -211,7 +211,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. > > > - > > > > Please, keep the line. > > > > > + - Encap & Decap action support for MPLS packet type. > > > > Please, don't touch release notes for 2.14. Move to Post-2.16. > > > > > > > > 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 f0595eeba..88a653771 100644 > > > --- a/datapath/linux/compat/include/linux/openvswitch.h > > > +++ b/datapath/linux/compat/include/linux/openvswitch.h > > > @@ -817,8 +817,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. > > > + */ > > > + > > > > Above code is located in a different place in the kernel header. > > Please move it accordingly. > > > > > +/* 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 > > > @@ -1008,6 +1032,10 @@ 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_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. > > > */ > > > > > > @@ -1037,6 +1065,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..96ef3d951 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,12 @@ 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 +107,13 @@ 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 75f381ec1..140302a90 100644 > > > --- a/lib/dpif-netdev.c > > > +++ b/lib/dpif-netdev.c > > > @@ -8304,6 +8304,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 8c4aed47b..99aba3cae 100644 > > > --- a/lib/dpif.c > > > +++ b/lib/dpif.c > > > @@ -1274,6 +1274,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 fbdfc7ad8..709f4f31a 100644 > > > --- a/lib/odp-util.c > > > +++ b/lib/odp-util.c > > > @@ -142,6 +142,7 @@ 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 +1255,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_add_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; > > > @@ -2595,6 +2604,29 @@ parse_odp_action__(struct parse_odp_context > *context, const char *s, > > > return retval; > > > } > > > } > > > + { > > > + struct ovs_action_add_mpls mpls; > > > + uint32_t lse; > > > + uint8_t ttl, tc, bos; > > > + int n = -1; > > > + uint16_t eth_type; > > > + > > > + if (ovs_scan(s, > > > + > "add_mpls(label=%"SCNi32",tc=%"SCNd8",ttl=%"SCNd8",bos=%"SCNd8",\ > > > + eth_type=0x%"SCNx16")%n", &lse, &tc, &ttl, &bos, > ð_type, &n)) { > > > > Please, don't use a line continuation here. Just close the '"' on the > > first line and open a new one on the second. > > > > > + > > > + mpls.mpls_ethertype = htons(eth_type); > > > + mpls.mpls_lse = htonl(lse << MPLS_LABEL_SHIFT | > > > + tc << MPLS_TC_SHIFT | > > > > Nit: Will look nicer with two spaces after 'tc'. > > > > > + ttl << MPLS_TTL_SHIFT | > > > + bos << MPLS_BOS_SHIFT); > > > + mpls.tun_flags = 0; > > > + nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ADD_MPLS, > > > + &mpls, sizeof mpls); > > > + > > > + return n; > > > + } > > > + } > > > > > > { > > > struct ovs_action_push_tnl data; > > > @@ -7890,7 +7922,7 @@ 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) > > > { > > > int base_n = flow_count_mpls_labels(base, NULL); > > > int flow_n = flow_count_mpls_labels(flow, NULL); > > > @@ -7938,18 +7970,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++; > > > } > > > } > > > @@ -8600,6 +8643,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); > > > + break; > > > default: > > > /* Only the above protocols are supported for encap. > > > * The check is done at action translation. */ > > > @@ -8622,6 +8669,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); > > > + break; > > > default: > > > /* Checks are done during translation. */ > > > OVS_NOT_REACHED(); > > > @@ -8667,7 +8718,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); > > > mpls_done = true; > > > } > > > commit_set_nsh_action(flow, base, odp_actions, wc, use_masked); > > > @@ -8675,7 +8726,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); > > > } > > > 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 ecf914eac..8b74386b1 100644 > > > --- a/lib/ofp-actions.c > > > +++ b/lib/ofp-actions.c > > > @@ -4468,6 +4468,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: > > > @@ -4519,6 +4520,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; > > > } > > > @@ -4600,6 +4603,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..14dfbae1e 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 *opmet = > > > + ALIGNED_CAST(struct ofp_ed_prop_mpls_ethertype *, > *ofp_prop); > > > + if (len > sizeof(*opmet) || len > *remaining) { > > > > Please, don't parethesize the argument of a sizeof. I know that > > code in this file does that a lot, but it's not a reson to folow > > a bad pattern. Some for other places in this file. > > > > No need to touch existing code, but the new code should follow > > the coding style. > > > > > + return OFPERR_NXBAC_BAD_ED_PROP; > > > + } > > > + struct ofpact_ed_prop_mpls_ethertype *pmet = > > > + ofpbuf_put_uninit(out, sizeof(*pmet)); > > > > This structure contains an uninitialized padding. Later the > > ofpact will be compared with memcmp causing incorect behavior. > > Please, use ofpbuf_put_zero() here. See the following commit > > for an example of other places that needs to be fixed: > > > > 51dec40f8e9d ("ofp-ed-props: Fix using uninitialized padding for NSH > encap actions.") > > > > Also, since you're adding a new OF action, please add a test > > to tests/ofp-actions.at. Again, see the example of such a test > > in the commit above. > > > > > + pmet->header.prop_class = prop_class; > > > + pmet->header.type = prop_type; > > > + pmet->header.len = len; > > > + pmet->ether_type = opmet->ether_type; > > > + break; > > > + } > > > + default: > > > + return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > > + } > > > + break; > > > + } > > > default: > > > return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > > } > > > @@ -134,6 +155,26 @@ 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 *pmet = > > > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, > *prop); > > > + struct ofp_ed_prop_mpls_ethertype *opmet = > > > + ofpbuf_put_uninit(out, sizeof(*opmet)); > > > + opmet->header.prop_class = htons((*prop)->prop_class); > > > + opmet->header.type = (*prop)->type; > > > + opmet->header.len = > > > + offsetof(struct ofpact_ed_prop_mpls_ethertype, > pad); > > > + opmet->ether_type = pmet->ether_type; > > > + prop_len = sizeof(*pmet); > > > + break; > > > + } > > > + default: > > > + return OFPERR_OFPBAC_BAD_ARGUMENT; > > > + } > > > + break; > > > + } > > > default: > > > return OFPERR_OFPBAC_BAD_ARGUMENT; > > > } > > > @@ -181,6 +222,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 +307,29 @@ 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 *pmet = > > > + ofpbuf_put_uninit(out, sizeof(*pmet)); > > > + pmet->header.prop_class = prop_class; > > > + pmet->header.type = prop_type; > > > + pmet->header.len = > > > + offsetof(struct ofpact_ed_prop_mpls_ethertype, > pad); > > > + pmet->ether_type = ethertype; > > > + > > > + break; > > > + } > > > + default: > > > + /* Unsupported property types rejected before. */ > > > + OVS_NOT_REACHED(); > > > + } > > > + 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 *pmet = > > > + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, > prop); > > > + ds_put_format(s, "%s=%d", format_ed_prop_type(prop), > > > + pmet->ether_type); > > > + return; > > > + } > > > + default: > > > + OVS_NOT_REACHED(); > > > + } > > > default: > > > OVS_NOT_REACHED(); > > > } > > > diff --git a/lib/packets.c b/lib/packets.c > > > index 4a7643c5d..d373e1580 100644 > > > --- a/lib/packets.c > > > +++ b/lib/packets.c > > > @@ -418,6 +418,39 @@ 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_encap) > > > +{ > > > + if (!eth_type_mpls(ethtype)) { > > > + return; > > > + } > > > + > > > + if (!l3_encap) { > > > + ovs_be32 *header = dp_packet_push_uninit(packet, MPLS_HLEN); > > > + > > > + *header = lse; > > > + packet->l2_5_ofs = 0; > > > + packet->packet_type = htonl(PT_MPLS); > > > + } else { > > > + size_t len; > > > + char *header; > > > + > > > + 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 > > > @@ -440,6 +473,18 @@ pop_mpls(struct dp_packet *packet, ovs_be16 > ethtype) > > > /* Invalidate offload flags as they are not valid after > > > * decapsulation of MPLS header. */ > > > dp_packet_reset_offload(packet); > > > + > > > + /* packet_type must be reset for the MPLS packets with no l2 > header */ > > > + if (!len) { > > > + if (ethtype == htons(ETH_TYPE_TEB)) { > > > + /* The inner packet must be classsified as ethernet > if the > > > + * ethtype is ETH_TYPE_TEB. */ > > > + packet->packet_type = htonl(PT_ETH); > > > + } else { > > > + packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, > > > + ntohs(ethtype)); > > > > Nit: Please, shift above line one space to the left. > > > > > + } > > > + } > > > } > > > } > > > > > > diff --git a/lib/packets.h b/lib/packets.h > > > index 515bb59b1..f522492b1 100644 > > > --- a/lib/packets.h > > > +++ b/lib/packets.h > > > @@ -356,6 +356,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_encap); > > > > > > /* 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 864c136b5..30e7caf54 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 8723cb4e8..0f68e281f 100644 > > > --- a/ofproto/ofproto-dpif-xlate.c > > > +++ b/ofproto/ofproto-dpif-xlate.c > > > @@ -6404,6 +6404,54 @@ rewrite_flow_encap_ethernet(struct xlate_ctx > *ctx, > > > } > > > } > > > > > > +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; > > > > Reverse xmas tree, please. :) > > > > And is it necessary to cast to 'char *' here? > > > > > + > > > + 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; > > > + } > > > + } > > > + } > > > > You're not advancing the 'ptr' here checking the same data. > > Please, add a unit test for this scenario. It's not obvious, > > how this should work. > > > It will not work. I have tested only with n_props=1 and hence it worked > for me. > I took the implementation from NSH and didn’t adapt it correctly. I will > rework this part. > > For MPLS, n_props always has to be 1. > I will add a check for n_props=1 in parse_ENCAP for OFPPPC_MPLS prop_class. > I Will leave a error message “Invalid Argument” if n_prop!=1. > > > > > + } > > > + > > > + wc->masks.packet_type = OVS_BE32_MAX; > > > + > > > + /* If the current packet is already a MPLS packet with ethernet > header the > > > + * existing MPLS states must be cleared before the encap MPLS > action is > > > + * applied */ > > > > Period at the end of a comment. > > > > > + if (flow->packet_type == htonl(PT_ETH) && > > > + flow->dl_type == htons(ETH_TYPE_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); > > > + memset(&ctx->base_flow.mpls_lse, 0x0, sizeof > *ctx->base_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); > > > > I suppose, flow_push_mpls() will crash if n > FLOW_MAX_MPLS_LABELS. > > Should we check and stop processing in this case with > > XLATE_TOO_MANY_MPLS_LABELS ? > > > > Unit test for this would be great too. > > > > > + flow->dl_src = eth_addr_zero; > > > + flow->dl_dst = eth_addr_zero; > > > +} > > > + > > > /* 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 > > > * will be stored as encap_data in the ctx and copied into the > push_nsh > > > @@ -6535,6 +6583,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(); > > > @@ -6606,6 +6660,30 @@ 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); > > > + if (!ethertype) { > > > + ethertype = htons(ETH_TYPE_TEB); > > > + } > > > + flow_pop_mpls(flow, n, ethertype, ctx->wc); > > > > Repeating myself, but what happens if the inner packet actually has a > > different packet type than one specified in the OF rule? > > > > > + > > > + if (!ctx->xbridge->support.add_mpls) { > > > + ctx->xout->slow |= SLOW_ACTION; > > > + } > > > + ctx->pending_decap = true; > > > + if (n == 1) { > > > + /* Trigger recirculation. */ > > > + return true; > > > + } else { > > > + return false; > > > + } > > > + } > > > default: > > > /* Error handling: drop packet. */ > > > xlate_report_debug( > > > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > > > index cba49a99e..c65b992a7 100644 > > > --- a/ofproto/ofproto-dpif.c > > > +++ b/ofproto/ofproto-dpif.c > > > @@ -1538,6 +1538,42 @@ 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 > \ > > > @@ -1609,6 +1645,7 @@ check_support(struct dpif_backer *backer) > > > backer->rt_support.lb_output_action = > > > dpif_supports_lb_output_action(backer->dpif); > > > backer->rt_support.ct_zero_snat = > dpif_supports_ct_zero_snat(backer); > > > + backer->rt_support.add_mpls = check_add_mpls(backer); > > > > > > /* Flow fields. */ > > > backer->rt_support.odp.ct_state = check_ct_state(backer); > > > @@ -5625,6 +5662,7 @@ get_datapath_cap(const char *datapath_type, > struct smap *cap) > > > s.explicit_drop_action ? "true" :"false"); > > > smap_add(cap, "lb_output_action", s.lb_output_action ? "true" : > "false"); > > > smap_add(cap, "ct_zero_snat", s.ct_zero_snat ? "true" : "false"); > > > + smap_add(cap, "add_mpls", s.add_mpls ? "true" : "false"); > > > } > > > > > > /* Gets timeout policy name in 'backer' based on 'zone', 'dl_type' and > > > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h > > > index 191cfcb0d..a83cc42a9 100644 > > > --- a/ofproto/ofproto-dpif.h > > > +++ b/ofproto/ofproto-dpif.h > > > @@ -207,7 +207,9 @@ struct group_dpif *group_dpif_lookup(struct > ofproto_dpif *, > > > DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP > mode")\ > > > > \ > > > /* True if the datapath supports all-zero IP SNAT. */ > \ > > > - DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP > SNAT") > > > + DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP > SNAT") \ > > > + /* True if the datapath supports add_mpls action */ > \ > > > + DPIF_SUPPORT_FIELD(bool, add_mpls, "MPLS Label add") > > > > > > > > > /* Stores the various features which the corresponding backer > supports. */ > > > diff --git a/tests/mpls-xlate.at b/tests/mpls-xlate.at > > > index aafa89ba6..eada8501c 100644 > > > --- a/tests/mpls-xlate.at > > > +++ b/tests/mpls-xlate.at > > > @@ -207,3 +207,60 @@ AT_CHECK([tail -1 stdout], [0], > > > > > > OVS_VSWITCHD_STOP > > > AT_CLEANUP > > > + > > > +AT_SETUP([Encap Decap MPLS xlate action]) > > > + > > > +OVS_VSWITCHD_START( > > > + [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- > \ > > > + add-port br0 p2 -- set Interface p2 type=patch \ > > > + options:peer=p3 > ofport_request=2 -- \ > > > + add-br br1 -- \ > > > + set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ > > > + set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ > > > + fail-mode=secure -- \ > > > + add-port br1 p3 -- set Interface p3 type=patch \ > > > + options:peer=p2 > ofport_request=3 -- \ > > > + add-port br1 p4 -- set Interface p4 type=dummy ofport_request=4]) > > > + > > > +AT_CHECK([ovs-appctl dpif/show], [0], [dnl > > > +dummy@ovs-dummy: hit:0 missed:0 > > > + br0: > > > + br0 65534/100: (dummy-internal) > > > + p1 1/1: (dummy) > > > + p2 2/none: (patch: peer=p3) > > > + br1: > > > + br1 65534/101: (dummy-internal) > > > + p3 3/none: (patch: peer=p2) > > > + p4 4/4: (dummy) > > > +]) > > > + > > > +AT_CHECK([ovs-ofctl del-flows br0]) > > > +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 > "in_port=p1,actions=encap(mpls(ether_type=0x8847)),encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:p2"]) > > > +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 > "in_port=p3,dl_type=0x8847 > actions=decap(),decap(packet_type(ns=0,type=0)),output:p4"]) > > > + > > > +# Now send two real ICMP echo request packets in on port p1 > > > + > > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 > 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] > ,[0], [ignore]) > > > + > > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 > 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] > ,[0], [ignore]) > > > + > > > +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | > grep -v ipv6 |sort], [0], > > > +[flow-dump from the main thread: > > > > +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), > packets:1, bytes:98, used:0.0s, > actions:add_mpls(label=0,tc=0,ttl=64,bos=1,eth_type=0x8847),push_eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),pop_eth,pop_mpls(eth_type=0x6558),recirc(0x1) > > > > +recirc_id(0x1),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), > packets:1, bytes:98, used:0.0s, actions:4 > > > +]) > > > + > > > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > > > + > > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 > 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] > ,[0], [ignore]) > > > + > > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 > 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] > ,[0], [ignore]) > > > + > > > +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | > grep -v ipv6 |sort], [0], > > > +[flow-dump from the main thread: > > > > +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), > packets:1, bytes:98, used:0.0s, actions:userspace(pid=0,slow_path(action)) > > > > +recirc_id(0x2),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), > packets:1, bytes:98, used:0.0s, actions:4 > > > +]) > > > + > > > +OVS_VSWITCHD_STOP > > > +AT_CLEANUP > > > diff --git a/tests/odp.at b/tests/odp.at > > > index 07a5cfe39..4d08c59ca 100644 > > > --- a/tests/odp.at > > > +++ b/tests/odp.at > > > @@ -384,6 +384,7 @@ check_pkt_len(size=200,gt(drop),le(5)) > > > check_pkt_len(size=200,gt(ct(nat)),le(drop)) > > > > check_pkt_len(size=200,gt(set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))),le(set(eth(src=00:01:02:03:04:06,dst=10:11:12:13:14:16)))) > > > lb_output(1) > > > +add_mpls(label=200,tc=7,ttl=64,bos=1,eth_type=0x8847) > > > ]) > > > AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], > > > [`cat actions.txt` > > > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > > > index c4442c183..ee7120ed5 100644 > > > --- a/tests/system-traffic.at > > > +++ b/tests/system-traffic.at > > > @@ -1087,6 +1087,314 @@ NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 > -w 2 10.1.1.1 | FORMAT_PING], [0], > > > OVS_TRAFFIC_VSWITCHD_STOP > > > AT_CLEANUP > > > > > > +AT_SETUP([mpls - encap header dp-support]) > > > +OVS_TRAFFIC_VSWITCHD_START() > > > + > > > +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS > Label add: Yes" >/dev/null]) > > > > AT_SKIP_IF([test $HAVE_TCPDUMP = no]) > > > > Smae for other tests that use tcpdump. > > > > > + > > > +ADD_NAMESPACES(at_ns0, at_ns1) > > > + > > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > > + > > > +dnl The flow will encap a mpls header to the ip packet > > > +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 > "table=0,priority=100,dl_type=0x0800 > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) > > > + > > > +rm -rf p1.pcap > > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > > +sleep 1 > > > + > > > +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp > > > +dnl The packet is sent from p0(at_ns0) interface directed to > > > +dnl p1(at_ns1) interface > > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c > 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 > 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 > 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 > 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > > + > > > +dnl Check the expected mpls encapsulated packet on the egress > interface > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 > *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 > *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 > *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac > *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > +AT_SETUP([mpls - encap header slow-path]) > > > +OVS_TRAFFIC_VSWITCHD_START() > > > + > > > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > > > +ADD_NAMESPACES(at_ns0, at_ns1) > > > + > > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > > + > > > +dnl The flow will encap a mpls header to the ip packet > > > +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 > "table=0,priority=100,dl_type=0x0800 > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) > > > + > > > +rm -rf p1.pcap > > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > > +sleep 1 > > > + > > > +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp > > > +dnl The packet is sent from p0(at_ns0) interface directed to > > > +dnl p1(at_ns1) interface > > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c > 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 > 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 > 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 > 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > > + > > > +dnl Check the expected mpls encapsulated packet on the egress > interface > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 > *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 > *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 > *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac > *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > + > > > +AT_SETUP([mpls - decap header dp-support]) > > > +OVS_TRAFFIC_VSWITCHD_START() > > > + > > > +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS > Label add: Yes" >/dev/null]) > > > + > > > +ADD_NAMESPACES(at_ns0, at_ns1) > > > + > > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > > + > > > +dnl The flow will decap a mpls header which in turn carries a icmp > packet > > > +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp > > > + > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 > "table=0,priority=100,dl_type=0x8847,mpls_label=2 > actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) > > > + > > > +rm -rf p1.pcap > > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > > +sleep 1 > > > + > > > +dnl The hex dump is an mpls packet encapsulating ethernet packet. > pkt=eth/mpls/eth/ip/icmp > > > +dnl The packet is sent from p0(at_ns0) interface directed to > > > +dnl p1(at_ns1) interface > > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 > 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 > 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 > ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 > 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b > 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > > + > > > +dnl Check the expected decapsulated on the egress interface > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 > *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 > *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 > *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 > *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d > *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d > *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 > 1>/dev/null]) > > > + > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > +AT_SETUP([mpls - decap header slow-path]) > > > +OVS_TRAFFIC_VSWITCHD_START() > > > + > > > +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) > > > +ADD_NAMESPACES(at_ns0, at_ns1) > > > + > > > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) > > > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > > + > > > +dnl The flow will decap a mpls header which in turn carries a icmp > packet > > > +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp > > > + > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 > "table=0,priority=100,dl_type=0x8847,mpls_label=2 > actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) > > > + > > > +rm -rf p1.pcap > > > +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) > > > +sleep 1 > > > + > > > +dnl The hex dump is an mpls packet encapsulating ethernet packet. > pkt=eth/mpls/eth/ip/icmp > > > +dnl The packet is sent from p0(at_ns0) interface directed to > > > +dnl p1(at_ns1) interface > > > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 > 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 > 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 > ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 > 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b > 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) > > > + > > > +dnl Check the expected decapsulated on the egress interface > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 > *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 > *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 > *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 > *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d > *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d > *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) > > > +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 > 1>/dev/null]) > > > + > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > + > > > +AT_SETUP([datapath - Encap Decap 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,in_port=ovs-p0 > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 > actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p0 > > > +]) > > > + > > > +AT_DATA([flows1.txt], [dnl > > > +table=0,priority=100,in_port=ovs-p1 > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 > actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1 > > > +]) > > > + > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) > > > + > > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | > FORMAT_PING], [0], [dnl > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > > +]) > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > +AT_SETUP([datapath - multiple encap decap 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,in_port=ovs-p0 > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, > encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 > actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p0 > > > +]) > > > + > > > +AT_DATA([flows1.txt], [dnl > > > +table=0,priority=100,in_port=ovs-p1 > actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, > encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 > > > +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 > actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p1 > > > +]) > > > + > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) > > > +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) > > > + > > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], > [0], [dnl > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > > +]) > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > +AT_SETUP([datapath - encap mpls pop 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", 36:b1:ee:7c:01:03) > > > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > > + > > > +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=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,output:100 > > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 > actions=pop_mpls:0x0800,resubmit(,3) > > > +table=0,priority=10 actions=resubmit(,3) > > > +table=3,priority=10 actions=normal > > > +]) > > > + > > > +AT_DATA([flows1.txt], [dnl > > > +table=0,priority=100,dl_type=0x0800 > actions=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,output:100 > > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 > actions=pop_mpls:0x0800,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 flows1.txt]) > > > + > > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], > [0], [dnl > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > > +]) > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > +AT_SETUP([datapath - push mpls decap 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", 36:b1:ee:7c:01:03) > > > +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) > > > + > > > +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=push_mpls:0x8847,set_field:2->mpls_label,output:100 > > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 > actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,resubmit(,3) > > > +table=0,priority=10 actions=resubmit(,3) > > > +table=3,priority=10 actions=normal > > > +]) > > > + > > > +AT_DATA([flows1.txt], [dnl > > > +table=0,priority=100,dl_type=0x0800 > actions=push_mpls:0x8847,set_field:2->mpls_label,output:100 > > > +table=0,priority=100,dl_type=0x8847,mpls_label=2 > actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,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 flows1.txt]) > > > + > > > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], > [0], [dnl > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > > > +]) > > > + > > > +OVS_TRAFFIC_VSWITCHD_STOP > > > +AT_CLEANUP > > > + > > > AT_SETUP([datapath - basic truncate action]) > > > AT_SKIP_IF([test $HAVE_NC = no]) > > > OVS_TRAFFIC_VSWITCHD_START() > > > > > >
diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst index 7224896df..9d60bf631 100644 --- a/Documentation/ref/ovs-actions.7.rst +++ b/Documentation/ref/ovs-actions.7.rst @@ -212,12 +212,13 @@ Unsupported packet type is not an L3 packet, and ``encap(nsh)`` raises this error if the current packet is not Ethernet, IPv4, IPv6, or NSH. + The ``decap`` action is supported only for packet types ethernet, NSH and + MPLS. Openvswitch raises this error for other packet types. When a ``decap`` action decapsulates a packet, Open vSwitch raises this error if it does not support the type of inner packet. ``decap`` of an Ethernet header raises this error if a VLAN header is present, ``decap`` of a NSH packet raises this error if the NSH inner packet is not Ethernet, IPv4, IPv6, - or NSH, and ``decap`` of other types of packets is unsupported and also - raises this error. + or NSH. This error terminates packet processing, retaining any previous side effects (e.g. output actions). When this error arises within the execution of a @@ -995,6 +996,7 @@ The ``encap`` action | ``encap(nsh([md_type=``\ *md_type*\ ``], [tlv(``\ *class*,\ *type*,\ *value*\ ``)]...))`` | ``encap(ethernet)`` + | ``encap(mpls(ether_type=``\ *ether_type*\) The ``encap`` action encapsulates a packet with a specified header. It has variants for different kinds of encapsulation. @@ -1018,6 +1020,10 @@ frame. The Ethernet type is initialized to the L3 packet's type, e.g. 0x0800 if the L3 packet is IPv4. The Ethernet source and destination are initially zeroed. +The ``encap(mpls(ethertype=....))`` variant encapsulates an ethernet or +L3 packet with a MPLS header. The ethertype could be MPLS unicast (0x8847) or +multicast (0x8848) ethertypes. + **Conformance** This action is an Open vSwitch extension to OpenFlow 1.3 and later, introduced in Open vSwitch 2.8. @@ -1030,6 +1036,7 @@ The ``decap`` action **Syntax**: | ``decap`` + | ``decap([packet_type(ns=namespace,type=type)])`` Removes an outermost encapsulation from the packet: @@ -1042,6 +1049,12 @@ Removes an outermost encapsulation from the packet: and NSH inner packet types. Other types raise unsupported packet type errors. + - 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. The packet_type field specifies the type of + the packet in the format specified in OpenFlow 1.5 chapter + `7.2.3.11 Packet Type Match Field` + - Otherwise, raises an unsupported packet type error. **Conformance** diff --git a/NEWS b/NEWS index 90f4b1590..cac602d09 100644 --- a/NEWS +++ b/NEWS @@ -211,7 +211,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 f0595eeba..88a653771 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -817,8 +817,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 @@ -1008,6 +1032,10 @@ 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_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. */ @@ -1037,6 +1065,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..96ef3d951 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,12 @@ 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 +107,13 @@ 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 75f381ec1..140302a90 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -8304,6 +8304,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 8c4aed47b..99aba3cae 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1274,6 +1274,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 fbdfc7ad8..709f4f31a 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -142,6 +142,7 @@ 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 +1255,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_add_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; @@ -2595,6 +2604,29 @@ parse_odp_action__(struct parse_odp_context *context, const char *s, return retval; } } + { + struct ovs_action_add_mpls mpls; + uint32_t lse; + uint8_t ttl, tc, bos; + int n = -1; + uint16_t eth_type; + + if (ovs_scan(s, + "add_mpls(label=%"SCNi32",tc=%"SCNd8",ttl=%"SCNd8",bos=%"SCNd8",\ + eth_type=0x%"SCNx16")%n", &lse, &tc, &ttl, &bos, ð_type, &n)) { + + mpls.mpls_ethertype = htons(eth_type); + mpls.mpls_lse = htonl(lse << MPLS_LABEL_SHIFT | + tc << MPLS_TC_SHIFT | + ttl << MPLS_TTL_SHIFT | + bos << MPLS_BOS_SHIFT); + mpls.tun_flags = 0; + nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ADD_MPLS, + &mpls, sizeof mpls); + + return n; + } + } { struct ovs_action_push_tnl data; @@ -7890,7 +7922,7 @@ 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) { int base_n = flow_count_mpls_labels(base, NULL); int flow_n = flow_count_mpls_labels(flow, NULL); @@ -7938,18 +7970,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++; } } @@ -8600,6 +8643,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); + break; default: /* Only the above protocols are supported for encap. * The check is done at action translation. */ @@ -8622,6 +8669,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); + break; default: /* Checks are done during translation. */ OVS_NOT_REACHED(); @@ -8667,7 +8718,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); mpls_done = true; } commit_set_nsh_action(flow, base, odp_actions, wc, use_masked); @@ -8675,7 +8726,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); } 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 ecf914eac..8b74386b1 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -4468,6 +4468,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: @@ -4519,6 +4520,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; } @@ -4600,6 +4603,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..14dfbae1e 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 *opmet = + ALIGNED_CAST(struct ofp_ed_prop_mpls_ethertype *, *ofp_prop); + if (len > sizeof(*opmet) || len > *remaining) { + return OFPERR_NXBAC_BAD_ED_PROP; + } + struct ofpact_ed_prop_mpls_ethertype *pmet = + ofpbuf_put_uninit(out, sizeof(*pmet)); + pmet->header.prop_class = prop_class; + pmet->header.type = prop_type; + pmet->header.len = len; + pmet->ether_type = opmet->ether_type; + break; + } + default: + return OFPERR_NXBAC_UNKNOWN_ED_PROP; + } + break; + } default: return OFPERR_NXBAC_UNKNOWN_ED_PROP; } @@ -134,6 +155,26 @@ 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 *pmet = + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, *prop); + struct ofp_ed_prop_mpls_ethertype *opmet = + ofpbuf_put_uninit(out, sizeof(*opmet)); + opmet->header.prop_class = htons((*prop)->prop_class); + opmet->header.type = (*prop)->type; + opmet->header.len = + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); + opmet->ether_type = pmet->ether_type; + prop_len = sizeof(*pmet); + break; + } + default: + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + break; + } default: return OFPERR_OFPBAC_BAD_ARGUMENT; } @@ -181,6 +222,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 +307,29 @@ 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 *pmet = + ofpbuf_put_uninit(out, sizeof(*pmet)); + pmet->header.prop_class = prop_class; + pmet->header.type = prop_type; + pmet->header.len = + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); + pmet->ether_type = ethertype; + + break; + } + default: + /* Unsupported property types rejected before. */ + OVS_NOT_REACHED(); + } + 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 *pmet = + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, prop); + ds_put_format(s, "%s=%d", format_ed_prop_type(prop), + pmet->ether_type); + return; + } + default: + OVS_NOT_REACHED(); + } default: OVS_NOT_REACHED(); } diff --git a/lib/packets.c b/lib/packets.c index 4a7643c5d..d373e1580 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -418,6 +418,39 @@ 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_encap) +{ + if (!eth_type_mpls(ethtype)) { + return; + } + + if (!l3_encap) { + ovs_be32 *header = dp_packet_push_uninit(packet, MPLS_HLEN); + + *header = lse; + packet->l2_5_ofs = 0; + packet->packet_type = htonl(PT_MPLS); + } else { + size_t len; + char *header; + + 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 @@ -440,6 +473,18 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype) /* Invalidate offload flags as they are not valid after * decapsulation of MPLS header. */ dp_packet_reset_offload(packet); + + /* packet_type must be reset for the MPLS packets with no l2 header */ + if (!len) { + if (ethtype == htons(ETH_TYPE_TEB)) { + /* The inner packet must be classsified as ethernet if the + * ethtype is ETH_TYPE_TEB. */ + packet->packet_type = htonl(PT_ETH); + } else { + packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, + ntohs(ethtype)); + } + } } } diff --git a/lib/packets.h b/lib/packets.h index 515bb59b1..f522492b1 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -356,6 +356,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_encap); /* 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 864c136b5..30e7caf54 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 8723cb4e8..0f68e281f 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -6404,6 +6404,54 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx, } } +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 the current packet is already a MPLS packet with ethernet header the + * existing MPLS states must be cleared before the encap MPLS action is + * applied */ + if (flow->packet_type == htonl(PT_ETH) && + flow->dl_type == htons(ETH_TYPE_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); + memset(&ctx->base_flow.mpls_lse, 0x0, sizeof *ctx->base_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); + flow->dl_src = eth_addr_zero; + flow->dl_dst = eth_addr_zero; +} + /* 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 * will be stored as encap_data in the ctx and copied into the push_nsh @@ -6535,6 +6583,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(); @@ -6606,6 +6660,30 @@ 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); + if (!ethertype) { + ethertype = htons(ETH_TYPE_TEB); + } + flow_pop_mpls(flow, n, ethertype, ctx->wc); + + if (!ctx->xbridge->support.add_mpls) { + ctx->xout->slow |= SLOW_ACTION; + } + ctx->pending_decap = true; + if (n == 1) { + /* Trigger recirculation. */ + return true; + } else { + return false; + } + } default: /* Error handling: drop packet. */ xlate_report_debug( diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index cba49a99e..c65b992a7 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1538,6 +1538,42 @@ 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 \ @@ -1609,6 +1645,7 @@ check_support(struct dpif_backer *backer) backer->rt_support.lb_output_action = dpif_supports_lb_output_action(backer->dpif); backer->rt_support.ct_zero_snat = dpif_supports_ct_zero_snat(backer); + backer->rt_support.add_mpls = check_add_mpls(backer); /* Flow fields. */ backer->rt_support.odp.ct_state = check_ct_state(backer); @@ -5625,6 +5662,7 @@ get_datapath_cap(const char *datapath_type, struct smap *cap) s.explicit_drop_action ? "true" :"false"); smap_add(cap, "lb_output_action", s.lb_output_action ? "true" : "false"); smap_add(cap, "ct_zero_snat", s.ct_zero_snat ? "true" : "false"); + smap_add(cap, "add_mpls", s.add_mpls ? "true" : "false"); } /* Gets timeout policy name in 'backer' based on 'zone', 'dl_type' and diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 191cfcb0d..a83cc42a9 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -207,7 +207,9 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *, DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode")\ \ /* True if the datapath supports all-zero IP SNAT. */ \ - DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP SNAT") + DPIF_SUPPORT_FIELD(bool, ct_zero_snat, "Conntrack all-zero IP SNAT") \ + /* True if the datapath supports add_mpls action */ \ + DPIF_SUPPORT_FIELD(bool, add_mpls, "MPLS Label add") /* Stores the various features which the corresponding backer supports. */ diff --git a/tests/mpls-xlate.at b/tests/mpls-xlate.at index aafa89ba6..eada8501c 100644 --- a/tests/mpls-xlate.at +++ b/tests/mpls-xlate.at @@ -207,3 +207,60 @@ AT_CHECK([tail -1 stdout], [0], OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([Encap Decap MPLS xlate action]) + +OVS_VSWITCHD_START( + [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ + add-port br0 p2 -- set Interface p2 type=patch \ + options:peer=p3 ofport_request=2 -- \ + add-br br1 -- \ + set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ + set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ + fail-mode=secure -- \ + add-port br1 p3 -- set Interface p3 type=patch \ + options:peer=p2 ofport_request=3 -- \ + add-port br1 p4 -- set Interface p4 type=dummy ofport_request=4]) + +AT_CHECK([ovs-appctl dpif/show], [0], [dnl +dummy@ovs-dummy: hit:0 missed:0 + br0: + br0 65534/100: (dummy-internal) + p1 1/1: (dummy) + p2 2/none: (patch: peer=p3) + br1: + br1 65534/101: (dummy-internal) + p3 3/none: (patch: peer=p2) + p4 4/4: (dummy) +]) + +AT_CHECK([ovs-ofctl del-flows br0]) +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "in_port=p1,actions=encap(mpls(ether_type=0x8847)),encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:p2"]) +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 "in_port=p3,dl_type=0x8847 actions=decap(),decap(packet_type(ns=0,type=0)),output:p4"]) + +# Now send two real ICMP echo request packets in on port p1 + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) + +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], +[flow-dump from the main thread: +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:add_mpls(label=0,tc=0,ttl=64,bos=1,eth_type=0x8847),push_eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),pop_eth,pop_mpls(eth_type=0x6558),recirc(0x1) +recirc_id(0x1),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 +]) + +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) + +AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], +[flow-dump from the main thread: +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:userspace(pid=0,slow_path(action)) +recirc_id(0x2),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP diff --git a/tests/odp.at b/tests/odp.at index 07a5cfe39..4d08c59ca 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -384,6 +384,7 @@ check_pkt_len(size=200,gt(drop),le(5)) check_pkt_len(size=200,gt(ct(nat)),le(drop)) check_pkt_len(size=200,gt(set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))),le(set(eth(src=00:01:02:03:04:06,dst=10:11:12:13:14:16)))) lb_output(1) +add_mpls(label=200,tc=7,ttl=64,bos=1,eth_type=0x8847) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [`cat actions.txt` diff --git a/tests/system-traffic.at b/tests/system-traffic.at index c4442c183..ee7120ed5 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -1087,6 +1087,314 @@ NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], [0], OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([mpls - encap header dp-support]) +OVS_TRAFFIC_VSWITCHD_START() + +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) + +ADD_NAMESPACES(at_ns0, at_ns1) + +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) + +dnl The flow will encap a mpls header to the ip packet +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) + +rm -rf p1.pcap +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) +sleep 1 + +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp +dnl The packet is sent from p0(at_ns0) interface directed to +dnl p1(at_ns1) interface +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) + +dnl Check the expected mpls encapsulated packet on the egress interface +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([mpls - encap header slow-path]) +OVS_TRAFFIC_VSWITCHD_START() + +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) +ADD_NAMESPACES(at_ns0, at_ns1) + +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) + +dnl The flow will encap a mpls header to the ip packet +dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) + +rm -rf p1.pcap +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) +sleep 1 + +dnl The hex dump is a icmp packet. pkt=eth/ip/icmp +dnl The packet is sent from p0(at_ns0) interface directed to +dnl p1(at_ns1) interface +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) + +dnl Check the expected mpls encapsulated packet on the egress interface +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *0000 *0000 *0002 *0000 *0000 *0001 *8847 *0000" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *2140 *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *4500 *0054 *0344 *4000 *4001 *2161 *0a01 *0101" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0a01 *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61" 2>&1 1>/dev/null]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + + +AT_SETUP([mpls - decap header dp-support]) +OVS_TRAFFIC_VSWITCHD_START() + +AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) + +ADD_NAMESPACES(at_ns0, at_ns1) + +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) + +dnl The flow will decap a mpls header which in turn carries a icmp packet +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp + +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) + +rm -rf p1.pcap +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) +sleep 1 + +dnl The hex dump is an mpls packet encapsulating ethernet packet. pkt=eth/mpls/eth/ip/icmp +dnl The packet is sent from p0(at_ns0) interface directed to +dnl p1(at_ns1) interface +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) + +dnl Check the expected decapsulated on the egress interface +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 1>/dev/null]) + + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([mpls - decap header slow-path]) +OVS_TRAFFIC_VSWITCHD_START() + +AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) +ADD_NAMESPACES(at_ns0, at_ns1) + +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) + +dnl The flow will decap a mpls header which in turn carries a icmp packet +dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp + +AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) + +rm -rf p1.pcap +NS_CHECK_EXEC([at_ns1], [tcpdump -l -n -xx -U -i p1 > p1.pcap &]) +sleep 1 + +dnl The hex dump is an mpls packet encapsulating ethernet packet. pkt=eth/mpls/eth/ip/icmp +dnl The packet is sent from p0(at_ns0) interface directed to +dnl p1(at_ns1) interface +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) + +dnl Check the expected decapsulated on the egress interface +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0000: *36b1 *ee7c *0102 *36b1 *ee7c *0103 *0800 *4500" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0010: *0054 *0344 *4000 *4001 *2161 *0a01 *0101 *0a01" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0020: *0102 *0800 *efac *7ce4 *0003 *5b2c *1f61 *0000" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0030: *0000 *500b *0200 *0000 *0000 *1011 *1213 *1415" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | egrep "0x0060: *3637" 2>&1 1>/dev/null]) + + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + + +AT_SETUP([datapath - Encap Decap 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,in_port=ovs-p0 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p0 +]) + +AT_DATA([flows1.txt], [dnl +table=0,priority=100,in_port=ovs-p1 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1 +]) + +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([datapath - multiple encap decap 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,in_port=ovs-p0 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p0 +]) + +AT_DATA([flows1.txt], [dnl +table=0,priority=100,in_port=ovs-p1 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:3, encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 +table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p1 +]) + +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([datapath - encap mpls pop 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", 36:b1:ee:7c:01:03) +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) + +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=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,output:100 +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,resubmit(,3) +table=0,priority=10 actions=resubmit(,3) +table=3,priority=10 actions=normal +]) + +AT_DATA([flows1.txt], [dnl +table=0,priority=100,dl_type=0x0800 actions=decap,encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,output:100 +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,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 flows1.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([datapath - push mpls decap 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", 36:b1:ee:7c:01:03) +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) + +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=push_mpls:0x8847,set_field:2->mpls_label,output:100 +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,resubmit(,3) +table=0,priority=10 actions=resubmit(,3) +table=3,priority=10 actions=normal +]) + +AT_DATA([flows1.txt], [dnl +table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_field:2->mpls_label,output:100 +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,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 flows1.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 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 10.1.1.1 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([datapath - basic truncate action]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_TRAFFIC_VSWITCHD_START()