Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/tests/net/link_addr_test.cc
39500 views
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
534