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