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 48 operator==(ether_addr a, ether_addr b) 49 { 50 return (std::ranges::equal(a.octet, b.octet)); 51 } 52 53 std::ostream & 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 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> 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 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 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> 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) 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) 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) 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) 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) 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) 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) 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 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