1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2017 RackTop Systems. 25 * Copyright 2022 Sebastian Wiedenroth 26 * Copyright 2026 Oxide Computer Company 27 */ 28 29 #include <netdb.h> 30 #include <nss_dbdefs.h> 31 #include <netinet/in.h> 32 #include <sys/socket.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <stdio.h> 36 #include <sys/sockio.h> 37 #include <sys/types.h> 38 #include <stdlib.h> 39 #include <net/if.h> 40 #include <door.h> 41 #include <fcntl.h> 42 #include <sys/mman.h> 43 #include <sys/dld_ioc.h> 44 #include <sys/dld.h> 45 #include <sys/dls_mgmt.h> 46 #include <sys/mac.h> 47 #include <sys/dlpi.h> 48 #include <net/if_types.h> 49 #include <ifaddrs.h> 50 #include <libsocket_priv.h> 51 52 /* 53 * <ifaddrs.h> directs folks towards our internal symbol, __getifaddrs. This 54 * means we cannot name the original symbol 'getifaddrs' here or it will be 55 * renamed. Instead, we use another redefine_extname to take care of this. Note, 56 * the extern declaration is required as gcc and others will only apply this for 57 * things they see an extern declaration for. 58 */ 59 #pragma redefine_extname getifaddrs_old getifaddrs 60 extern int getifaddrs_old(struct ifaddrs **); 61 62 /* 63 * Create a linked list of `struct ifaddrs' structures, one for each 64 * address that is UP. If successful, store the list in *ifap and 65 * return 0. On errors, return -1 and set `errno'. 66 * 67 * The storage returned in *ifap is allocated dynamically and can 68 * only be properly freed by passing it to `freeifaddrs'. 69 */ 70 int 71 _getifaddrs(struct ifaddrs **ifap, boolean_t can_handle_links) 72 { 73 int err; 74 char *cp; 75 struct ifaddrs *curr; 76 77 if (ifap == NULL) { 78 errno = EINVAL; 79 return (-1); 80 } 81 *ifap = NULL; 82 83 if (can_handle_links) { 84 err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED); 85 } else { 86 err = getallifaddrs(AF_INET, ifap, LIFC_ENABLED); 87 if (err != 0) 88 return (err); 89 90 /* Find end of the list to append to */ 91 curr = *ifap; 92 while (curr && curr->ifa_next) { 93 curr = curr->ifa_next; 94 } 95 96 err = getallifaddrs(AF_INET6, curr ? &curr->ifa_next : ifap, 97 LIFC_ENABLED); 98 } 99 100 if (err != 0) 101 return (err); 102 103 for (curr = *ifap; curr != NULL; curr = curr->ifa_next) { 104 if ((cp = strchr(curr->ifa_name, ':')) != NULL) 105 *cp = '\0'; 106 } 107 108 return (0); 109 } 110 111 /* 112 * Legacy symbol 113 * For a long time getifaddrs() only returned AF_INET and AF_INET6 entries. 114 * Some consumers came to expect that no other address family may be returned. 115 * To prevent existing binaries that can't handle AF_LINK entries from breaking 116 * this symbol is kept around. Consumers that want the fixed behaviour need to 117 * recompile and link to the fixed symbol. 118 */ 119 int 120 getifaddrs_old(struct ifaddrs **ifap) 121 { 122 return (_getifaddrs(ifap, B_FALSE)); 123 } 124 125 /* 126 * Current symbol 127 * May return AF_INET, AF_INET6 and AF_LINK entries 128 */ 129 int 130 __getifaddrs(struct ifaddrs **ifap) 131 { 132 return (_getifaddrs(ifap, B_TRUE)); 133 } 134 135 void 136 freeifaddrs(struct ifaddrs *ifa) 137 { 138 struct ifaddrs *curr; 139 140 while (ifa != NULL) { 141 curr = ifa; 142 ifa = ifa->ifa_next; 143 free(curr->ifa_name); 144 free(curr->ifa_addr); 145 free(curr->ifa_netmask); 146 free(curr->ifa_dstaddr); 147 free(curr->ifa_data); 148 free(curr); 149 } 150 } 151 152 static uint_t 153 dlpi_iftype(uint_t dlpitype) 154 { 155 switch (dlpitype) { 156 case DL_ETHER: 157 return (IFT_ETHER); 158 159 case DL_ATM: 160 return (IFT_ATM); 161 162 case DL_CSMACD: 163 return (IFT_ISO88023); 164 165 case DL_TPB: 166 return (IFT_ISO88024); 167 168 case DL_TPR: 169 return (IFT_ISO88025); 170 171 case DL_FDDI: 172 return (IFT_FDDI); 173 174 case DL_IB: 175 return (IFT_IB); 176 177 case DL_OTHER: 178 return (IFT_OTHER); 179 } 180 181 return (IFT_OTHER); 182 } 183 184 /* 185 * Make a door call to dlmgmtd. 186 * If successful the result is stored in rbuf and 0 returned. 187 * On errors, return -1 and set `errno'. 188 */ 189 static int 190 dl_door_call(int door_fd, void *arg, size_t asize, void *rbuf, size_t *rsizep) 191 { 192 int err; 193 door_arg_t darg; 194 darg.data_ptr = arg; 195 darg.data_size = asize; 196 darg.desc_ptr = NULL; 197 darg.desc_num = 0; 198 darg.rbuf = rbuf; 199 darg.rsize = *rsizep; 200 201 if (door_call(door_fd, &darg) == -1) { 202 return (-1); 203 } 204 205 if (darg.rbuf != rbuf) { 206 /* 207 * The size of the input rbuf was not big enough so that 208 * the door allocated the rbuf itself. In this case, return 209 * the required size to the caller. 210 */ 211 err = errno; 212 (void) munmap(darg.rbuf, darg.rsize); 213 *rsizep = darg.rsize; 214 errno = err; 215 return (-1); 216 } else if (darg.rsize != *rsizep) { 217 return (-1); 218 } 219 return (0); 220 } 221 222 223 /* 224 * Get the name from dlmgmtd by linkid. 225 * If successful the result is stored in name_retval and 0 returned. 226 * On errors, return -1 and set `errno'. 227 */ 228 static int 229 dl_get_name(int door_fd, datalink_id_t linkid, 230 dlmgmt_getname_retval_t *name_retval) 231 { 232 size_t name_sz = sizeof (*name_retval); 233 dlmgmt_door_getname_t getname; 234 bzero(&getname, sizeof (dlmgmt_door_getname_t)); 235 getname.ld_cmd = DLMGMT_CMD_GETNAME; 236 getname.ld_linkid = linkid; 237 238 if (dl_door_call(door_fd, &getname, sizeof (getname), name_retval, 239 &name_sz) < 0) { 240 return (-1); 241 } 242 if (name_retval->lr_err != 0) { 243 errno = name_retval->lr_err; 244 return (-1); 245 } 246 return (0); 247 } 248 249 /* 250 * Get the next link from dlmgmtd. 251 * Start iterating by passing DATALINK_INVALID_LINKID as linkid. 252 * The end is marked by next_retval.lr_linkid set to DATALINK_INVALID_LINKID. 253 * If successful the result is stored in next_retval and 0 returned. 254 * On errors, return -1 and set `errno'. 255 */ 256 static int 257 dl_get_next(int door_fd, datalink_id_t linkid, datalink_class_t class, 258 datalink_media_t dmedia, uint32_t flags, 259 dlmgmt_getnext_retval_t *next_retval) 260 { 261 size_t next_sz = sizeof (*next_retval); 262 dlmgmt_door_getnext_t getnext; 263 bzero(&getnext, sizeof (dlmgmt_door_getnext_t)); 264 getnext.ld_cmd = DLMGMT_CMD_GETNEXT; 265 getnext.ld_class = class; 266 getnext.ld_dmedia = dmedia; 267 getnext.ld_flags = flags; 268 getnext.ld_linkid = linkid; 269 270 if (dl_door_call(door_fd, &getnext, sizeof (getnext), next_retval, 271 &next_sz) < 0) { 272 return (-1); 273 } 274 if (next_retval->lr_err != 0) { 275 errno = next_retval->lr_err; 276 return (-1); 277 } 278 return (0); 279 } 280 281 /* 282 * Returns all addresses configured on the system. If flags contain 283 * LIFC_ENABLED, only the addresses that are UP are returned. 284 * Address list that is returned by this function must be freed 285 * using freeifaddrs(). 286 */ 287 int 288 getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags) 289 { 290 struct lifreq *buf = NULL; 291 struct lifreq *lifrp; 292 struct lifreq lifrl; 293 int ret; 294 int s, n, numifs; 295 struct ifaddrs *curr, *prev; 296 struct sockaddr_dl *ifa_addr = NULL; 297 if_data_t *ifa_data = NULL; 298 sa_family_t lifr_af; 299 datalink_id_t linkid; 300 dld_ioc_attr_t dia; 301 dld_macaddrinfo_t *dmip; 302 dld_ioc_macaddrget_t *iomp = NULL; 303 dlmgmt_getnext_retval_t next_retval; 304 dlmgmt_getname_retval_t name_retval; 305 int bufsize; 306 int nmacaddr = 1024; 307 int sock4 = -1; 308 int sock6 = -1; 309 int door_fd = -1; 310 int dld_fd = -1; 311 int err; 312 313 /* 314 * Initialize ifap to NULL so we can safely call freeifaddrs 315 * on it in case of error. 316 */ 317 if (ifap == NULL) 318 return (EINVAL); 319 *ifap = NULL; 320 321 if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || 322 (sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 323 goto fail; 324 } 325 326 bufsize = sizeof (dld_ioc_macaddrget_t) + nmacaddr * 327 sizeof (dld_macaddrinfo_t); 328 if ((iomp = malloc(bufsize)) == NULL) 329 goto fail; 330 331 retry: 332 bzero(iomp, bufsize); 333 /* Get all interfaces from SIOCGLIFCONF */ 334 ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED)); 335 if (ret != 0) 336 goto fail; 337 338 /* 339 * Loop through the interfaces obtained from SIOCGLIFCOMF 340 * and retrieve the addresses, netmask and flags. 341 */ 342 prev = NULL; 343 lifrp = buf; 344 for (n = 0; n < numifs; n++, lifrp++) { 345 346 /* Prepare for the ioctl call */ 347 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name, 348 sizeof (lifrl.lifr_name)); 349 lifr_af = lifrp->lifr_addr.ss_family; 350 if (af != AF_UNSPEC && lifr_af != af) 351 continue; 352 353 s = (lifr_af == AF_INET ? sock4 : sock6); 354 355 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) 356 goto fail; 357 if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP)) 358 continue; 359 360 /* 361 * Allocate the current list node. Each node contains data 362 * for one ifaddrs structure. 363 */ 364 curr = calloc(1, sizeof (struct ifaddrs)); 365 if (curr == NULL) 366 goto fail; 367 368 if (prev != NULL) { 369 prev->ifa_next = curr; 370 } else { 371 /* First node in the linked list */ 372 *ifap = curr; 373 } 374 prev = curr; 375 376 curr->ifa_flags = lifrl.lifr_flags; 377 if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL) 378 goto fail; 379 380 curr->ifa_addr = malloc(sizeof (struct sockaddr_storage)); 381 if (curr->ifa_addr == NULL) 382 goto fail; 383 (void) memcpy(curr->ifa_addr, &lifrp->lifr_addr, 384 sizeof (struct sockaddr_storage)); 385 386 /* Get the netmask */ 387 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0) 388 goto fail; 389 curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage)); 390 if (curr->ifa_netmask == NULL) 391 goto fail; 392 (void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr, 393 sizeof (struct sockaddr_storage)); 394 395 /* Get the destination for a pt-pt interface */ 396 if (curr->ifa_flags & IFF_POINTOPOINT) { 397 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0) 398 goto fail; 399 curr->ifa_dstaddr = malloc( 400 sizeof (struct sockaddr_storage)); 401 if (curr->ifa_dstaddr == NULL) 402 goto fail; 403 (void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr, 404 sizeof (struct sockaddr_storage)); 405 } else if (curr->ifa_flags & IFF_BROADCAST) { 406 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0) 407 goto fail; 408 curr->ifa_broadaddr = malloc( 409 sizeof (struct sockaddr_storage)); 410 if (curr->ifa_broadaddr == NULL) 411 goto fail; 412 (void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr, 413 sizeof (struct sockaddr_storage)); 414 } 415 416 } 417 418 /* add AF_LINK entries */ 419 if (af == AF_UNSPEC || af == AF_LINK) { 420 /* 421 * A datalink management door may not be available (for example 422 * in a shared IP zone). Only enumerate AF_LINK entries if the 423 * door exists. 424 */ 425 door_fd = open(DLMGMT_DOOR, O_RDONLY); 426 if (door_fd < 0) { 427 if (errno == ENOENT) 428 goto nolink; 429 goto fail; 430 } 431 if ((dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 432 goto fail; 433 434 linkid = DATALINK_INVALID_LINKID; 435 for (;;) { 436 struct sockaddr_storage *s; 437 size_t len; 438 439 if (dl_get_next(door_fd, linkid, DATALINK_CLASS_ALL, 440 DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE, 441 &next_retval) != 0) { 442 break; 443 } 444 445 linkid = next_retval.lr_linkid; 446 if (linkid == DATALINK_INVALID_LINKID) 447 break; 448 449 /* get mac addr */ 450 iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t); 451 iomp->dig_linkid = linkid; 452 453 if (ioctl(dld_fd, DLDIOC_MACADDRGET, iomp) < 0) 454 continue; 455 456 dmip = (dld_macaddrinfo_t *)(iomp + 1); 457 458 /* get name */ 459 if (dl_get_name(door_fd, linkid, &name_retval) != 0) 460 continue; 461 462 /* get MTU */ 463 dia.dia_linkid = linkid; 464 if (ioctl(dld_fd, DLDIOC_ATTR, &dia) < 0) 465 continue; 466 467 curr = calloc(1, sizeof (struct ifaddrs)); 468 if (curr == NULL) 469 goto fail; 470 471 if (prev != NULL) { 472 prev->ifa_next = curr; 473 } else { 474 /* First node in the linked list */ 475 *ifap = curr; 476 } 477 prev = curr; 478 479 if ((curr->ifa_name = strdup(name_retval.lr_link)) == 480 NULL) 481 goto fail; 482 483 /* 484 * We split this allocation and assignment to satisfy 485 * smatch. 486 */ 487 s = calloc(1, sizeof (struct sockaddr_storage)); 488 if (s == NULL) 489 goto fail; 490 curr->ifa_addr = (struct sockaddr *)s; 491 492 curr->ifa_data = calloc(1, sizeof (if_data_t)); 493 if (curr->ifa_data == NULL) 494 goto fail; 495 496 curr->ifa_addr->sa_family = AF_LINK; 497 ifa_addr = (struct sockaddr_dl *)curr->ifa_addr; 498 ifa_data = curr->ifa_data; 499 500 /* 501 * Place both the interface name and the address in 502 * sdl_data[] as long as both fit. The name will not 503 * include a NUL terminator in the data section. In 504 * general, both of these should fit due to the system 505 * constraints of a 31-character link name 506 * (MAXLINKNAMELEN). 507 * 508 * Verify both fit. If they don't, skip the interface 509 * name. 510 */ 511 len = strlen(curr->ifa_name); 512 if (len + dmip->dmi_addrlen <= 513 sizeof (ifa_addr->sdl_data)) { 514 ifa_addr->sdl_nlen = len; 515 (void) memcpy(ifa_addr->sdl_data, 516 curr->ifa_name, len); 517 } 518 ifa_addr->sdl_alen = dmip->dmi_addrlen; 519 (void) memcpy(LLADDR(ifa_addr), dmip->dmi_addr, 520 dmip->dmi_addrlen); 521 522 ifa_data->ifi_mtu = dia.dia_max_sdu; 523 ifa_data->ifi_type = dlpi_iftype(next_retval.lr_media); 524 ifa_data->ifi_addrlen = dmip->dmi_addrlen; 525 ifa_addr->sdl_type = ifa_data->ifi_type; 526 527 /* 528 * get interface index 529 * This is only possible if the link has been plumbed. 530 */ 531 if (strlcpy(lifrl.lifr_name, name_retval.lr_link, 532 sizeof (lifrl.lifr_name)) >= 533 sizeof (lifrl.lifr_name)) 534 continue; 535 536 if (ioctl(sock4, SIOCGLIFINDEX, (caddr_t)&lifrl) >= 0) { 537 ifa_addr->sdl_index = lifrl.lifr_index; 538 } else if (ioctl(sock6, SIOCGLIFINDEX, 539 (caddr_t)&lifrl) >= 0) { 540 /* retry for IPv6 */ 541 ifa_addr->sdl_index = lifrl.lifr_index; 542 } 543 } 544 } 545 nolink: 546 free(buf); 547 free(iomp); 548 (void) close(sock4); 549 (void) close(sock6); 550 if (door_fd >= 0) 551 (void) close(door_fd); 552 if (dld_fd >= 0) 553 (void) close(dld_fd); 554 return (0); 555 fail: 556 err = errno; 557 free(buf); 558 buf = NULL; 559 freeifaddrs(*ifap); 560 *ifap = NULL; 561 if (err == ENXIO) 562 goto retry; 563 free(iomp); 564 565 if (sock4 >= 0) 566 (void) close(sock4); 567 if (sock6 >= 0) 568 (void) close(sock6); 569 if (door_fd >= 0) 570 (void) close(door_fd); 571 if (dld_fd >= 0) 572 (void) close(dld_fd); 573 errno = err; 574 return (-1); 575 } 576 577 /* 578 * Do a SIOCGLIFCONF and store all the interfaces in `buf'. 579 */ 580 int 581 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs, 582 int64_t lifc_flags) 583 { 584 struct lifnum lifn; 585 struct lifconf lifc; 586 size_t bufsize; 587 char *tmp; 588 caddr_t *buf = (caddr_t *)lifr; 589 590 lifn.lifn_family = af; 591 lifn.lifn_flags = lifc_flags; 592 593 *buf = NULL; 594 retry: 595 if (ioctl(s, SIOCGLIFNUM, &lifn) < 0) 596 goto fail; 597 598 /* 599 * When calculating the buffer size needed, add a small number 600 * of interfaces to those we counted. We do this to capture 601 * the interface status of potential interfaces which may have 602 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. 603 */ 604 bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq); 605 606 if ((tmp = realloc(*buf, bufsize)) == NULL) 607 goto fail; 608 609 *buf = tmp; 610 lifc.lifc_family = af; 611 lifc.lifc_flags = lifc_flags; 612 lifc.lifc_len = bufsize; 613 lifc.lifc_buf = *buf; 614 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) 615 goto fail; 616 617 *numifs = lifc.lifc_len / sizeof (struct lifreq); 618 if (*numifs >= (lifn.lifn_count + 4)) { 619 /* 620 * If every entry was filled, there are probably 621 * more interfaces than (lifn.lifn_count + 4). 622 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to 623 * get all the interfaces. 624 */ 625 goto retry; 626 } 627 return (0); 628 fail: 629 free(*buf); 630 *buf = NULL; 631 return (-1); 632 } 633