<div dir="auto">Hi, <span class="gmail_chip gmail_plusreply" dir="auto"><a href="mailto:olivier.matz@6wind.com" style="color:#15c;text-decoration:underline">@olivier.matz@6wind.com</a></span><span> </span></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, 11 Jun 2023, 1:17 am Tanzeel-inline, <<a href="mailto:tanxeel1.ahmed@gmail.com" target="_blank" rel="noreferrer">tanxeel1.ahmed@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">None of the foundational NICs currently supports MPLS insertion and<br>
stripping, this functionality can help users who rely on MPLS in their<br>
network application.<br>
<br>
Signed-off-by: Tanzeel Ahmed <<a href="mailto:tanxeel1.ahmed@gmail.com" rel="noreferrer noreferrer" target="_blank">tanxeel1.ahmed@gmail.com</a>><br>
<br>
---<br>
<br>
This patch is new version of [PATCH] lib/net: added push MPLS header API.<br>
I have also added the MPLS strip functionality to address the question<br>
asked in last patch.<br>
<br>
> To be honest, I have some doubts about the usefulness of the patch,<br>
> especially the function that strips all the MPLS headers.<br>
<br>
I believe it serves a practical purpose, in scenarios involving tapped traffic where MPLS headers<br>
have not been stripped.<br>
While some headers in the lib/net folder have well-defined functions, others are limited to their<br>
structure alone. It would be advantageous to have basic functions available for all headers.<br>
<br>
> I think the function should only strip the first MPLS header, it is<br>
> symmetric with the previous function, and more flexible.<br>
<br>
You are right, stripping one header is more flexible.<br>
I updated the function to return 1, in case of stripping last MPLS header.<br>
<br>
v5:<br>
* Updated the MPLS strip function to strip one header at a time.<br>
* Added the unit test cases.<br>
<br>
v4:<br>
* Removed extra void cast.<br>
* rte_pktmbuf_append/mtod now return void*.<br>
  The memmove result is casted to rte_ether_hdr*.<br>
<br>
v3:<br>
* fixed patch check failure issue<br>
<br>
v2:<br>
* marked experimental<br>
* coding style fixed<br>
* changed rte_memcpy to memcpy<br>
* mpls header marked as const in parameter<br>
* added MPLS stripping functionality<br>
---<br>
 app/test/meson.build |   2 +<br>
 app/test/test_mpls.c | 180 +++++++++++++++++++++++++++++++++++++++++++<br>
 lib/net/rte_mpls.h   | 106 +++++++++++++++++++++++++<br>
 3 files changed, 288 insertions(+)<br>
 create mode 100644 app/test/test_mpls.c<br>
