1 /*
2 * Copyright (c) 2025 Lexi Winter
3 *
4 * SPDX-License-Identifier: ISC
5 */
6
7 /*
8 * Tests for link_addr() and link_ntoa().
9 *
10 * link_addr converts a string representing an (optionally null) interface name
11 * followed by an Ethernet address into a sockaddr_dl. The expected format is
12 * "[ifname]:lladdr". This means if ifname is not specified, the leading colon
13 * is still required.
14 *
15 * link_ntoa does the inverse of link_addr, returning a string representation
16 * of the address.
17 *
18 * Note that the output format of link_ntoa is not valid input for link_addr
19 * since the leading colon may be omitted. This is by design.
20 */
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24
25 #include <net/ethernet.h>
26 #include <net/if_dl.h>
27
28 #include <format>
29 #include <iostream>
30 #include <ranges>
31 #include <span>
32 #include <utility>
33 #include <vector>
34
35 #include <cstddef>
36 #include <cstdint>
37
38 #include <atf-c++.hpp>
39
40 using namespace std::literals;
41
42 /*
43 * Define operator== and operator<< for ether_addr so we can use them in
44 * ATF_EXPECT_EQ expressions.
45 */
46
47 bool
operator ==(ether_addr a,ether_addr b)48 operator==(ether_addr a, ether_addr b)
49 {
50 return (std::ranges::equal(a.octet, b.octet));
51 }
52
53 std::ostream &
operator <<(std::ostream & s,ether_addr a)54 operator<<(std::ostream &s, ether_addr a)
55 {
56 for (unsigned i = 0; i < ETHER_ADDR_LEN; ++i) {
57 if (i > 0)
58 s << ":";
59
60 s << std::format("{:02x}", static_cast<int>(a.octet[i]));
61 }
62
63 return (s);
64 }
65
66 /*
67 * Create a sockaddr_dl from a string using link_addr(), and ensure the
68 * returned struct looks valid.
69 */
70 sockaddr_dl
make_linkaddr(const std::string & addr)71 make_linkaddr(const std::string &addr)
72 {
73 auto sdl = sockaddr_dl{};
74 int ret;
75
76 sdl.sdl_len = sizeof(sdl);
77 ret = ::link_addr(addr.c_str(), &sdl);
78
79 ATF_REQUIRE_EQ(0, ret);
80 ATF_REQUIRE_EQ(AF_LINK, static_cast<int>(sdl.sdl_family));
81 ATF_REQUIRE_EQ(true, sdl.sdl_len > 0);
82 ATF_REQUIRE_EQ(true, sdl.sdl_nlen >= 0);
83
84 return (sdl);
85 }
86
87 /*
88 * Return the data stored in a sockaddr_dl as a span.
89 */
90 std::span<const char>
data(const sockaddr_dl & sdl)91 data(const sockaddr_dl &sdl)
92 {
93 // sdl_len is the entire structure, but we only want the length of the
94 // data area.
95 auto dlen = sdl.sdl_len - offsetof(sockaddr_dl, sdl_data);
96 return {&sdl.sdl_data[0], dlen};
97 }
98
99 /*
100 * Return the interface name stored in a sockaddr_dl as a string.
101 */
102 std::string_view
ifname(const sockaddr_dl & sdl)103 ifname(const sockaddr_dl &sdl)
104 {
105 auto name = data(sdl).subspan(0, sdl.sdl_nlen);
106 return {name.begin(), name.end()};
107 }
108
109 /*
110 * Return the Ethernet address stored in a sockaddr_dl as an ether_addr.
111 */
112 ether_addr
addr(const sockaddr_dl & sdl)113 addr(const sockaddr_dl &sdl)
114 {
115 ether_addr ret{};
116 ATF_REQUIRE_EQ(ETHER_ADDR_LEN, sdl.sdl_alen);
117 std::ranges::copy(data(sdl).subspan(sdl.sdl_nlen, ETHER_ADDR_LEN),
118 &ret.octet[0]);
119 return (ret);
120 }
121
122 /*
123 * Return the link address stored in a sockaddr_dl as a span of octets.
124 */
125 std::span<const std::uint8_t>
lladdr(const sockaddr_dl & sdl)126 lladdr(const sockaddr_dl &sdl)
127 {
128 auto data = reinterpret_cast<const uint8_t *>(LLADDR(&sdl));
129 return {data, data + sdl.sdl_alen};
130 }
131
132
133 /*
134 * Some sample addresses we use for testing. Include at least one address for
135 * each format we want to support.
136 */
137
138 struct test_address {
139 std::string input; /* value passed to link_addr */
140 std::string ntoa; /* expected return from link_ntoa */
141 ether_addr addr{}; /* expected return from link_addr */
142 };
143
144 std::vector<test_address> test_addresses{
145 // No delimiter
146 {"001122334455"s, "0.11.22.33.44.55",
147 ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
148
149 // Colon delimiter
150 {"00:11:22:33:44:55"s, "0.11.22.33.44.55",
151 ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
152
153 // Dash delimiter
154 {"00-11-22-33-44-55"s, "0.11.22.33.44.55",
155 ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
156
157 // Period delimiter (link_ntoa format)
158 {"00.11.22.33.44.55"s, "0.11.22.33.44.55",
159 ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
160
161 // Period delimiter (Cisco format)
162 {"0011.2233.4455"s, "0.11.22.33.44.55",
163 ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
164
165 // An addresses without leading zeroes.
166 {"0:1:02:30:4:55"s, "0.1.2.30.4.55",
167 ether_addr{0x00, 0x01, 0x02, 0x30, 0x04, 0x55}},
168
169 // An address with some uppercase letters.
170 {"AA:B:cC:Dd:e0:1f"s, "aa.b.cc.dd.e0.1f",
171 ether_addr{0xaa, 0x0b, 0xcc, 0xdd, 0xe0, 0x1f}},
172
173 // Addresses composed only of letters, to make sure they're not
174 // confused with an interface name.
175
176 {"aabbccddeeff"s, "aa.bb.cc.dd.ee.ff",
177 ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}},
178
179 {"aa:bb:cc:dd:ee:ff"s, "aa.bb.cc.dd.ee.ff",
180 ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}},
181
182 // Address with a blank octet; this is an old form of Ethernet address.
183 {"00:11::33:44:55"s, "0.11.0.33.44.55",
184 ether_addr{0x00, 0x11, 0x00, 0x33, 0x44, 0x55}},
185 };
186
187 /*
188 * Test without an interface name.
189 */
190 ATF_TEST_CASE_WITHOUT_HEAD(basic)
ATF_TEST_CASE_BODY(basic)191 ATF_TEST_CASE_BODY(basic)
192 {
193 for (const auto &ta : test_addresses) {
194 // This does basic tests on the returned value.
195 auto sdl = make_linkaddr(":" + ta.input);
196
197 // Check the ifname and address.
198 ATF_REQUIRE_EQ(""s, ifname(sdl));
199 ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen));
200 ATF_REQUIRE_EQ(ta.addr, addr(sdl));
201
202 // Check link_ntoa returns the expected value.
203 auto ntoa = std::string(::link_ntoa(&sdl));
204 ATF_REQUIRE_EQ(ta.ntoa, ntoa);
205 }
206
207 }
208
209 /*
210 * Test with an interface name.
211 */
212 ATF_TEST_CASE_WITHOUT_HEAD(ifname)
ATF_TEST_CASE_BODY(ifname)213 ATF_TEST_CASE_BODY(ifname)
214 {
215 for (const auto &ta : test_addresses) {
216 auto sdl = make_linkaddr("ix0:" + ta.input);
217
218 ATF_REQUIRE_EQ("ix0", ifname(sdl));
219 ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen));
220 ATF_REQUIRE_EQ(ta.addr, addr(sdl));
221
222 auto ntoa = std::string(::link_ntoa(&sdl));
223 ATF_REQUIRE_EQ("ix0:" + ta.ntoa, ntoa);
224 }
225
226 }
227
228 /*
229 * Test with some invalid addresses.
230 */
231 ATF_TEST_CASE_WITHOUT_HEAD(invalid)
ATF_TEST_CASE_BODY(invalid)232 ATF_TEST_CASE_BODY(invalid)
233 {
234 auto const invalid_addresses = std::vector{
235 // Invalid separator
236 ":1/2/3"s,
237 "ix0:1/2/3"s,
238
239 // Multiple different separators
240 ":1.2-3"s,
241 "ix0:1.2-3"s,
242
243 // An IP address
244 ":10.1.2.200/28"s,
245 "ix0:10.1.2.200/28"s,
246
247 // Valid address followed by garbage
248 ":1.2.3xxx"s,
249 ":1.2.3.xxx"s,
250 "ix0:1.2.3xxx"s,
251 "ix0:1.2.3.xxx"s,
252 };
253
254 for (auto const &addr : invalid_addresses) {
255 int ret;
256
257 auto sdl = sockaddr_dl{};
258 sdl.sdl_len = sizeof(sdl);
259
260 ret = link_addr(addr.c_str(), &sdl);
261 ATF_REQUIRE_EQ(-1, ret);
262 }
263 }
264
265 /*
266 * Test some non-Ethernet addresses.
267 */
268 ATF_TEST_CASE_WITHOUT_HEAD(nonether)
ATF_TEST_CASE_BODY(nonether)269 ATF_TEST_CASE_BODY(nonether)
270 {
271 sockaddr_dl sdl;
272
273 /* A short address */
274 sdl = make_linkaddr(":1:23:cc");
275 ATF_REQUIRE_EQ("", ifname(sdl));
276 ATF_REQUIRE_EQ("1.23.cc"s, ::link_ntoa(&sdl));
277 ATF_REQUIRE_EQ(3, sdl.sdl_alen);
278 ATF_REQUIRE_EQ(true,
279 std::ranges::equal(std::vector{0x01u, 0x23u, 0xccu}, lladdr(sdl)));
280
281 /* A long address */
282 sdl = make_linkaddr(":1:23:cc:a:b:c:d:e:f");
283 ATF_REQUIRE_EQ("", ifname(sdl));
284 ATF_REQUIRE_EQ("1.23.cc.a.b.c.d.e.f"s, ::link_ntoa(&sdl));
285 ATF_REQUIRE_EQ(9, sdl.sdl_alen);
286 ATF_REQUIRE_EQ(true, std::ranges::equal(
287 std::vector{0x01u, 0x23u, 0xccu, 0xau, 0xbu, 0xcu, 0xdu, 0xeu, 0xfu},
288 lladdr(sdl)));
289 }
290
291 /*
292 * Test link_addr behaviour with undersized buffers.
293 */
294 ATF_TEST_CASE_WITHOUT_HEAD(smallbuf)
ATF_TEST_CASE_BODY(smallbuf)295 ATF_TEST_CASE_BODY(smallbuf)
296 {
297 static constexpr auto garbage = std::byte{0xcc};
298 auto buf = std::vector<std::byte>();
299 sockaddr_dl *sdl;
300 int ret;
301
302 /*
303 * Make an sdl with an sdl_data member of the appropriate size, and
304 * place it in buf. Ensure it's followed by a trailing byte of garbage
305 * so we can test that link_addr doesn't write past the end.
306 */
307 auto mksdl = [&buf](std::size_t datalen) -> sockaddr_dl * {
308 auto actual_size = datalen + offsetof(sockaddr_dl, sdl_data);
309
310 buf.resize(actual_size + 1);
311 std::ranges::fill(buf, garbage);
312
313 auto *sdl = new(reinterpret_cast<sockaddr_dl *>(&buf[0]))
314 sockaddr_dl;
315 sdl->sdl_len = actual_size;
316 return (sdl);
317 };
318
319 /* An sdl large enough to store the interface name */
320 sdl = mksdl(3);
321 ret = link_addr("ix0:1.2.3", sdl);
322 ATF_REQUIRE(*rbegin(buf) == garbage);
323 ATF_REQUIRE_EQ(-1, ret);
324 ATF_REQUIRE_EQ(ENOSPC, errno);
325 ATF_REQUIRE_EQ(3, sdl->sdl_nlen);
326 ATF_REQUIRE_EQ("ix0", ifname(*sdl));
327 ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen));
328
329 /*
330 * For these tests, test both with and without an interface name, since
331 * that will overflow the buffer in different places.
332 */
333
334 /* An empty sdl. Nothing may grow on this cursed ground. */
335
336 sdl = mksdl(0);
337 ret = link_addr("ix0:1.2.3", sdl);
338 ATF_REQUIRE(*rbegin(buf) == garbage);
339 ATF_REQUIRE_EQ(-1, ret);
340 ATF_REQUIRE_EQ(ENOSPC, errno);
341 ATF_REQUIRE_EQ(0, sdl->sdl_nlen);
342 ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen));
343
344 sdl = mksdl(0);
345 ret = link_addr(":1.2.3", sdl);
346 ATF_REQUIRE(*rbegin(buf) == garbage);
347 ATF_REQUIRE_EQ(-1, ret);
348 ATF_REQUIRE_EQ(ENOSPC, errno);
349 ATF_REQUIRE_EQ(0, sdl->sdl_nlen);
350 ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen));
351
352 /*
353 * An sdl large enough to store the interface name and two octets of the
354 * address.
355 */
356
357 sdl = mksdl(5);
358 ret = link_addr("ix0:1.2.3", sdl);
359 ATF_REQUIRE(*rbegin(buf) == garbage);
360 ATF_REQUIRE_EQ(-1, ret);
361 ATF_REQUIRE_EQ(ENOSPC, errno);
362 ATF_REQUIRE_EQ("ix0", ifname(*sdl));
363 ATF_REQUIRE(std::ranges::equal(
364 std::vector{ 0x01, 0x02 }, lladdr(*sdl)));
365
366 sdl = mksdl(2);
367 ret = link_addr(":1.2.3", sdl);
368 ATF_REQUIRE(*rbegin(buf) == garbage);
369 ATF_REQUIRE_EQ(-1, ret);
370 ATF_REQUIRE_EQ(ENOSPC, errno);
371 ATF_REQUIRE_EQ("", ifname(*sdl));
372 ATF_REQUIRE(std::ranges::equal(
373 std::vector{ 0x01, 0x02 }, lladdr(*sdl)));
374
375 /*
376 * An sdl large enough to store the entire address.
377 */
378
379 sdl = mksdl(6);
380 ret = link_addr("ix0:1.2.3", sdl);
381 ATF_REQUIRE(*rbegin(buf) == garbage);
382 ATF_REQUIRE_EQ(0, ret);
383 ATF_REQUIRE_EQ("ix0", ifname(*sdl));
384 ATF_REQUIRE(std::ranges::equal(
385 std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl)));
386
387 sdl = mksdl(3);
388 ret = link_addr(":1.2.3", sdl);
389 ATF_REQUIRE(*rbegin(buf) == garbage);
390 ATF_REQUIRE_EQ(0, ret);
391 ATF_REQUIRE_EQ("", ifname(*sdl));
392 ATF_REQUIRE(std::ranges::equal(
393 std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl)));
394 }
395
396 /*
397 * Test an extremely long address which would overflow link_ntoa's internal
398 * buffer. It should handle this by truncating the output.
399 * (Test for SA-16:37.libc / CVE-2016-6559.)
400 */
401 ATF_TEST_CASE_WITHOUT_HEAD(overlong)
ATF_TEST_CASE_BODY(overlong)402 ATF_TEST_CASE_BODY(overlong)
403 {
404 auto sdl = make_linkaddr(
405 ":01.02.03.04.05.06.07.08.09.0a.0b.0c.0d.0e.0f."
406 "11.12.13.14.15.16.17.18.19.1a.1b.1c.1d.1e.1f."
407 "22.22.23.24.25.26.27.28.29.2a.2b.2c.2d.2e.2f");
408
409 ATF_REQUIRE_EQ(
410 "1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.11.12.13.14.15.16.17.18.19.1a.1b."s,
411 ::link_ntoa(&sdl));
412 }
413
414 /*
415 * Test link_ntoa_r, the re-entrant version of link_ntoa().
416 */
417 ATF_TEST_CASE_WITHOUT_HEAD(link_ntoa_r)
ATF_TEST_CASE_BODY(link_ntoa_r)418 ATF_TEST_CASE_BODY(link_ntoa_r)
419 {
420 static constexpr char garbage = 0x41;
421 std::vector<char> buf;
422 sockaddr_dl sdl;
423 size_t len;
424 int ret;
425
426 // Return the contents of buf as a string, using the NUL terminator to
427 // determine length. This is to ensure we're using the return value in
428 // the same way C code would, but we do a bit more verification to
429 // elicit a test failure rather than a SEGV if it's broken.
430 auto bufstr = [&buf]() -> std::string_view {
431 // Find the NUL.
432 auto end = std::ranges::find(buf, '\0');
433 ATF_REQUIRE(end != buf.end());
434
435 // Intentionally chopping the NUL off.
436 return {begin(buf), end};
437 };
438
439 // Resize the buffer and set the contents to a known garbage value, so
440 // we don't accidentally have a NUL in the right place when link_ntoa_r
441 // didn't put it there.
442 auto resetbuf = [&buf, &len](std::size_t size) {
443 len = size;
444 buf.resize(len);
445 std::ranges::fill(buf, garbage);
446 };
447
448 // Test a short address with a large buffer.
449 sdl = make_linkaddr("ix0:1.2.3");
450 resetbuf(64);
451 ret = ::link_ntoa_r(&sdl, &buf[0], &len);
452 ATF_REQUIRE_EQ(0, ret);
453 ATF_REQUIRE_EQ(10, len);
454 ATF_REQUIRE_EQ("ix0:1.2.3"s, bufstr());
455
456 // Test a buffer which is exactly the right size.
457 sdl = make_linkaddr("ix0:1.2.3");
458 resetbuf(10);
459 ret = ::link_ntoa_r(&sdl, &buf[0], &len);
460 ATF_REQUIRE_EQ(0, ret);
461 ATF_REQUIRE_EQ(10, len);
462 ATF_REQUIRE_EQ("ix0:1.2.3"sv, bufstr());
463
464 // Test various short buffers, using a table of buffer length and the
465 // output we expect. All of these should produce valid but truncated
466 // strings, with a trailing NUL and with buflen set correctly.
467
468 auto buftests = std::vector<std::pair<std::size_t, std::string_view>>{
469 {1u, ""sv},
470 {2u, ""sv},
471 {3u, ""sv},
472 {4u, "ix0"sv},
473 {5u, "ix0:"sv},
474 {6u, "ix0:1"sv},
475 {7u, "ix0:1."sv},
476 {8u, "ix0:1.2"sv},
477 {9u, "ix0:1.2."sv},
478 };
479
480 for (auto const &[buflen, expected] : buftests) {
481 sdl = make_linkaddr("ix0:1.2.3");
482 resetbuf(buflen);
483 ret = ::link_ntoa_r(&sdl, &buf[0], &len);
484 ATF_REQUIRE_EQ(-1, ret);
485 ATF_REQUIRE_EQ(10, len);
486 ATF_REQUIRE_EQ(expected, bufstr());
487 }
488
489 // Test a NULL buffer, which should just set buflen.
490 sdl = make_linkaddr("ix0:1.2.3");
491 len = 0;
492 ret = ::link_ntoa_r(&sdl, NULL, &len);
493 ATF_REQUIRE_EQ(-1, ret);
494 ATF_REQUIRE_EQ(10, len);
495
496 // A NULL buffer with a non-zero length should also be accepted.
497 sdl = make_linkaddr("ix0:1.2.3");
498 len = 64;
499 ret = ::link_ntoa_r(&sdl, NULL, &len);
500 ATF_REQUIRE_EQ(-1, ret);
501 ATF_REQUIRE_EQ(10, len);
502
503 // Test a non-NULL buffer, but with a length of zero.
504 sdl = make_linkaddr("ix0:1.2.3");
505 resetbuf(1);
506 len = 0;
507 ret = ::link_ntoa_r(&sdl, &buf[0], &len);
508 ATF_REQUIRE_EQ(-1, ret);
509 ATF_REQUIRE_EQ(10, len);
510 // Check we really didn't write anything.
511 ATF_REQUIRE_EQ(garbage, buf[0]);
512
513 // Test a buffer which would be truncated in the middle of a two-digit
514 // hex octet, which should not write the truncated octet at all.
515 sdl = make_linkaddr("ix0:1.22.3");
516 resetbuf(8);
517 ret = ::link_ntoa_r(&sdl, &buf[0], &len);
518 ATF_REQUIRE_EQ(-1, ret);
519 ATF_REQUIRE_EQ(11, len);
520 ATF_REQUIRE_EQ("ix0:1."sv, bufstr());
521 }
522
ATF_INIT_TEST_CASES(tcs)523 ATF_INIT_TEST_CASES(tcs)
524 {
525 ATF_ADD_TEST_CASE(tcs, basic);
526 ATF_ADD_TEST_CASE(tcs, ifname);
527 ATF_ADD_TEST_CASE(tcs, smallbuf);
528 ATF_ADD_TEST_CASE(tcs, invalid);
529 ATF_ADD_TEST_CASE(tcs, nonether);
530 ATF_ADD_TEST_CASE(tcs, overlong);
531 ATF_ADD_TEST_CASE(tcs, link_ntoa_r);
532 }
533