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 A143B45B69; Fri, 18 Oct 2024 16:06:42 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 835E540687; Fri, 18 Oct 2024 16:06:35 +0200 (CEST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by mails.dpdk.org (Postfix) with ESMTP id 4363640669 for ; Fri, 18 Oct 2024 16:06:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1729260390; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RWhsgXczD9YL8NC7fRxW5T+9/lp38pnb596Xzg7eZP8=; b=fkGpUWbiMYRD70scogjYEdR76uu7StWMYSWhjWLAsnH84rqRPT2la/YA9rqtgdv/G1lTZy GqcGAH4ftsvK70MbJLG6JhjbQtnkJNSJbePx3FzkRcu5lv40IohK47yakvi03IFAGPJ9fI 7L3ZFUGhRgyiNB2ryVRjDXseolNNXXU= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-7-RipESsDsNeG_6K_S9lhAeg-1; Fri, 18 Oct 2024 10:06:29 -0400 X-MC-Unique: RipESsDsNeG_6K_S9lhAeg-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 94E881944A8B; Fri, 18 Oct 2024 14:06:10 +0000 (UTC) Received: from ringo.redhat.com (unknown [10.39.208.23]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2FC6919560A2; Fri, 18 Oct 2024 14:06:08 +0000 (UTC) From: Robin Jarry To: dev@dpdk.org, Thomas Monjalon Subject: [PATCH dpdk v5 03/17] net: add IPv6 address structure and utils Date: Fri, 18 Oct 2024 16:05:39 +0200 Message-ID: <20241018140553.79789-4-rjarry@redhat.com> In-Reply-To: <20241018140553.79789-1-rjarry@redhat.com> References: <20240821162516.610624-17-rjarry@redhat.com> <20241018140553.79789-1-rjarry@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true 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 There is currently no structure defined for IPv6 addresses. Introduce one that is simply a uint8_t array of 16 elements. The idea is to ensure this structure alignment is 1 so that it can be mapped directly on unaligned packet memory. Add utility functions and macros that use the newly added rte_ipv6_addr structure. Add basic unit tests to ensure everything works as expected. These functions will be used in the next commits to replace private and/or duplicated functions. Signed-off-by: Robin Jarry --- MAINTAINERS | 1 + app/test/meson.build | 1 + app/test/test_net_ip6.c | 111 +++++++++++++ doc/guides/rel_notes/release_24_11.rst | 5 + lib/net/rte_ip6.h | 213 +++++++++++++++++++++++++ 5 files changed, 331 insertions(+) create mode 100644 app/test/test_net_ip6.c diff --git a/MAINTAINERS b/MAINTAINERS index f09cda04c87d..b94bf57a2034 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1526,6 +1526,7 @@ F: lib/net/ F: app/test/test_cksum.c F: app/test/test_cksum_perf.c F: app/test/test_net_ether.c +F: app/test/test_net_ip6.c Packet CRC M: Jasvinder Singh diff --git a/app/test/meson.build b/app/test/meson.build index fe248b786c8e..0f7e11969a44 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -131,6 +131,7 @@ source_file_deps = { 'test_metrics.c': ['metrics'], 'test_mp_secondary.c': ['hash'], 'test_net_ether.c': ['net'], + 'test_net_ip6.c': ['net'], 'test_pcapng.c': ['ethdev', 'net', 'pcapng', 'bus_vdev'], 'test_pdcp.c': ['eventdev', 'pdcp', 'net', 'timer', 'security'], 'test_pdump.c': ['pdump'] + sample_packet_forward_deps, diff --git a/app/test/test_net_ip6.c b/app/test/test_net_ip6.c new file mode 100644 index 000000000000..dcc80c1309d1 --- /dev/null +++ b/app/test/test_net_ip6.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2024 Robin Jarry + */ + +#include + +#include "test.h" + +static const struct rte_ipv6_addr mask_full = RTE_IPV6_MASK_FULL; +static const struct rte_ipv6_addr zero_addr = RTE_IPV6_ADDR_UNSPEC; + +static int +test_ipv6_addr_mask(void) +{ + const struct rte_ipv6_addr masked_3 = RTE_IPV6(0xe000, 0, 0, 0, 0, 0, 0, 0); + const struct rte_ipv6_addr masked_42 = RTE_IPV6(0xffff, 0xffff, 0xffc0, 0, 0, 0, 0, 0); + const struct rte_ipv6_addr masked_85 = + RTE_IPV6(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xf800, 0, 0); + const struct rte_ipv6_addr masked_127 = + RTE_IPV6(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfffe); + struct rte_ipv6_addr ip; + + ip = mask_full; + rte_ipv6_addr_mask(&ip, 0); + TEST_ASSERT(rte_ipv6_addr_eq(&ip, &zero_addr), ""); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&zero_addr), 0, ""); + + ip = mask_full; + rte_ipv6_addr_mask(&ip, 3); + TEST_ASSERT(rte_ipv6_addr_eq(&ip, &masked_3), ""); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&masked_3), 3, ""); + + ip = mask_full; + rte_ipv6_addr_mask(&ip, 42); + TEST_ASSERT(rte_ipv6_addr_eq(&ip, &masked_42), ""); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&masked_42), 42, ""); + + ip = mask_full; + rte_ipv6_addr_mask(&ip, 85); + TEST_ASSERT(rte_ipv6_addr_eq(&ip, &masked_85), ""); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&masked_85), 85, ""); + + ip = mask_full; + rte_ipv6_addr_mask(&ip, 127); + TEST_ASSERT(rte_ipv6_addr_eq(&ip, &masked_127), ""); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&masked_127), 127, ""); + + ip = mask_full; + rte_ipv6_addr_mask(&ip, 128); + TEST_ASSERT(rte_ipv6_addr_eq(&ip, &mask_full), ""); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&mask_full), 128, ""); + + const struct rte_ipv6_addr mask_holed = + RTE_IPV6(0xffff, 0xffff, 0xffff, 0xefff, 0xffff, 0xffff, 0xffff, 0xffff); + TEST_ASSERT_EQUAL(rte_ipv6_mask_depth(&mask_holed), 51, ""); + + return TEST_SUCCESS; +} + +static int +test_ipv6_addr_eq_prefix(void) +{ + const struct rte_ipv6_addr ip1 = + RTE_IPV6(0x2a01, 0xcb00, 0x0254, 0x3300, 0x1b9f, 0x8071, 0x67cd, 0xbf20); + const struct rte_ipv6_addr ip2 = + RTE_IPV6(0x2a01, 0xcb00, 0x0254, 0x3300, 0x6239, 0xe1f4, 0x7a0b, 0x2371); + const struct rte_ipv6_addr ip3 = + RTE_IPV6(0xfd10, 0x0039, 0x0208, 0x0001, 0x0000, 0x0000, 0x0000, 0x1008); + + TEST_ASSERT(rte_ipv6_addr_eq_prefix(&ip1, &ip2, 1), ""); + TEST_ASSERT(rte_ipv6_addr_eq_prefix(&ip1, &ip2, 37), ""); + TEST_ASSERT(rte_ipv6_addr_eq_prefix(&ip1, &ip2, 64), ""); + TEST_ASSERT(!rte_ipv6_addr_eq_prefix(&ip1, &ip2, 112), ""); + TEST_ASSERT(rte_ipv6_addr_eq_prefix(&ip1, &ip3, 0), ""); + TEST_ASSERT(!rte_ipv6_addr_eq_prefix(&ip1, &ip3, 13), ""); + + return TEST_SUCCESS; +} + +static int +test_ipv6_addr_kind(void) +{ + TEST_ASSERT(rte_ipv6_addr_is_unspec(&zero_addr), ""); + + const struct rte_ipv6_addr ucast = + RTE_IPV6(0x2a01, 0xcb00, 0x0254, 0x3300, 0x6239, 0xe1f4, 0x7a0b, 0x2371); + TEST_ASSERT(!rte_ipv6_addr_is_unspec(&ucast), ""); + + const struct rte_ipv6_addr mcast = RTE_IPV6(0xff01, 0, 0, 0, 0, 0, 0, 1); + TEST_ASSERT(!rte_ipv6_addr_is_unspec(&mcast), ""); + + const struct rte_ipv6_addr lo = RTE_IPV6_ADDR_LOOPBACK; + TEST_ASSERT(!rte_ipv6_addr_is_unspec(&lo), ""); + + const struct rte_ipv6_addr local = + RTE_IPV6(0xfe80, 0, 0, 0, 0x5a84, 0xc52c, 0x6aef, 0x4639); + TEST_ASSERT(!rte_ipv6_addr_is_unspec(&local), ""); + + return TEST_SUCCESS; +} + +static int +test_net_ipv6(void) +{ + TEST_ASSERT_SUCCESS(test_ipv6_addr_mask(), ""); + TEST_ASSERT_SUCCESS(test_ipv6_addr_eq_prefix(), ""); + TEST_ASSERT_SUCCESS(test_ipv6_addr_kind(), ""); + return TEST_SUCCESS; +} + +REGISTER_FAST_TEST(net_ipv6_autotest, true, true, test_net_ipv6); diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst index d2301461ce35..e68676caf029 100644 --- a/doc/guides/rel_notes/release_24_11.rst +++ b/doc/guides/rel_notes/release_24_11.rst @@ -238,6 +238,11 @@ New Features Added ability for node to advertise and update multiple xstat counters, that can be retrieved using ``rte_graph_cluster_stats_get``. +* **Added IPv6 address structure and related utilities.** + + A new IPv6 address structure is now available in ``rte_ip6.h``. + It comes with a set of helper functions and macros. + Removed Items ------------- diff --git a/lib/net/rte_ip6.h b/lib/net/rte_ip6.h index fe00c53ae9f3..2420339d1598 100644 --- a/lib/net/rte_ip6.h +++ b/lib/net/rte_ip6.h @@ -16,6 +16,7 @@ */ #include +#include #ifdef RTE_EXEC_ENV_WINDOWS #include @@ -35,6 +36,218 @@ extern "C" { #endif +/** + * Maximum IPv6 address size in bytes. + */ +#define RTE_IPV6_ADDR_SIZE 16 + +/** + * Maximum IPv6 address size in bits. + */ +#define RTE_IPV6_MAX_DEPTH (RTE_IPV6_ADDR_SIZE * CHAR_BIT) + +/** + * IPv6 Address + */ +struct rte_ipv6_addr { + uint8_t a[RTE_IPV6_ADDR_SIZE]; +}; + +/** + * Check if two IPv6 Addresses are equal. + * + * @param a + * The first address. + * @param b + * The second address. + * @return + * true if both addresses are identical. + */ +static inline bool +rte_ipv6_addr_eq(const struct rte_ipv6_addr *a, const struct rte_ipv6_addr *b) +{ + return memcmp(a, b, sizeof(*a)) == 0; +} + +/** + * Mask an IPv6 address using the specified depth. + * + * Leave untouched one bit per unit in the depth variable and set the rest to 0. + * + * @param[in] ip + * The address to mask. + * @param[out] depth + * All bits starting from this bit number will be set to zero. + */ +static inline void +rte_ipv6_addr_mask(struct rte_ipv6_addr *ip, uint8_t depth) +{ + if (depth < RTE_IPV6_MAX_DEPTH) { + uint8_t d = depth / 8; + uint8_t mask = ~(UINT8_MAX >> (depth % 8)); + ip->a[d] &= mask; + d++; + memset(&ip->a[d], 0, sizeof(*ip) - d); + } +} + +/** + * Check if two IPv6 addresses belong to the same network prefix. + * + * @param a + * The first address or network. + * @param b + * The second address or network. + * @param depth + * The network prefix length. + * @return + * @c true if the first @p depth bits of both addresses are identical. + */ +static inline bool +rte_ipv6_addr_eq_prefix(const struct rte_ipv6_addr *a, const struct rte_ipv6_addr *b, uint8_t depth) +{ + if (depth < RTE_IPV6_MAX_DEPTH) { + uint8_t d = depth / 8; + uint8_t mask = ~(UINT8_MAX >> (depth % 8)); + + if ((a->a[d] ^ b->a[d]) & mask) + return false; + + return memcmp(a, b, d) == 0; + } + return rte_ipv6_addr_eq(a, b); +} + +/** + * Get the depth of a given IPv6 address mask. + * + * @param mask + * The address mask. + * @return + * The number of consecutive bits set to 1 starting from the beginning of the mask. + */ +static inline uint8_t +rte_ipv6_mask_depth(const struct rte_ipv6_addr *mask) +{ + uint8_t depth = 0; + + for (unsigned int i = 0; i < RTE_DIM(mask->a); i++) { + uint8_t m = mask->a[i]; + if (m == 0xff) { + depth += 8; + } else { + while (m & 0x80) { + m <<= 1; + depth++; + } + break; + } + } + + return depth; +} + +/** + * Split a literal 16 bit unsigned integer into two bytes separated by a comma + * according to the platform endianness. + * + * @param x + * A @c uint16_t literal value. + * @return + * Two @c uint8_t literals separated by a coma. + */ +#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN +#define RTE_IPV6_U16_SPLIT(x) \ + (uint8_t)((uint16_t)(x) & UINT16_C(0xff)), \ + (uint8_t)(((uint16_t)(x) >> 8) & UINT16_C(0xff)) +#else +#define RTE_IPV6_U16_SPLIT(x) \ + (uint8_t)(((uint16_t)(x) >> 8) & UINT16_C(0xff)), \ + (uint8_t)((uint16_t)(x) & UINT16_C(0xff)) +#endif + +/** + * Shorthand to define a literal IPv6 address based on 16bit unsigned integers. + * + * @param a,b,c,d,e,f + * @c uint8_t literals that will be passed to RTE_IPV6_U16_SPLIT(x). + * @return + * A literal @ref rte_ipv6_addr value suitable for static initialization. + */ +#define RTE_IPV6(a, b, c, d, e, f, g, h) \ + {{ \ + RTE_IPV6_U16_SPLIT(a), \ + RTE_IPV6_U16_SPLIT(b), \ + RTE_IPV6_U16_SPLIT(c), \ + RTE_IPV6_U16_SPLIT(d), \ + RTE_IPV6_U16_SPLIT(e), \ + RTE_IPV6_U16_SPLIT(f), \ + RTE_IPV6_U16_SPLIT(g), \ + RTE_IPV6_U16_SPLIT(h) \ + }} + +/** + * printf() format element for @ref rte_ipv6_addr structures. + * To be used along with RTE_IPV6_ADDR_SPLIT(ip). + */ +#define RTE_IPV6_ADDR_FMT \ + "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x" + +/** + * For use with #RTE_IPV6_ADDR_FMT. E.g.: + * + * @code + * printf(RTE_IPV6_ADDR_FMT "\n", RTE_IPV6_ADDR_SPLIT(&ip)); + * @endcode + * + * @param ip + * A struct rte_ipv6_addr pointer. + * @return + * A set of 16 @c uint8_t values separated by comas for use in printf(). + */ +#define RTE_IPV6_ADDR_SPLIT(ip) \ + ((uint8_t)(ip)->a[0]), \ + ((uint8_t)(ip)->a[1]), \ + ((uint8_t)(ip)->a[2]), \ + ((uint8_t)(ip)->a[3]), \ + ((uint8_t)(ip)->a[4]), \ + ((uint8_t)(ip)->a[5]), \ + ((uint8_t)(ip)->a[6]), \ + ((uint8_t)(ip)->a[7]), \ + ((uint8_t)(ip)->a[8]), \ + ((uint8_t)(ip)->a[9]), \ + ((uint8_t)(ip)->a[10]), \ + ((uint8_t)(ip)->a[11]), \ + ((uint8_t)(ip)->a[12]), \ + ((uint8_t)(ip)->a[13]), \ + ((uint8_t)(ip)->a[14]), \ + ((uint8_t)(ip)->a[15]) + +/** Full IPv6 mask. NB: this is not a valid/routable IPv6 address. */ +#define RTE_IPV6_MASK_FULL \ + RTE_IPV6(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff) + +/** Unspecified IPv6 address as defined in RFC 4291, section 2.5.2. */ +#define RTE_IPV6_ADDR_UNSPEC RTE_IPV6(0, 0, 0, 0, 0, 0, 0, 0) + +/** + * Check if an IPv6 address is unspecified as defined in RFC 4291, section 2.5.2. + * + * @param ip + * The address to check. + * @return + * @c true if the address is the unspecified address (all zeroes). + */ +static inline bool +rte_ipv6_addr_is_unspec(const struct rte_ipv6_addr *ip) +{ + const struct rte_ipv6_addr unspec = RTE_IPV6_ADDR_UNSPEC; + return rte_ipv6_addr_eq(ip, &unspec); +} + +/** Loopback IPv6 address as defined in RFC 4291, section 2.5.3. */ +#define RTE_IPV6_ADDR_LOOPBACK RTE_IPV6(0, 0, 0, 0, 0, 0, 0, 1) + /** * IPv6 Header */ -- 2.47.0