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 EE32845B09; Thu, 10 Oct 2024 21:42:33 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 612C94068A; Thu, 10 Oct 2024 21:42:24 +0200 (CEST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mails.dpdk.org (Postfix) with ESMTP id F3BB040A6E for ; Thu, 10 Oct 2024 21:42:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1728589341; 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=AFZC6V8WZBF/Pm3Xzi7L48O8BqiB73I4KPigk3C52pM=; b=T+KoxgwcD/tEdhPJxBTcaGCzEtW+vWLcJWyRh5BWXEQcO2p+aTJkHrpAQPky0vpHtc/nPy ayOJ1hw8VlEf2awysBGdK53W62phaPuWysIvzgqGMPwlNKHlXMSluaDvmL8DBsf0riDt5b p61Evh7qAMCJwG/TVGcYPypQnXtbtAw= Received: from mx-prod-mc-02.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-604-A4MCHDcyMUuATOZ9cbc8tA-1; Thu, 10 Oct 2024 15:42:18 -0400 X-MC-Unique: A4MCHDcyMUuATOZ9cbc8tA-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 371931955EE8; Thu, 10 Oct 2024 19:42:17 +0000 (UTC) Received: from ringo.redhat.com (unknown [10.39.208.7]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 78BE819560AA; Thu, 10 Oct 2024 19:42:15 +0000 (UTC) From: Robin Jarry To: dev@dpdk.org, Thomas Monjalon Subject: [PATCH dpdk v3 04/17] net: add ipv6 address utilities Date: Thu, 10 Oct 2024 21:41:34 +0200 Message-ID: <20241010194148.1877659-5-rjarry@redhat.com> In-Reply-To: <20241010194148.1877659-1-rjarry@redhat.com> References: <20240821162516.610624-17-rjarry@redhat.com> <20241010194148.1877659-1-rjarry@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 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 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 --- Notes: v3: - rename test_net_ipv6.c -> test_net_ip6.c to match rte_ip6.h - remove rte_ipv6_addr_cpy() - rename RTE_IPV6_ADDR(...) to RTE_IPV6() to match RTE_IPV4() - add RTE_IPV6_ADDR_FMT and RTE_IPV6_ADDR_SPLIT() - add RTE_IPV6_ADDR_LOOPBACK and RTE_IPV6_MASK_FULL MAINTAINERS | 1 + app/test/meson.build | 1 + app/test/test_net_ip6.c | 111 +++++++++++++++++++++++++++ lib/net/rte_ip6.h | 165 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 app/test/test_net_ip6.c diff --git a/MAINTAINERS b/MAINTAINERS index 812463fe9f90..0b09df757d6b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1519,6 +1519,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 e29258e6ec05..c297dcdb13e0 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -130,6 +130,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/lib/net/rte_ip6.h b/lib/net/rte_ip6.h index a5ebe065b61e..725402e6d08e 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 @@ -36,6 +37,7 @@ extern "C" { #endif #define RTE_IPV6_ADDR_SIZE 16 +#define RTE_IPV6_MAX_DEPTH (RTE_IPV6_ADDR_SIZE * CHAR_BIT) /** * IPv6 Address @@ -44,6 +46,169 @@ struct rte_ipv6_addr { uint8_t a[RTE_IPV6_ADDR_SIZE]; }; +/** + * Check if two IPv6 Addresses are equal. + */ +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 ip + * The address to mask. + * @param 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. + */ +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. + * + * This function does not handle masks with "holes" and will return the number + * of consecurive bits set to 1 starting from the beginning of the mask. + * + * @param mask + * The address 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. + */ +#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN +#define RTE_IPV6_U16_SPLIT(x) (uint8_t)(x), (uint8_t)((uint16_t)(x) >> 8) +#else +#define RTE_IPV6_U16_SPLIT(x) (uint8_t)((uint16_t)(x) >> 8), (uint8_t)(x) +#endif + +/** + * Shorthand to define a literal IPv6 address based on 16bit unsigned integers. + */ +#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 rte_ipv6_addr structures. + */ +#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.: + * + * printf(RTE_IPV6_ADDR_FMT "\n", RTE_IPV6_ADDR_SPLIT(&ip)); + * + * @param ip + * A struct rte_ipv6_addr pointer. + */ +#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. + */ +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.46.2