From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 3363D42D26; Thu, 22 Jun 2023 21:48:08 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B71A640DDA; Thu, 22 Jun 2023 21:48:07 +0200 (CEST) Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) by mails.dpdk.org (Postfix) with ESMTP id A7A0B406BA for ; Thu, 22 Jun 2023 21:48:06 +0200 (CEST) Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-3111547c8f9so8752192f8f.1 for ; Thu, 22 Jun 2023 12:48:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1687463286; x=1690055286; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=kYIaDYzm8lJmOHHsAeX4VW8CdqEyKpYP/BcRkHOSaz8=; b=fBjnQddfIzYUir39HrCRLIGGllz1c5s36a97dLm4kNm9t1SJn+F8QYn2OAhZpckoeS tOwRksIuwET+kkJMe+O093UzJOcHLsJmC+d1om+2c5j3Pj7vQ+AG/rFma1CYyRSglQXK dnGS9xo52Uh7rJZg3RK6NcHtuI1dhKr+wd/t8pYR+Ityp8i636iGw8dF/WoMYRyfgxJD yrGECclYLa5pp8Wo5Mfi9jde1VlU9vEWQtny6oNDz7kGIPvwSpx9B4MqP0QTtNSsr+6n HZxf0K1fQOHsw3xeK2gzP0v4FwXGxm3iSbLXX6qqJqNf2mfqUXtTmHhv2eBBTXXppbi6 zzfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687463286; x=1690055286; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=kYIaDYzm8lJmOHHsAeX4VW8CdqEyKpYP/BcRkHOSaz8=; b=TKjhyA/XVKgyk3ihIGUBzb8wKqnonFsOETE2iYvJJsvWOSxXUF2MQDHzpENwupsTJb k03AQb4pvEkDcmK4fjJGquRFUWXzY0AAFoqTWMihKmifuTTQ1PqlMgkUfT4nwQZXVgEn i772QgEDxJBjw94jxuvJCXUVCN58lLaprpsC9WVd9NM+LlTR1KCWyjePr7q2CH2mD/rg 39x1hc6OjIOmt/GZS6ggDYcyCmeHVb1v/lcyQNs6wgQan82N07ekizHFNxo4S1y3njXs NvDeQLPYC2l4uZlqF1i/FLH/lF/8BIkysKDEEm1n6ftj/mEq03eIfNNBPW1JxSuLI3OY ux8A== X-Gm-Message-State: AC+VfDz3foFQ13B7EqntkcS3c19o6ZkyfhME3k49ompHcK7xn3lUSkBw r2JFxVJzTBCbhzSD89C2uwSWs9yzVTw8wT1gGb4= X-Google-Smtp-Source: ACHHUZ6R9rf5RK7cCTVFoK5ocMUhgEqn8uZcvYWIN7tpoYx7RfGHyHf22adUQXoH0SdHeEbZ/bNY9JkrfpTRGZ4Wcyg= X-Received: by 2002:a5d:4c49:0:b0:307:9702:dfc8 with SMTP id n9-20020a5d4c49000000b003079702dfc8mr16883561wrt.48.1687463285881; Thu, 22 Jun 2023 12:48:05 -0700 (PDT) MIME-Version: 1.0 References: <20230219224334.309-1-tanxeel1.ahmed@gmail.com> <20230610201710.929-1-tanxeel1.ahmed@gmail.com> In-Reply-To: <20230610201710.929-1-tanxeel1.ahmed@gmail.com> From: Tanzeel Ahmed Date: Fri, 23 Jun 2023 00:47:53 +0500 Message-ID: Subject: Re: [PATCH v5] lib/net: add MPLS insert and strip functionality To: Thomas Monjalon , Tanzeel Ahmed , "olivier.matz@6wind.com" Cc: dev@dpdk.org Content-Type: multipart/alternative; boundary="000000000000b9fe6805febd296d" X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org --000000000000b9fe6805febd296d Content-Type: text/plain; charset="UTF-8" Hi, @olivier.matz@6wind.com On Sun, 11 Jun 2023, 1:17 am Tanzeel-inline, wrote: > None of the foundational NICs currently supports MPLS insertion and > stripping, this functionality can help users who rely on MPLS in their > network application. > > Signed-off-by: Tanzeel Ahmed > > --- > > This patch is new version of [PATCH] lib/net: added push MPLS header API. > I have also added the MPLS strip functionality to address the question > asked in last patch. > > > To be honest, I have some doubts about the usefulness of the patch, > > especially the function that strips all the MPLS headers. > > I believe it serves a practical purpose, in scenarios involving tapped > traffic where MPLS headers > have not been stripped. > While some headers in the lib/net folder have well-defined functions, > others are limited to their > structure alone. It would be advantageous to have basic functions > available for all headers. > > > I think the function should only strip the first MPLS header, it is > > symmetric with the previous function, and more flexible. > > You are right, stripping one header is more flexible. > I updated the function to return 1, in case of stripping last MPLS header. > > v5: > * Updated the MPLS strip function to strip one header at a time. > * Added the unit test cases. > > v4: > * Removed extra void cast. > * rte_pktmbuf_append/mtod now return void*. > The memmove result is casted to rte_ether_hdr*. > > v3: > * fixed patch check failure issue > > v2: > * marked experimental > * coding style fixed > * changed rte_memcpy to memcpy > * mpls header marked as const in parameter > * added MPLS stripping functionality > --- > app/test/meson.build | 2 + > app/test/test_mpls.c | 180 +++++++++++++++++++++++++++++++++++++++++++ > lib/net/rte_mpls.h | 106 +++++++++++++++++++++++++ > 3 files changed, 288 insertions(+) > create mode 100644 app/test/test_mpls.c > > diff --git a/app/test/meson.build b/app/test/meson.build > index f34d19e3c3..548349399f 100644 > --- a/app/test/meson.build > +++ b/app/test/meson.build > @@ -95,6 +95,7 @@ test_sources = files( > 'test_meter.c', > 'test_mcslock.c', > 'test_mp_secondary.c', > + 'test_mpls.c', > 'test_per_lcore.c', > 'test_pflock.c', > 'test_pmd_perf.c', > @@ -205,6 +206,7 @@ fast_tests = [ > ['mempool_autotest', false, true], > ['memzone_autotest', false, true], > ['meter_autotest', true, true], > + ['mpls_autotest', false, true], > ['multiprocess_autotest', false, false], > ['per_lcore_autotest', true, true], > ['pflock_autotest', true, true], > diff --git a/app/test/test_mpls.c b/app/test/test_mpls.c > new file mode 100644 > index 0000000000..8ff701f6e0 > --- /dev/null > +++ b/app/test/test_mpls.c > @@ -0,0 +1,180 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2010-2014 Intel Corporation > + */ > + > +#include "test.h" > + > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define MEMPOOL_CACHE_SIZE 32 > +#define MBUF_DATA_SIZE 2048 > +#define NB_MBUF 128 > + > +static int > +test_mpls_fail_push(struct rte_mbuf *m) > +{ > + struct rte_mpls_hdr mpls; > + > + /* create dummy MPLS header */ > + mpls.tag_msb = 1; > + mpls.tag_lsb = 2; > + mpls.bs = 1; > + mpls.tc = 1; > + mpls.ttl = 255; > + > + /* push first MPLS header */ > + if (rte_mpls_push_over_l2(m, &mpls) != 0) > + return 0; > + return -1; > +} > + > +static int > +test_mpls_push(struct rte_mbuf *m) > +{ > + struct rte_mpls_hdr mpls; > + > + /* create dummy MPLS header */ > + mpls.tag_msb = 1; > + mpls.tag_lsb = 2; > + mpls.bs = 1; > + mpls.tc = 1; > + mpls.ttl = 255; > + > + /* push first MPLS header */ > + if (rte_mpls_push_over_l2(m, &mpls) != 0) { > + printf("Failed to insert mpls 1\n"); > + return -1; > + } > + if (rte_pktmbuf_pkt_len(m) != RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN) { > + printf("Bad pkt length after inserting first mpls > header\n"); > + return -1; > + } > + > + /* push second MPLS header*/ > + if (rte_mpls_push_over_l2(m, &mpls) != 0) { > + printf("failed to insert mpls 1\n"); > + return -1; > + } > + if (rte_pktmbuf_pkt_len(m) != RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN * > 2) { > + printf("bad pkt length after inserting second mpls > header\n"); > + return -1; > + } > + return 0; > +} > + > +static int > +test_mpls_fail_strip(struct rte_mbuf *m) > +{ > + /* strip MPLS headers */ > + if (rte_mpls_strip_over_l2(m) != 0) > + return 0; > + return -1; > +} > + > +static int > +test_mpls_strip(struct rte_mbuf *m) > +{ > + /* strip MPLS headers */ > + return rte_mpls_strip_over_l2(m); > +} > + > +static int > +test_mpls(void) > +{ > + int ret = -1; > + struct rte_mempool *pktmbuf_pool = NULL; > + struct rte_mbuf *m = NULL; > + char *data; > + struct rte_ether_hdr eh; > + > + /* create pktmbuf pool */ > + pktmbuf_pool = rte_pktmbuf_pool_create("test_mpls_pool", > + NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE, > + SOCKET_ID_ANY); > + > + if (pktmbuf_pool == NULL) { > + printf("cannot allocate mbuf pool\n"); > + goto err; > + } > + > + /* allocate mbuf from pool */ > + m = rte_pktmbuf_alloc(pktmbuf_pool); > + if (m == NULL) { > + printf("mbuf alloc failed\n"); > + goto err; > + } > + if (rte_pktmbuf_data_len(m) != 0) { > + printf("mbuf alloc bad length\n"); > + goto err; > + } > + > + if (test_mpls_fail_push(m) < 0) { > + printf("test_mpls_fail_push() failed\n"); > + goto err; > + } > + > + if (test_mpls_fail_strip(m) < 0) { > + printf("test_mpls_fail_strip() failed\n"); > + goto err; > + } > + > + /* create a dummy ethernet header */ > + memset(&eh.src_addr, 0, RTE_ETHER_ADDR_LEN); > + memset(&eh.dst_addr, 0, RTE_ETHER_ADDR_LEN); > + eh.ether_type = rte_be_to_cpu_16(RTE_ETHER_TYPE_IPV4); > + > + /* append ethernet header into mbuf */ > + data = rte_pktmbuf_append(m, RTE_ETHER_HDR_LEN); > + if (data == NULL) { > + printf("cannot append data\n"); > + goto err; > + } > + if (rte_pktmbuf_data_len(m) != RTE_ETHER_HDR_LEN) { > + printf("bad pkt data length\n"); > + goto err; > + } > + memcpy(data, &eh, RTE_ETHER_HDR_LEN); > + > + if (test_mpls_push(m) < 0) { > + printf("test_mpls_push() failed\n"); > + goto err; > + } > + > + if (test_mpls_strip(m) < 0) { > + printf("test_mpls_push() failed\n"); > + goto err; > + } > + if (rte_pktmbuf_data_len(m) != RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN) { > + printf("bad pkt data length after stripping first MPLS > header\n"); > + goto err; > + } > + > + if (test_mpls_strip(m) < 0) { > + printf("test_mpls_push() failed\n"); > + goto err; > + } > + if (rte_pktmbuf_data_len(m) != RTE_ETHER_HDR_LEN) { > + printf("bad pkt data length after stripping second MPLS > header\n"); > + goto err; > + } > + ret = 0; > +err: > + if (m) > + rte_pktmbuf_free(m); > + if (pktmbuf_pool) > + rte_mempool_free(pktmbuf_pool); > + return ret; > +} > + > +REGISTER_TEST_COMMAND(mpls_autotest, test_mpls); > diff --git a/lib/net/rte_mpls.h b/lib/net/rte_mpls.h > index 3e8cb90ec3..a2072cdd10 100644 > --- a/lib/net/rte_mpls.h > +++ b/lib/net/rte_mpls.h > @@ -13,6 +13,8 @@ > > #include > #include > +#include > +#include > > #ifdef __cplusplus > extern "C" { > @@ -36,6 +38,110 @@ struct rte_mpls_hdr { > uint8_t ttl; /**< Time to live. */ > } __rte_packed; > > +#define RTE_MPLS_HLEN 4 /**< Length of MPLS header. */ > + > +/** > + * @warning > + * @b EXPERIMENTAL: this API may change without prior notice. > + * > + * Insert MPLS header into the packet. > + * If it's the first MPLS header to be inserted in the packet, > + * - Update the ether type. > + * - Set the MPLS bottom-of-stack bit to 1. > + * > + * @param m > + * The pointer to the mbuf. > + * @param mp > + * The pointer to the MPLS header. > + * @return > + * 0 on success, -1 on error. > + */ > +__rte_experimental > +static inline int > +rte_mpls_push_over_l2(struct rte_mbuf *m, const struct rte_mpls_hdr *mp) > +{ > + struct rte_ether_hdr *oh, *nh; > + struct rte_mpls_hdr *mph; > + > + /* Can't insert header if mbuf is shared */ > + if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1) > + return -EINVAL; > + > + /* Can't insert header if ethernet frame doesn't exist */ > + if (rte_pktmbuf_data_len(m) < RTE_ETHER_HDR_LEN) > + return -EINVAL; > + > + oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); > + nh = (struct rte_ether_hdr *)(void *) > + rte_pktmbuf_prepend(m, sizeof(struct rte_mpls_hdr)); > + if (nh == NULL) > + return -ENOSPC; > + > + memmove(nh, oh, RTE_ETHER_HDR_LEN); > + > + /* Copy the MPLS header after ethernet frame */ > + mph = rte_pktmbuf_mtod_offset(m, struct rte_mpls_hdr *, > + sizeof(struct rte_ether_hdr)); > + memcpy(mph, mp, RTE_MPLS_HLEN); > + > + mph->tag_msb = rte_cpu_to_be_16(mp->tag_msb); > + > + /* If first MPLS header, update ether type and bottom-of-stack bit > */ > + if (nh->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) { > + nh->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS); > + mph->bs = 1; > + } else { > + mph->bs = 0; > + } > + > + return 0; > +} > + > +/** > + * @warning > + * @b EXPERIMENTAL: this API may change without prior notice. > + * > + * Strip MPLS header from the packet without updating the ether type. > + * > + * @param m > + * The pointer to the mbuf. > + * @return > + * 1 if last MPLS header is stripped, > + * 0 on success, > + * -1 on error. > + */ > +__rte_experimental > +static inline int > +rte_mpls_strip_over_l2(struct rte_mbuf *m) > +{ > + struct rte_ether_hdr *eh = rte_pktmbuf_mtod(m, struct > rte_ether_hdr *); > + struct rte_mpls_hdr *mph; > + int result = 0; > + > + /* Can't strip header if mbuf is shared */ > + if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1) > + return -EINVAL; > + > + /* Can't strip header if packet length is smaller */ > + if (rte_pktmbuf_data_len(m) < RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN) > + return -EINVAL; > + > + if (eh->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) > + return -1; > + > + /* Stripping MPLS header */ > + mph = rte_pktmbuf_mtod_offset(m, struct rte_mpls_hdr *, > + sizeof(struct rte_ether_hdr)); > + if (mph->bs & 1) > + result = 1; > + memmove( > + rte_pktmbuf_adj(m, sizeof(struct rte_mpls_hdr)), > + eh, sizeof(struct rte_ether_hdr)); > + eh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); > + > + return result; > +} > + > #ifdef __cplusplus > } > #endif > -- > 2.34.1 > > --000000000000b9fe6805febd296d Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi, @olivier.matz@6wind.com=C2=A0
On Su= n, 11 Jun 2023, 1:17 am Tanzeel-inline, <tanxeel1.ahmed@gmail.com<= /a>> wrote:
None of the foundati= onal NICs currently supports MPLS insertion and
stripping, this functionality can help users who rely on MPLS in their
network application.

Signed-off-by: Tanzeel Ahmed <
tanxeel1.ahmed@gmail.com<= /a>>

---

This patch is new version of [PATCH] lib/net: added push MPLS header API. I have also added the MPLS strip functionality to address the question
asked in last patch.

> To be honest, I have some doubts about the usefulness of the patch, > especially the function that strips all the MPLS headers.

I believe it serves a practical purpose, in scenarios involving tapped traf= fic where MPLS headers
have not been stripped.
While some headers in the lib/net folder have well-defined functions, other= s are limited to their
structure alone. It would be advantageous to have basic functions available= for all headers.

> I think the function should only strip the first MPLS header, it is > symmetric with the previous function, and more flexible.

You are right, stripping one header is more flexible.
I updated the function to return 1, in case of stripping last MPLS header.<= br>
v5:
* Updated the MPLS strip function to strip one header at a time.
* Added the unit test cases.

v4:
* Removed extra void cast.
* rte_pktmbuf_append/mtod now return void*.
=C2=A0 The memmove result is casted to rte_ether_hdr*.

v3:
* fixed patch check failure issue

v2:
* marked experimental
* coding style fixed
* changed rte_memcpy to memcpy
* mpls header marked as const in parameter
* added MPLS stripping functionality
---
=C2=A0app/test/meson.build |=C2=A0 =C2=A02 +
=C2=A0app/test/test_mpls.c | 180 ++++++++++++++++++++++++++++++++++++++++++= +
=C2=A0lib/net/rte_mpls.h=C2=A0 =C2=A0| 106 +++++++++++++++++++++++++
=C2=A03 files changed, 288 insertions(+)
=C2=A0create mode 100644 app/test/test_mpls.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f34d19e3c3..548349399f 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -95,6 +95,7 @@ test_sources =3D files(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'test_meter.c',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'test_mcslock.c',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'test_mp_secondary.c',
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 'test_mpls.c',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'test_per_lcore.c',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'test_pflock.c',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'test_pmd_perf.c',
@@ -205,6 +206,7 @@ fast_tests =3D [
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0['mempool_autotest', false, true]= ,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0['memzone_autotest', false, true]= ,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0['meter_autotest', true, true], +=C2=A0 =C2=A0 =C2=A0 =C2=A0 ['mpls_autotest', false, true],
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0['multiprocess_autotest', false, = false],
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0['per_lcore_autotest', true, true= ],
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0['pflock_autotest', true, true],<= br> diff --git a/app/test/test_mpls.c b/app/test/test_mpls.c
new file mode 100644
index 0000000000..8ff701f6e0
--- /dev/null
+++ b/app/test/test_mpls.c
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation
+ */
+
+#include "test.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_malloc.h>
+#include <rte_ether.h>
+#include <rte_mpls.h>
+
+#define MEMPOOL_CACHE_SIZE=C2=A0 =C2=A0 =C2=A0 32
+#define MBUF_DATA_SIZE=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 2048
+#define NB_MBUF=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0128
+
+static int
+test_mpls_fail_push(struct rte_mbuf *m)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_mpls_hdr mpls;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* create dummy MPLS header */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.tag_msb =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.tag_lsb =3D 2;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0
mpls.bs =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.tc =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.ttl =3D 255;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* push first MPLS header */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_mpls_push_over_l2(m, &mpls) !=3D 0)=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+}
+
+static int
+test_mpls_push(struct rte_mbuf *m)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_mpls_hdr mpls;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* create dummy MPLS header */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.tag_msb =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.tag_lsb =3D 2;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.bs =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.tc =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mpls.ttl =3D 255;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* push first MPLS header */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_mpls_push_over_l2(m, &mpls) !=3D 0)= {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("Failed= to insert mpls 1\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_pkt_len(m) !=3D RTE_ETHER_HDR_L= EN + RTE_MPLS_HLEN) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("Bad pk= t length after inserting first mpls header\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* push second MPLS header*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_mpls_push_over_l2(m, &mpls) !=3D 0)= {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("failed= to insert mpls 1\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_pkt_len(m) !=3D RTE_ETHER_HDR_L= EN + RTE_MPLS_HLEN * 2) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("bad pk= t length after inserting second mpls header\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
+}
+
+static int
+test_mpls_fail_strip(struct rte_mbuf *m)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* strip MPLS headers */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_mpls_strip_over_l2(m) !=3D 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+}
+
+static int
+test_mpls_strip(struct rte_mbuf *m)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* strip MPLS headers */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return rte_mpls_strip_over_l2(m);
+}
+
+static int
+test_mpls(void)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0int ret =3D -1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_mempool *pktmbuf_pool =3D NULL;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_mbuf *m =3D NULL;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0char *data;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_ether_hdr eh;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* create pktmbuf pool */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0pktmbuf_pool =3D rte_pktmbuf_pool_create("= test_mpls_pool",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0SOCKET_ID_ANY);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (pktmbuf_pool =3D=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("cannot= allocate mbuf pool\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 /* allocate mbuf from pool */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0m =3D rte_pktmbuf_alloc(pktmbuf_pool);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (m =3D=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("mbuf a= lloc failed\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_data_len(m) !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("mbuf a= lloc bad length\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (test_mpls_fail_push(m) < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("test_m= pls_fail_push() failed\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (test_mpls_fail_strip(m) < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("test_m= pls_fail_strip() failed\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* create a dummy ethernet header */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0memset(&eh.src_addr, 0, RTE_ETHER_ADDR_LEN)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0memset(&eh.dst_addr, 0, RTE_ETHER_ADDR_LEN)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0eh.ether_type =3D rte_be_to_cpu_16(RTE_ETHER_TY= PE_IPV4);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* append ethernet header into mbuf */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0data =3D rte_pktmbuf_append(m, RTE_ETHER_HDR_LE= N);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (data =3D=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("cannot= append data\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_data_len(m) !=3D RTE_ETHER_HDR_= LEN) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("bad pk= t data length\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy(data, &eh, RTE_ETHER_HDR_LEN);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (test_mpls_push(m) < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("test_m= pls_push() failed\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (test_mpls_strip(m) < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("test_m= pls_push() failed\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_data_len(m) !=3D RTE_ETHER_HDR_= LEN + RTE_MPLS_HLEN) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("bad pk= t data length after stripping first MPLS header\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (test_mpls_strip(m) < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("test_m= pls_push() failed\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_data_len(m) !=3D RTE_ETHER_HDR_= LEN) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("bad pk= t data length after stripping second MPLS header\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0;
+err:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (m)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_pktmbuf_free(m)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (pktmbuf_pool)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_mempool_free(pk= tmbuf_pool);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return ret;
+}
+
+REGISTER_TEST_COMMAND(mpls_autotest, test_mpls);
diff --git a/lib/net/rte_mpls.h b/lib/net/rte_mpls.h
index 3e8cb90ec3..a2072cdd10 100644
--- a/lib/net/rte_mpls.h
+++ b/lib/net/rte_mpls.h
@@ -13,6 +13,8 @@

=C2=A0#include <stdint.h>
=C2=A0#include <rte_byteorder.h>
+#include <rte_ether.h>
+#include <rte_mbuf.h>

=C2=A0#ifdef __cplusplus
=C2=A0extern "C" {
@@ -36,6 +38,110 @@ struct rte_mpls_hdr {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 uint8_t=C2=A0 ttl;=C2=A0 =C2=A0 =C2=A0 =C2=A0/*= *< Time to live. */
=C2=A0} __rte_packed;

+#define RTE_MPLS_HLEN 4 /**< Length of MPLS header. */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Insert MPLS header into the packet.
+ * If it's the first MPLS header to be inserted in the packet,
+ *=C2=A0 - Update the ether type.
+ *=C2=A0 - Set the MPLS bottom-of-stack bit to 1.
+ *
+ * @param m
+ *=C2=A0 =C2=A0The pointer to the mbuf.
+ * @param mp
+ *=C2=A0 =C2=A0The pointer to the MPLS header.
+ * @return
+ *=C2=A0 =C2=A00 on success, -1 on error.
+ */
+__rte_experimental
+static inline int
+rte_mpls_push_over_l2(struct rte_mbuf *m, const struct rte_mpls_hdr *mp) +{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_ether_hdr *oh, *nh;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_mpls_hdr *mph;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Can't insert header if mbuf is shared */=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read= (m) > 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -EINVAL;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Can't insert header if ethernet frame do= esn't exist */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_data_len(m) < RTE_ETHER_HDR_= LEN)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -EINVAL;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0oh =3D rte_pktmbuf_mtod(m, struct rte_ether_hdr= *);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0nh =3D (struct rte_ether_hdr *)(void *)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_pktmbuf_prepend= (m, sizeof(struct rte_mpls_hdr));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (nh =3D=3D NULL)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENOSPC;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0memmove(nh, oh, RTE_ETHER_HDR_LEN);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Copy the MPLS header after ethernet frame */=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mph =3D rte_pktmbuf_mtod_offset(m, struct rte_m= pls_hdr *,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0sizeof(struct rte_ether_hdr));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy(mph, mp, RTE_MPLS_HLEN);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mph->tag_msb =3D rte_cpu_to_be_16(mp->tag= _msb);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* If first MPLS header, update ether type and = bottom-of-stack bit */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (nh->ether_type !=3D rte_cpu_to_be_16(RTE= _ETHER_TYPE_MPLS)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0nh->ether_type = =3D rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0mph->bs =3D 1; +=C2=A0 =C2=A0 =C2=A0 =C2=A0} else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0mph->bs =3D 0; +=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Strip MPLS header from the packet without updating the ether type.
+ *
+ * @param m
+ *=C2=A0 =C2=A0The pointer to the mbuf.
+ * @return
+ *=C2=A0 =C2=A01 if last MPLS header is stripped,
+ *=C2=A0 =C2=A00 on success,
+ *=C2=A0 =C2=A0-1 on error.
+ */
+__rte_experimental
+static inline int
+rte_mpls_strip_over_l2(struct rte_mbuf *m)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_ether_hdr *eh =3D rte_pktmbuf_mtod(m= , struct rte_ether_hdr *);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct rte_mpls_hdr *mph;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0int result =3D 0;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Can't strip header if mbuf is shared */<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read= (m) > 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -EINVAL;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Can't strip header if packet length is s= maller */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (rte_pktmbuf_data_len(m) < RTE_ETHER_HDR_= LEN + RTE_MPLS_HLEN)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -EINVAL;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (eh->ether_type !=3D rte_cpu_to_be_16(RTE= _ETHER_TYPE_MPLS))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Stripping MPLS header */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0mph =3D rte_pktmbuf_mtod_offset(m, struct rte_m= pls_hdr *,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(struct rte_e= ther_hdr));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (mph->bs & 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0result =3D 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0memmove(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_pktmbuf_adj(m, = sizeof(struct rte_mpls_hdr)),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh, sizeof(struct r= te_ether_hdr));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0eh =3D rte_pktmbuf_mtod(m, struct rte_ether_hdr= *);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return result;
+}
+
=C2=A0#ifdef __cplusplus
=C2=A0}
=C2=A0#endif
--
2.34.1

--000000000000b9fe6805febd296d--