<br>
diff --git a/app/test/meson.build b/app/test/meson.build<br>
index f34d19e3c3..548349399f 100644<br>
--- a/app/test/meson.build<br>
+++ b/app/test/meson.build<br>
@@ -95,6 +95,7 @@ test_sources = files(<br>
         'test_meter.c',<br>
         'test_mcslock.c',<br>
         'test_mp_secondary.c',<br>
+        'test_mpls.c',<br>
         'test_per_lcore.c',<br>
         'test_pflock.c',<br>
         'test_pmd_perf.c',<br>
@@ -205,6 +206,7 @@ fast_tests = [<br>
         ['mempool_autotest', false, true],<br>
         ['memzone_autotest', false, true],<br>
         ['meter_autotest', true, true],<br>
+        ['mpls_autotest', false, true],<br>
         ['multiprocess_autotest', false, false],<br>
         ['per_lcore_autotest', true, true],<br>
         ['pflock_autotest', true, true],<br>
diff --git a/app/test/test_mpls.c b/app/test/test_mpls.c<br>
new file mode 100644<br>
index 0000000000..8ff701f6e0<br>
--- /dev/null<br>
+++ b/app/test/test_mpls.c<br>
@@ -0,0 +1,180 @@<br>
+/* SPDX-License-Identifier: BSD-3-Clause<br>
+ * Copyright(c) 2010-2014 Intel Corporation<br>
+ */<br>
+<br>
+#include "test.h"<br>
+<br>
+#include <string.h><br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <stdint.h><br>
+<br>
+#include <rte_common.h><br>
+#include <rte_memory.h><br>
+#include <rte_memcpy.h><br>
+#include <rte_mbuf.h><br>
+#include <rte_malloc.h><br>
+#include <rte_ether.h><br>
+#include <rte_mpls.h><br>
+<br>
+#define MEMPOOL_CACHE_SIZE      32<br>
+#define MBUF_DATA_SIZE          2048<br>
+#define NB_MBUF                 128<br>
+<br>
+static int<br>
+test_mpls_fail_push(struct rte_mbuf *m)<br>
+{<br>
+       struct rte_mpls_hdr mpls;<br>
+<br>
+       /* create dummy MPLS header */<br>
+       mpls.tag_msb = 1;<br>
+       mpls.tag_lsb = 2;<br>
+       <a href="http://mpls.bs" rel="noreferrer noreferrer noreferrer" target="_blank">mpls.bs</a> = 1;<br>
+       <a href="http://mpls.tc" rel="noreferrer noreferrer noreferrer" target="_blank">mpls.tc</a> = 1;<br>
+       mpls.ttl = 255;<br>
+<br>
+       /* push first MPLS header */<br>
+       if (rte_mpls_push_over_l2(m, &mpls) != 0)<br>
+               return 0;<br>
+       return -1;<br>
+}<br>
+<br>
+static int<br>
+test_mpls_push(struct rte_mbuf *m)<br>
+{<br>
+       struct rte_mpls_hdr mpls;<br>
+<br>
+       /* create dummy MPLS header */<br>
+       mpls.tag_msb = 1;<br>
+       mpls.tag_lsb = 2;<br>
+       <a href="http://mpls.bs" rel="noreferrer noreferrer noreferrer" target="_blank">mpls.bs</a> = 1;<br>
+       <a href="http://mpls.tc" rel="noreferrer noreferrer noreferrer" target="_blank">mpls.tc</a> = 1;<br>
+       mpls.ttl = 255;<br>
+<br>
+       /* push first MPLS header */<br>
+       if (rte_mpls_push_over_l2(m, &mpls) != 0) {<br>
+               printf("Failed to insert mpls 1\n");<br>
+               return -1;<br>
+       }<br>
+       if (rte_pktmbuf_pkt_len(m) != RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN) {<br>
+               printf("Bad pkt length after inserting first mpls header\n");<br>
+               return -1;<br>
+       }<br>
+<br>
+       /* push second MPLS header*/<br>
+       if (rte_mpls_push_over_l2(m, &mpls) != 0) {<br>
+               printf("failed to insert mpls 1\n");<br>
+               return -1;<br>
+       }<br>
+       if (rte_pktmbuf_pkt_len(m) != RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN * 2) {<br>
+               printf("bad pkt length after inserting second mpls header\n");<br>
+               return -1;<br>
+       }<br>
+       return 0;<br>
+}<br>
+<br>
+static int<br>
+test_mpls_fail_strip(struct rte_mbuf *m)<br>
+{<br>
+       /* strip MPLS headers */<br>
+       if (rte_mpls_strip_over_l2(m) != 0)<br>
+               return 0;<br>
+       return -1;<br>
+}<br>
+<br>
+static int<br>
+test_mpls_strip(struct rte_mbuf *m)<br>
+{<br>
+       /* strip MPLS headers */<br>
+       return rte_mpls_strip_over_l2(m);<br>
+}<br>
+<br>
+static int<br>
+test_mpls(void)<br>
+{<br>
+       int ret = -1;<br>
+       struct rte_mempool *pktmbuf_pool = NULL;<br>
+       struct rte_mbuf *m = NULL;<br>
+       char *data;<br>
+       struct rte_ether_hdr eh;<br>
+<br>
+       /* create pktmbuf pool */<br>
+       pktmbuf_pool = rte_pktmbuf_pool_create("test_mpls_pool",<br>
+                       NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE,<br>
+                       SOCKET_ID_ANY);<br>
+<br>
+       if (pktmbuf_pool == NULL) {<br>
+               printf("cannot allocate mbuf pool\n");<br>
+               goto err;<br>
+       }<br>
+<br>
+    /* allocate mbuf from pool */<br>
+       m = rte_pktmbuf_alloc(pktmbuf_pool);<br>
+       if (m == NULL) {<br>
+               printf("mbuf alloc failed\n");<br>
+               goto err;<br>
+       }<br>
+       if (rte_pktmbuf_data_len(m) != 0) {<br>
+               printf("mbuf alloc bad length\n");<br>
+               goto err;<br>
+       }<br>
+<br>
+       if (test_mpls_fail_push(m) < 0) {<br>
+               printf("test_mpls_fail_push() failed\n");<br>
+               goto err;<br>
+       }<br>
+<br>
+       if (test_mpls_fail_strip(m) < 0) {<br>
+               printf("test_mpls_fail_strip() failed\n");<br>
+               goto err;<br>
+       }<br>
+<br>
+       /* create a dummy ethernet header */<br>
+       memset(&eh.src_addr, 0, RTE_ETHER_ADDR_LEN);<br>
+       memset(&eh.dst_addr, 0, RTE_ETHER_ADDR_LEN);<br>
+       eh.ether_type = rte_be_to_cpu_16(RTE_ETHER_TYPE_IPV4);<br>
+<br>
+       /* append ethernet header into mbuf */<br>
+       data = rte_pktmbuf_append(m, RTE_ETHER_HDR_LEN);<br>
+       if (data == NULL) {<br>
+               printf("cannot append data\n");<br>
+               goto err;<br>
+       }<br>
+       if (rte_pktmbuf_data_len(m) != RTE_ETHER_HDR_LEN) {<br>
+               printf("bad pkt data length\n");<br>
+               goto err;<br>
+       }<br>
+       memcpy(data, &eh, RTE_ETHER_HDR_LEN);<br>
+<br>
+       if (test_mpls_push(m) < 0) {<br>
+               printf("test_mpls_push() failed\n");<br>
+               goto err;<br>
+       }<br>
+<br>
+       if (test_mpls_strip(m) < 0) {<br>
+               printf("test_mpls_push() failed\n");<br>
+               goto err;<br>
+       }<br>
+       if (rte_pktmbuf_data_len(m) != RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN) {<br>
+               printf("bad pkt data length after stripping first MPLS header\n");<br>
+               goto err;<br>
+       }<br>
+<br>
+       if (test_mpls_strip(m) < 0) {<br>
+               printf("test_mpls_push() failed\n");<br>
+               goto err;<br>
+       }<br>
+       if (rte_pktmbuf_data_len(m) != RTE_ETHER_HDR_LEN) {<br>
+               printf("bad pkt data length after stripping second MPLS header\n");<br>
+               goto err;<br>
+       }<br>
+       ret = 0;<br>
+err:<br>
+       if (m)<br>
+               rte_pktmbuf_free(m);<br>
+       if (pktmbuf_pool)<br>
+               rte_mempool_free(pktmbuf_pool);<br>
+       return ret;<br>
+}<br>
+<br>
+REGISTER_TEST_COMMAND(mpls_autotest, test_mpls);<br>
diff --git a/lib/net/rte_mpls.h b/lib/net/rte_mpls.h<br>
index 3e8cb90ec3..a2072cdd10 100644<br>
--- a/lib/net/rte_mpls.h<br>
+++ b/lib/net/rte_mpls.h<br>
@@ -13,6 +13,8 @@<br>
<br>
 #include <stdint.h><br>
 #include <rte_byteorder.h><br>
+#include <rte_ether.h><br>
+#include <rte_mbuf.h><br>
<br>
 #ifdef __cplusplus<br>
 extern "C" {<br>
@@ -36,6 +38,110 @@ struct rte_mpls_hdr {<br>
        uint8_t  ttl;       /**< Time to live. */<br>
 } __rte_packed;<br>
<br>
+#define RTE_MPLS_HLEN 4 /**< Length of MPLS header. */<br>
+<br>
+/**<br>
+ * @warning<br>
+ * @b EXPERIMENTAL: this API may change without prior notice.<br>
+ *<br>
+ * Insert MPLS header into the packet.<br>
+ * If it's the first MPLS header to be inserted in the packet,<br>
+ *  - Update the ether type.<br>
+ *  - Set the MPLS bottom-of-stack bit to 1.<br>
+ *<br>
+ * @param m<br>
+ *   The pointer to the mbuf.<br>
+ * @param mp<br>
+ *   The pointer to the MPLS header.<br>
+ * @return<br>
+ *   0 on success, -1 on error.<br>
+ */<br>
+__rte_experimental<br>
+static inline int<br>
+rte_mpls_push_over_l2(struct rte_mbuf *m, const struct rte_mpls_hdr *mp)<br>
+{<br>
+       struct rte_ether_hdr *oh, *nh;<br>
+       struct rte_mpls_hdr *mph;<br>
+<br>
+       /* Can't insert header if mbuf is shared */<br>
+       if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)<br>
+               return -EINVAL;<br>
+<br>
+       /* Can't insert header if ethernet frame doesn't exist */<br>
+       if (rte_pktmbuf_data_len(m) < RTE_ETHER_HDR_LEN)<br>
+               return -EINVAL;<br>
+<br>
+       oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);<br>
+       nh = (struct rte_ether_hdr *)(void *)<br>
+               rte_pktmbuf_prepend(m, sizeof(struct rte_mpls_hdr));<br>
+       if (nh == NULL)<br>
+               return -ENOSPC;<br>
+<br>
+       memmove(nh, oh, RTE_ETHER_HDR_LEN);<br>
+<br>
+       /* Copy the MPLS header after ethernet frame */<br>
+       mph = rte_pktmbuf_mtod_offset(m, struct rte_mpls_hdr *,<br>
+                       sizeof(struct rte_ether_hdr));<br>
+       memcpy(mph, mp, RTE_MPLS_HLEN);<br>
+<br>
+       mph->tag_msb = rte_cpu_to_be_16(mp->tag_msb);<br>
+<br>
+       /* If first MPLS header, update ether type and bottom-of-stack bit */<br>
+       if (nh->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) {<br>
+               nh->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS);<br>
+               mph->bs = 1;<br>
+       } else {<br>
+               mph->bs = 0;<br>
+       }<br>
+<br>
+       return 0;<br>
+}<br>
+<br>
+/**<br>
+ * @warning<br>
+ * @b EXPERIMENTAL: this API may change without prior notice.<br>
+ *<br>
+ * Strip MPLS header from the packet without updating the ether type.<br>
+ *<br>
+ * @param m<br>
+ *   The pointer to the mbuf.<br>
+ * @return<br>
+ *   1 if last MPLS header is stripped,<br>
+ *   0 on success,<br>
+ *   -1 on error.<br>
+ */<br>
+__rte_experimental<br>
+static inline int<br>
+rte_mpls_strip_over_l2(struct rte_mbuf *m)<br>
+{<br>
+       struct rte_ether_hdr *eh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);<br>
+       struct rte_mpls_hdr *mph;<br>
+       int result = 0;<br>
+<br>
+       /* Can't strip header if mbuf is shared */<br>
+       if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)<br>
+               return -EINVAL;<br>
+<br>
+       /* Can't strip header if packet length is smaller */<br>
+       if (rte_pktmbuf_data_len(m) < RTE_ETHER_HDR_LEN + RTE_MPLS_HLEN)<br>
+               return -EINVAL;<br>
+<br>
+       if (eh->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS))<br>
+               return -1;<br>
+<br>
+       /* Stripping MPLS header */<br>
+       mph = rte_pktmbuf_mtod_offset(m, struct rte_mpls_hdr *,<br>
+               sizeof(struct rte_ether_hdr));<br>
+       if (mph->bs & 1)<br>
+               result = 1;<br>
+       memmove(<br>
+               rte_pktmbuf_adj(m, sizeof(struct rte_mpls_hdr)),<br>
+               eh, sizeof(struct rte_ether_hdr));<br>
+       eh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);<br>
+<br>
+       return result;<br>
+}<br>
+<br>
 #ifdef __cplusplus<br>
 }<br>
 #endif<br>
-- <br>
2.34.1<br>
<br>
</blockquote></div>