From: Tanzeel-inline <tanxeel1.ahmed@gmail.com>
To: olivier.matz@6wind.com, thomas@monjalon.net, tanzeelahmed713@gmail.com
Cc: dev@dpdk.org, Tanzeel-inline <tanxeel1.ahmed@gmail.com>
Subject: [PATCH v5] lib/net: add MPLS insert and strip functionality
Date: Sun, 11 Jun 2023 01:17:10 +0500 [thread overview]
Message-ID: <20230610201710.929-1-tanxeel1.ahmed@gmail.com> (raw)
In-Reply-To: <20230219224334.309-1-tanxeel1.ahmed@gmail.com>
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 <tanxeel1.ahmed@gmail.com>
---
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 <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 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 <stdint.h>
#include <rte_byteorder.h>
+#include <rte_ether.h>
+#include <rte_mbuf.h>
#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
next prev parent reply other threads:[~2023-06-10 20:17 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-19 22:43 [PATCH v2] " Tanzeel-inline
2023-02-24 11:25 ` [PATCH v3] " Tanzeel-inline
2023-02-24 16:29 ` Stephen Hemminger
2023-02-25 13:53 ` [PATCH v4] " Tanzeel-inline
2023-03-09 18:35 ` Tanzeel Ahmed
2023-04-18 13:18 ` Tanzeel Ahmed
2023-05-20 19:53 ` Tanzeel Ahmed
2023-06-05 9:31 ` Olivier Matz
2023-06-10 20:17 ` Tanzeel-inline [this message]
2023-06-22 19:47 ` [PATCH v5] " Tanzeel Ahmed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230610201710.929-1-tanxeel1.ahmed@gmail.com \
--to=tanxeel1.ahmed@gmail.com \
--cc=dev@dpdk.org \
--cc=olivier.matz@6wind.com \
--cc=tanzeelahmed713@gmail.com \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).