Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F164640742
D50062.id154433.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D50062.id154433.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D50062: libc tests: add tests for link_addr(3) and link_ntoa(3)
Attached
Detach File
Event Timeline
Log In to Comment