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