1*b5c46058SRobert Mustacchi /*
2*b5c46058SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*b5c46058SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*b5c46058SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*b5c46058SRobert Mustacchi * 1.0 of the CDDL.
6*b5c46058SRobert Mustacchi *
7*b5c46058SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*b5c46058SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*b5c46058SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*b5c46058SRobert Mustacchi */
11*b5c46058SRobert Mustacchi
12*b5c46058SRobert Mustacchi /*
13*b5c46058SRobert Mustacchi * Copyright 2026 Oxide Computer Company
14*b5c46058SRobert Mustacchi */
15*b5c46058SRobert Mustacchi
16*b5c46058SRobert Mustacchi /*
17*b5c46058SRobert Mustacchi * This is a basic set of sanity checks for getifaddrs(3SOCKET) and its behavior
18*b5c46058SRobert Mustacchi * with AF_LINK addresses. This is generally designed around being a regression
19*b5c46058SRobert Mustacchi * test for #16383 and #16384 which were missing data entries in both the
20*b5c46058SRobert Mustacchi * sockaddr_dl and the struct if_data. Rather than change the system and see
21*b5c46058SRobert Mustacchi * what's there, we instead walk the links in the system assuming there is
22*b5c46058SRobert Mustacchi * usually at least one interface present.
23*b5c46058SRobert Mustacchi */
24*b5c46058SRobert Mustacchi
25*b5c46058SRobert Mustacchi #include <sys/types.h>
26*b5c46058SRobert Mustacchi #include <sys/socket.h>
27*b5c46058SRobert Mustacchi #include <ifaddrs.h>
28*b5c46058SRobert Mustacchi #include <net/if.h>
29*b5c46058SRobert Mustacchi #include <stdlib.h>
30*b5c46058SRobert Mustacchi #include <err.h>
31*b5c46058SRobert Mustacchi #include <libdladm.h>
32*b5c46058SRobert Mustacchi #include <libdllink.h>
33*b5c46058SRobert Mustacchi #include <string.h>
34*b5c46058SRobert Mustacchi #include <stdbool.h>
35*b5c46058SRobert Mustacchi #include <libdlpi.h>
36*b5c46058SRobert Mustacchi
37*b5c46058SRobert Mustacchi typedef struct {
38*b5c46058SRobert Mustacchi const struct ifaddrs *ic_ifa;
39*b5c46058SRobert Mustacchi int ic_ret;
40*b5c46058SRobert Mustacchi uint32_t ic_nmatch;
41*b5c46058SRobert Mustacchi } ifaddr_cb_t;
42*b5c46058SRobert Mustacchi
43*b5c46058SRobert Mustacchi typedef struct {
44*b5c46058SRobert Mustacchi bool mc_found;
45*b5c46058SRobert Mustacchi bool *mc_pass;
46*b5c46058SRobert Mustacchi const char *mc_name;
47*b5c46058SRobert Mustacchi const struct ifaddrs *mc_ifa;
48*b5c46058SRobert Mustacchi } mac_cb_t;
49*b5c46058SRobert Mustacchi
50*b5c46058SRobert Mustacchi static boolean_t
dladm_walk_mac_cb(void * arg,dladm_macaddr_attr_t * attr)51*b5c46058SRobert Mustacchi dladm_walk_mac_cb(void *arg, dladm_macaddr_attr_t *attr)
52*b5c46058SRobert Mustacchi {
53*b5c46058SRobert Mustacchi mac_cb_t *cb = arg;
54*b5c46058SRobert Mustacchi const struct ifaddrs *ifa = cb->mc_ifa;
55*b5c46058SRobert Mustacchi const struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr;
56*b5c46058SRobert Mustacchi const struct if_data *if_data = ifa->ifa_data;
57*b5c46058SRobert Mustacchi
58*b5c46058SRobert Mustacchi if (attr->ma_addrlen != dl->sdl_alen)
59*b5c46058SRobert Mustacchi return (B_TRUE);
60*b5c46058SRobert Mustacchi
61*b5c46058SRobert Mustacchi if (memcmp(LLADDR(dl), attr->ma_addr, attr->ma_addrlen) != 0) {
62*b5c46058SRobert Mustacchi return (B_TRUE);
63*b5c46058SRobert Mustacchi }
64*b5c46058SRobert Mustacchi
65*b5c46058SRobert Mustacchi cb->mc_found = true;
66*b5c46058SRobert Mustacchi if (if_data != NULL && if_data->ifi_addrlen != attr->ma_addrlen) {
67*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: found if_data address length "
68*b5c46058SRobert Mustacchi "0x%x, but expected 0x%x", cb->mc_name,
69*b5c46058SRobert Mustacchi if_data->ifi_addrlen, attr->ma_addrlen);
70*b5c46058SRobert Mustacchi *cb->mc_pass = false;
71*b5c46058SRobert Mustacchi }
72*b5c46058SRobert Mustacchi
73*b5c46058SRobert Mustacchi return (B_TRUE);
74*b5c46058SRobert Mustacchi }
75*b5c46058SRobert Mustacchi
76*b5c46058SRobert Mustacchi static int
dladm_walk_cb(dladm_handle_t hdl,datalink_id_t id,void * arg)77*b5c46058SRobert Mustacchi dladm_walk_cb(dladm_handle_t hdl, datalink_id_t id, void *arg)
78*b5c46058SRobert Mustacchi {
79*b5c46058SRobert Mustacchi dladm_status_t dlret;
80*b5c46058SRobert Mustacchi ifaddr_cb_t *cb = arg;
81*b5c46058SRobert Mustacchi char name[MAXLINKNAMELEN];
82*b5c46058SRobert Mustacchi char dlerr[DLADM_STRSIZE];
83*b5c46058SRobert Mustacchi uint32_t media;
84*b5c46058SRobert Mustacchi char buf[DLADM_PROP_VAL_MAX];
85*b5c46058SRobert Mustacchi char *valptr[1];
86*b5c46058SRobert Mustacchi uint_t valcnt;
87*b5c46058SRobert Mustacchi
88*b5c46058SRobert Mustacchi dlret = dladm_datalink_id2info(hdl, id, NULL, NULL, &media, name,
89*b5c46058SRobert Mustacchi sizeof (name));
90*b5c46058SRobert Mustacchi if (dlret != DLADM_STATUS_OK) {
91*b5c46058SRobert Mustacchi cb->ic_ret = EXIT_FAILURE;
92*b5c46058SRobert Mustacchi warnx("INTERNAL TEST FAILURE: failed to get datalink "
93*b5c46058SRobert Mustacchi "information for link 0x%x: %s", id, dladm_status2str(dlret,
94*b5c46058SRobert Mustacchi dlerr));
95*b5c46058SRobert Mustacchi
96*b5c46058SRobert Mustacchi return (DLADM_WALK_CONTINUE);
97*b5c46058SRobert Mustacchi }
98*b5c46058SRobert Mustacchi
99*b5c46058SRobert Mustacchi /*
100*b5c46058SRobert Mustacchi * Before we scan the list looking for this, see if the link is up. In
101*b5c46058SRobert Mustacchi * particular, dladm will see all datalinks; however, if it isn't really
102*b5c46058SRobert Mustacchi * in use by some client, then it's likely that it's not actually in our
103*b5c46058SRobert Mustacchi * getifaddrs() list. If filtering to links that are not in an unknown
104*b5c46058SRobert Mustacchi * state gives us false positives then we can revisit this.
105*b5c46058SRobert Mustacchi */
106*b5c46058SRobert Mustacchi valptr[0] = buf;
107*b5c46058SRobert Mustacchi valcnt = 1;
108*b5c46058SRobert Mustacchi if (dladm_get_linkprop(hdl, id, DLADM_PROP_VAL_CURRENT, "state", valptr,
109*b5c46058SRobert Mustacchi &valcnt) != DLADM_STATUS_OK) {
110*b5c46058SRobert Mustacchi warnx("skipping datalink %s as we could not get \"state\" link "
111*b5c46058SRobert Mustacchi "link property", name);
112*b5c46058SRobert Mustacchi return (DLADM_WALK_CONTINUE);
113*b5c46058SRobert Mustacchi }
114*b5c46058SRobert Mustacchi
115*b5c46058SRobert Mustacchi if (strcmp(buf, "up") != 0 && strcmp(buf, "down") != 0) {
116*b5c46058SRobert Mustacchi return (DLADM_WALK_CONTINUE);
117*b5c46058SRobert Mustacchi }
118*b5c46058SRobert Mustacchi
119*b5c46058SRobert Mustacchi for (const struct ifaddrs *i = cb->ic_ifa; i != NULL; i = i->ifa_next) {
120*b5c46058SRobert Mustacchi bool pass = true;
121*b5c46058SRobert Mustacchi
122*b5c46058SRobert Mustacchi if (strcmp(name, i->ifa_name) != 0)
123*b5c46058SRobert Mustacchi continue;
124*b5c46058SRobert Mustacchi if (i->ifa_addr->sa_family != AF_LINK)
125*b5c46058SRobert Mustacchi continue;
126*b5c46058SRobert Mustacchi
127*b5c46058SRobert Mustacchi struct sockaddr_dl *dl = (struct sockaddr_dl *)i->ifa_addr;
128*b5c46058SRobert Mustacchi uint_t ift_type = dlpi_iftype(media);
129*b5c46058SRobert Mustacchi
130*b5c46058SRobert Mustacchi if (ift_type != dl->sdl_type) {
131*b5c46058SRobert Mustacchi pass = false;
132*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: found type 0x%x, but "
133*b5c46058SRobert Mustacchi "expected type 0x%x (dlpi 0x%x)", name,
134*b5c46058SRobert Mustacchi dl->sdl_type, ift_type, media);
135*b5c46058SRobert Mustacchi }
136*b5c46058SRobert Mustacchi
137*b5c46058SRobert Mustacchi size_t nlen = strlen(name);
138*b5c46058SRobert Mustacchi if (nlen != dl->sdl_nlen) {
139*b5c46058SRobert Mustacchi pass = false;
140*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: name length mismatch: "
141*b5c46058SRobert Mustacchi "found %u, expected %zu", name, dl->sdl_nlen, nlen);
142*b5c46058SRobert Mustacchi }
143*b5c46058SRobert Mustacchi
144*b5c46058SRobert Mustacchi if (dl->sdl_nlen > 0) {
145*b5c46058SRobert Mustacchi if (strncmp(name, &dl->sdl_data[0], dl->sdl_nlen) !=
146*b5c46058SRobert Mustacchi 0) {
147*b5c46058SRobert Mustacchi pass = false;
148*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: sockaddr_dl does "
149*b5c46058SRobert Mustacchi "not match name", name);
150*b5c46058SRobert Mustacchi }
151*b5c46058SRobert Mustacchi }
152*b5c46058SRobert Mustacchi
153*b5c46058SRobert Mustacchi /*
154*b5c46058SRobert Mustacchi * Walk device MAC addresses to ensure that this looks right.
155*b5c46058SRobert Mustacchi */
156*b5c46058SRobert Mustacchi mac_cb_t mcb = { false, &pass, name, i };
157*b5c46058SRobert Mustacchi (void) dladm_walk_macaddr(hdl, id, &mcb, dladm_walk_mac_cb);
158*b5c46058SRobert Mustacchi if (!mcb.mc_found) {
159*b5c46058SRobert Mustacchi pass = false;
160*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: failed to find matching "
161*b5c46058SRobert Mustacchi "mac address", name);
162*b5c46058SRobert Mustacchi }
163*b5c46058SRobert Mustacchi
164*b5c46058SRobert Mustacchi /*
165*b5c46058SRobert Mustacchi * Check a few last aspects of the ifi_data. The ifi_addrlen has
166*b5c46058SRobert Mustacchi * already been filled in. We need to verify the MTU and type.
167*b5c46058SRobert Mustacchi */
168*b5c46058SRobert Mustacchi if (i->ifa_data != NULL) {
169*b5c46058SRobert Mustacchi struct if_data *data = i->ifa_data;
170*b5c46058SRobert Mustacchi
171*b5c46058SRobert Mustacchi if (data->ifi_type != ift_type) {
172*b5c46058SRobert Mustacchi pass = false;
173*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: found if_data "
174*b5c46058SRobert Mustacchi "ifi_type 0x%x, but expected type 0x%x "
175*b5c46058SRobert Mustacchi "(dlpi 0x%x)", name, data->ifi_type,
176*b5c46058SRobert Mustacchi ift_type, media);
177*b5c46058SRobert Mustacchi }
178*b5c46058SRobert Mustacchi
179*b5c46058SRobert Mustacchi valptr[0] = buf;
180*b5c46058SRobert Mustacchi valcnt = 1;
181*b5c46058SRobert Mustacchi if (dladm_get_linkprop(hdl, id, DLADM_PROP_VAL_CURRENT,
182*b5c46058SRobert Mustacchi "mtu", valptr, &valcnt) == DLADM_STATUS_OK) {
183*b5c46058SRobert Mustacchi /*
184*b5c46058SRobert Mustacchi * Assume libdladm gives us something
185*b5c46058SRobert Mustacchi * reasonable.
186*b5c46058SRobert Mustacchi */
187*b5c46058SRobert Mustacchi ulong_t val = strtoul(buf, NULL, 10);
188*b5c46058SRobert Mustacchi if (val != data->ifi_mtu) {
189*b5c46058SRobert Mustacchi pass = false;
190*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: found "
191*b5c46058SRobert Mustacchi "MTU %u, expected %lu", name,
192*b5c46058SRobert Mustacchi data->ifi_mtu, val);
193*b5c46058SRobert Mustacchi }
194*b5c46058SRobert Mustacchi }
195*b5c46058SRobert Mustacchi } else {
196*b5c46058SRobert Mustacchi pass = false;
197*b5c46058SRobert Mustacchi warnx("TEST FAILED: link %s: found NULL ifa_data "
198*b5c46058SRobert Mustacchi "pointer", name);
199*b5c46058SRobert Mustacchi }
200*b5c46058SRobert Mustacchi
201*b5c46058SRobert Mustacchi if (pass) {
202*b5c46058SRobert Mustacchi (void) printf("TEST PASSED: %s AF_LINK entry looks "
203*b5c46058SRobert Mustacchi "right\n", name);
204*b5c46058SRobert Mustacchi } else {
205*b5c46058SRobert Mustacchi cb->ic_ret = EXIT_FAILURE;
206*b5c46058SRobert Mustacchi }
207*b5c46058SRobert Mustacchi
208*b5c46058SRobert Mustacchi cb->ic_nmatch++;
209*b5c46058SRobert Mustacchi return (DLADM_WALK_CONTINUE);
210*b5c46058SRobert Mustacchi }
211*b5c46058SRobert Mustacchi
212*b5c46058SRobert Mustacchi warnx("TEST FAILED: failed to find matching ifaddrs entry for datalink "
213*b5c46058SRobert Mustacchi "%s (0x%x)", name, id);
214*b5c46058SRobert Mustacchi cb->ic_ret = EXIT_FAILURE;
215*b5c46058SRobert Mustacchi return (DLADM_WALK_CONTINUE);
216*b5c46058SRobert Mustacchi }
217*b5c46058SRobert Mustacchi
218*b5c46058SRobert Mustacchi int
main(void)219*b5c46058SRobert Mustacchi main(void)
220*b5c46058SRobert Mustacchi {
221*b5c46058SRobert Mustacchi struct ifaddrs *ifa;
222*b5c46058SRobert Mustacchi dladm_status_t dlret;
223*b5c46058SRobert Mustacchi dladm_handle_t dladm;
224*b5c46058SRobert Mustacchi char dlerr[DLADM_STRSIZE];
225*b5c46058SRobert Mustacchi ifaddr_cb_t cb;
226*b5c46058SRobert Mustacchi
227*b5c46058SRobert Mustacchi if (getifaddrs(&ifa) != 0) {
228*b5c46058SRobert Mustacchi err(EXIT_FAILURE, "INTERNAL TEST FAILURE: getifaddrs() failed: "
229*b5c46058SRobert Mustacchi "test cannot proceed");
230*b5c46058SRobert Mustacchi }
231*b5c46058SRobert Mustacchi
232*b5c46058SRobert Mustacchi dlret = dladm_open(&dladm);
233*b5c46058SRobert Mustacchi if (dlret != DLADM_STATUS_OK) {
234*b5c46058SRobert Mustacchi errx(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
235*b5c46058SRobert Mustacchi "initialize libdladm handle: %s", dladm_status2str(dlret,
236*b5c46058SRobert Mustacchi dlerr));
237*b5c46058SRobert Mustacchi }
238*b5c46058SRobert Mustacchi
239*b5c46058SRobert Mustacchi cb.ic_ifa = ifa;
240*b5c46058SRobert Mustacchi cb.ic_ret = EXIT_SUCCESS;
241*b5c46058SRobert Mustacchi cb.ic_nmatch = 0;
242*b5c46058SRobert Mustacchi (void) dladm_walk_datalink_id(dladm_walk_cb, dladm, &cb,
243*b5c46058SRobert Mustacchi DATALINK_CLASS_PHYS | DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE,
244*b5c46058SRobert Mustacchi DLADM_OPT_ACTIVE);
245*b5c46058SRobert Mustacchi dladm_close(dladm);
246*b5c46058SRobert Mustacchi freeifaddrs(ifa);
247*b5c46058SRobert Mustacchi
248*b5c46058SRobert Mustacchi if (cb.ic_nmatch == 0) {
249*b5c46058SRobert Mustacchi cb.ic_ret = EXIT_FAILURE;
250*b5c46058SRobert Mustacchi warnx("no AF_LINK entries found");
251*b5c46058SRobert Mustacchi }
252*b5c46058SRobert Mustacchi
253*b5c46058SRobert Mustacchi if (cb.ic_ret == EXIT_SUCCESS) {
254*b5c46058SRobert Mustacchi (void) printf("All tests passed successfully\n");
255*b5c46058SRobert Mustacchi }
256*b5c46058SRobert Mustacchi return (cb.ic_ret);
257*b5c46058SRobert Mustacchi }
258