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 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 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 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