Page MenuHomeFreeBSD

D50062.id154433.diff
No OneTemporary

D50062.id154433.diff

diff --git a/lib/libc/tests/net/Makefile b/lib/libc/tests/net/Makefile
--- a/lib/libc/tests/net/Makefile
+++ b/lib/libc/tests/net/Makefile
@@ -3,6 +3,9 @@
ATF_TESTS_C+= ether_test
ATF_TESTS_C+= eui64_aton_test
ATF_TESTS_C+= eui64_ntoa_test
+ATF_TESTS_CXX+= link_addr_test
+
+CXXSTD.link_addr_test= c++20
CFLAGS+= -I${.CURDIR}
diff --git a/lib/libc/tests/net/link_addr_test.cc b/lib/libc/tests/net/link_addr_test.cc
new file mode 100644
--- /dev/null
+++ b/lib/libc/tests/net/link_addr_test.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2025 Lexi Winter
+ *
+ * SPDX-License-Identifier: ISC
+ */
+
+/*
+ * Tests for link_addr() and link_ntoa().
+ *
+ * link_addr converts a string representing an (optionally null) interface name
+ * followed by an Ethernet address into a sockaddr_dl. The expected format is
+ * "[ifname]:lladdr". This means if ifname is not specified, the leading colon
+ * is still required.
+ *
+ * link_ntoa does the inverse of link_addr, returning a string representation
+ * of the address.
+ *
+ * Note that the output format of link_ntoa is not valid input for link_addr
+ * since the leading colon may be omitted. This is by design.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+
+#include <array>
+#include <span>
+#include <format>
+#include <utility>
+#include <ranges>
+#include <cstdint>
+
+#include <atf-c++.hpp>
+
+using namespace std::literals;
+
+/*
+ * Define operator== and operator<< for ether_addr so we can use them in
+ * ATF_EXPECT_EQ expressions.
+ */
+
+bool
+operator==(ether_addr a, ether_addr b)
+{
+ return (std::ranges::equal(a.octet, b.octet));
+}
+
+std::ostream &
+operator<<(std::ostream &s, ether_addr a)
+{
+ for (unsigned i = 0; i < ETHER_ADDR_LEN; ++i) {
+ if (i > 0)
+ s << ":";
+
+ s << std::format("{:02x}", static_cast<int>(a.octet[i]));
+ }
+
+ return (s);
+}
+
+/*
+ * Create a sockaddr_dl from a string using link_addr(), and ensure the
+ * returned struct looks valid.
+ */
+sockaddr_dl
+make_linkaddr(const std::string &addr)
+{
+ auto sdl = sockaddr_dl{};
+
+ sdl.sdl_len = sizeof(sdl);
+ ::link_addr(addr.c_str(), &sdl);
+
+ ATF_REQUIRE_EQ(AF_LINK, static_cast<int>(sdl.sdl_family));
+ ATF_REQUIRE_EQ(true, sdl.sdl_len >= 0);
+ ATF_REQUIRE_EQ(true, sdl.sdl_nlen >= 0);
+ ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen));
+
+ return (sdl);
+}
+
+/*
+ * Return the data stored in a sockaddr_dl as a span.
+ */
+std::span<const char>
+data(const sockaddr_dl &sdl)
+{
+ return {&sdl.sdl_data[0], sdl.sdl_len};
+}
+
+/*
+ * Return the interface name stored in a sockaddr_dl as a string.
+ */
+std::string_view
+ifname(const sockaddr_dl &sdl)
+{
+ auto name = data(sdl).subspan(0, sdl.sdl_nlen);
+ return {name.begin(), name.end()};
+}
+
+/*
+ * Return the Ethernet address stored in a sockaddr_dl as an ether_addr.
+ */
+ether_addr
+addr(const sockaddr_dl &sdl)
+{
+ ether_addr ret;
+ std::ranges::copy(data(sdl).subspan(sdl.sdl_nlen, ETHER_ADDR_LEN),
+ &ret.octet[0]);
+ return (ret);
+}
+
+/*
+ * Some sample addresses we use for testing. Include at least one address for
+ * each format we want to support.
+ */
+
+struct test_address {
+ std::string input; /* value passed to link_addr */
+ ether_addr addr; /* expected return from link_addr */
+ std::string ntoa; /* expected return from link_ntoa */
+};
+
+std::vector<test_address> test_addresses{
+ // No delimiter
+ {"001122334455"s,
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
+ "0.11.22.33.44.55"},
+
+ // Colon delimiter
+ {"00:11:22:33:44:55"s,
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
+ "0.11.22.33.44.55"},
+
+ // Dash delimiter
+ {"00-11-22-33-44-55"s,
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
+ "0.11.22.33.44.55"},
+
+ // Period delimiter (link_ntoa format)
+ {"00.11.22.33.44.55"s,
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
+ "0.11.22.33.44.55"},
+
+ // Period delimiter (Cisco format)
+ {"0011.2233.4455"s,
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
+ "0.11.22.33.44.55"},
+
+ // An addresses without leading zeroes.
+ {"0:1:02:30:4:55"s,
+ ether_addr{0x00, 0x01, 0x02, 0x30, 0x04, 0x55},
+ "0.1.2.30.4.55"},
+
+ // Addresses composed only of letters, to make sure they're not
+ // confused with an interface name.
+
+ {"aabbccddeeff"s,
+ ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+ "aa.bb.cc.dd.ee.ff"},
+
+ {"aa:bb:cc:dd:ee:ff"s,
+ ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+ "aa.bb.cc.dd.ee.ff"},
+};
+
+/*
+ * Test without an interface name.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(basic)
+ATF_TEST_CASE_BODY(basic)
+{
+ for (const auto &ta : test_addresses) {
+ // This does basic tests on the returned value.
+ auto sdl = make_linkaddr(":" + ta.input);
+
+ // Check the ifname and address.
+ ATF_REQUIRE_EQ(""s, ifname(sdl));
+ ATF_REQUIRE_EQ(ta.addr, addr(sdl));
+
+ // Check link_ntoa returns the expected value.
+ auto ntoa = std::string(::link_ntoa(&sdl));
+ ATF_REQUIRE_EQ(ta.ntoa, ntoa);
+ }
+
+}
+
+/*
+ * Test with an interface name.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(ifname)
+ATF_TEST_CASE_BODY(ifname)
+{
+ for (const auto &ta : test_addresses) {
+ auto sdl = make_linkaddr("ix0:" + ta.input);
+
+ ATF_REQUIRE_EQ("ix0", ifname(sdl));
+ ATF_REQUIRE_EQ(ta.addr, addr(sdl));
+
+ auto ntoa = std::string(::link_ntoa(&sdl));
+ ATF_REQUIRE_EQ("ix0:" + ta.ntoa, ntoa);
+ }
+
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, basic);
+ ATF_ADD_TEST_CASE(tcs, ifname);
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Aug 3, 7:42 PM (40 m, 37 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35914999
Default Alt Text
D50062.id154433.diff (5 KB)

Event Timeline