From patchwork Sat Jun 10 20:17:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tanzeel Ahmed X-Patchwork-Id: 128498 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 9117D42C73; Sat, 10 Jun 2023 22:17:25 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 1EB97410EF; Sat, 10 Jun 2023 22:17:25 +0200 (CEST) Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) by mails.dpdk.org (Postfix) with ESMTP id 0D83740DD8 for ; Sat, 10 Jun 2023 22:17:23 +0200 (CEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-655fce0f354so2480423b3a.0 for ; Sat, 10 Jun 2023 13:17:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1686428243; x=1689020243; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kSxa0ZgoyozgWrQqCFL1esysvwYLBDPZdRTIbVMCEwM=; b=eu3AnieSuuGxfHDqDHS063nFdFCnb4XVi+s+mv5RPKZlK8twfs1GoNefsW6nTzd3mz Xow0EeSy/lMfsdldkG92vHWI0zyiPQByt+P7IVt+KZ/9oVkR8pDS1tZYhi2MCEJTr6ct BnzG3ukzwKk37Ls5/EtOLtCq6/76oiJI7ANsf2kjspVX9xwjtnGx0pnm8otb1EkM+BNY KiJdVVDrrgLfZzM7oyEI0SmToBTOlZqJJJArH0TKhhMmzeCoFhJ2u5mfXNuUzpDPekFM yuUisyyor2hAoh38S66bTotrf0RLki42wW4IL7W46kdbEhx/g8KrlGRsy5+tnLqCkCe7 Qp0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686428243; x=1689020243; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kSxa0ZgoyozgWrQqCFL1esysvwYLBDPZdRTIbVMCEwM=; b=dLnGmJrceyFtf1ughfDb9DVHBPy5T/UBwF3rnbMnApz05O5hxP86aFF6jv4nxhkQXR zARmOn5wQjuCUY+W0zu16FW8l4MKRtI/7v5qPulStRpjoMPTdio2Btr6sPypV6TwThTJ IrGCVI30k4Yx9rg5kqnm/inDpoFYKI2ojSR/vg18djt57/qRJay4sNLcQSEWMxgW5Fnq RkXEGQquFRQdooBoxOb6sAJxBqpO+puRD33MyuTIW5ltr4vPTlaT9n/4ZyHx1h7hbLmF NaI2xsl5RzgQbKZsXCIZA2BGy59dKfiv4qIMw+6yYq8REvBpBQj4vy7s27uji9ysaeyw S0Tw== X-Gm-Message-State: AC+VfDx6XB/VwQ3kNlbR9C81WpenTD1dQphUecUcAeAMHlYNEQ7E89Y7 L1xIVk71VuX2sjP0QCrjbFs= X-Google-Smtp-Source: ACHHUZ7zrvvWZzO3Z+T3FJJAB4I+QddyLVTyJuumtHHxrNcwFL9tQAZLVfeLlDzwjBgAlvGAfxnhfw== X-Received: by 2002:a05:6a20:9383:b0:110:f65a:13d7 with SMTP id x3-20020a056a20938300b00110f65a13d7mr5869626pzh.9.1686428242753; Sat, 10 Jun 2023 13:17:22 -0700 (PDT) Received: from DESKTOP-KPIMD3E.localdomain ([2407:d000:f:612c:6d4f:2e76:df27:2e72]) by smtp.gmail.com with ESMTPSA id a192-20020a6390c9000000b0051b4a163ccdsm4812369pge.11.2023.06.10.13.17.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 10 Jun 2023 13:17:22 -0700 (PDT) From: Tanzeel-inline To: olivier.matz@6wind.com, thomas@monjalon.net, tanzeelahmed713@gmail.com Cc: dev@dpdk.org, Tanzeel-inline Subject: [PATCH v5] lib/net: add MPLS insert and strip functionality Date: Sun, 11 Jun 2023 01:17:10 +0500 Message-Id: <20230610201710.929-1-tanxeel1.ahmed@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230219224334.309-1-tanxeel1.ahmed@gmail.com> References: <20230219224334.309-1-tanxeel1.ahmed@gmail.com> MIME-Version: 1.0 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 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