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 = malloc(bufsize)) == NULL) 328 goto fail; 329 330 retry: 331 bzero(iomp, bufsize); 332 /* Get all interfaces from SIOCGLIFCONF */ 333 ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED)); 334 if (ret != 0) 335 goto fail; 336 337 /* 338 * Loop through the interfaces obtained from SIOCGLIFCOMF 339 * and retrieve the addresses, netmask and flags. 340 */ 341 prev = NULL; 342 lifrp = buf; 343 for (n = 0; n < numifs; n++, lifrp++) { 344 345 /* Prepare for the ioctl call */ 346 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name, 347 sizeof (lifrl.lifr_name)); 348 lifr_af = lifrp->lifr_addr.ss_family; 349 if (af != AF_UNSPEC && lifr_af != af) 350 continue; 351 352 s = (lifr_af == AF_INET ? sock4 : sock6); 353 354 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) 355 goto fail; 356 if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP)) 357 continue; 358 359 /* 360 * Allocate the current list node. Each node contains data 361 * for one ifaddrs structure. 362 */ 363 curr = calloc(1, sizeof (struct ifaddrs)); 364 if (curr == NULL) 365 goto fail; 366 367 if (prev != NULL) { 368 prev->ifa_next = curr; 369 } else { 370 /* First node in the linked list */ 371 *ifap = curr; 372 } 373 prev = curr; 374 375 curr->ifa_flags = lifrl.lifr_flags; 376 if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL) 377 goto fail; 378 379 curr->ifa_addr = malloc(sizeof (struct sockaddr_storage)); 380 if (curr->ifa_addr == NULL) 381 goto fail; 382 (void) memcpy(curr->ifa_addr, &lifrp->lifr_addr, 383 sizeof (struct sockaddr_storage)); 384 385 /* Get the netmask */ 386 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0) 387 goto fail; 388 curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage)); 389 if (curr->ifa_netmask == NULL) 390 goto fail; 391 (void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr, 392 sizeof (struct sockaddr_storage)); 393 394 /* Get the destination for a pt-pt interface */ 395 if (curr->ifa_flags & IFF_POINTOPOINT) { 396 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0) 397 goto fail; 398 curr->ifa_dstaddr = malloc( 399 sizeof (struct sockaddr_storage)); 400 if (curr->ifa_dstaddr == NULL) 401 goto fail; 402 (void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr, 403 sizeof (struct sockaddr_storage)); 404 } else if (curr->ifa_flags & IFF_BROADCAST) { 405 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0) 406 goto fail; 407 curr->ifa_broadaddr = malloc( 408 sizeof (struct sockaddr_storage)); 409 if (curr->ifa_broadaddr == NULL) 410 goto fail; 411 (void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr, 412 sizeof (struct sockaddr_storage)); 413 } 414 415 } 416 417 /* add AF_LINK entries */ 418 if (af == AF_UNSPEC || af == AF_LINK) { 419 /* 420 * A datalink management door may not be available (for example 421 * in a shared IP zone). Only enumerate AF_LINK entries if the 422 * door exists. 423 */ 424 door_fd = open(DLMGMT_DOOR, O_RDONLY); 425 if (door_fd < 0) { 426 if (errno == ENOENT) 427 goto nolink; 428 goto fail; 429 } 430 if ((dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 431 goto fail; 432 433 linkid = DATALINK_INVALID_LINKID; 434 for (;;) { 435 if (dl_get_next(door_fd, linkid, DATALINK_CLASS_ALL, 436 DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE, 437 &next_retval) != 0) { 438 break; 439 } 440 441 linkid = next_retval.lr_linkid; 442 if (linkid == DATALINK_INVALID_LINKID) 443 break; 444 445 /* get mac addr */ 446 iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t); 447 iomp->dig_linkid = linkid; 448 449 if (ioctl(dld_fd, DLDIOC_MACADDRGET, iomp) < 0) 450 continue; 451 452 dmip = (dld_macaddrinfo_t *)(iomp + 1); 453 454 /* get name */ 455 if (dl_get_name(door_fd, linkid, &name_retval) != 0) 456 continue; 457 458 /* get MTU */ 459 dia.dia_linkid = linkid; 460 if (ioctl(dld_fd, DLDIOC_ATTR, &dia) < 0) 461 continue; 462 463 curr = calloc(1, sizeof (struct ifaddrs)); 464 if (curr == NULL) 465 goto fail; 466 467 if (prev != NULL) { 468 prev->ifa_next = curr; 469 } else { 470 /* First node in the linked list */ 471 *ifap = curr; 472 } 473 prev = curr; 474 475 if ((curr->ifa_name = strdup(name_retval.lr_link)) == 476 NULL) 477 goto fail; 478 479 curr->ifa_addr = 480 calloc(1, sizeof (struct sockaddr_storage)); 481 if (curr->ifa_addr == NULL) 482 goto fail; 483 484 curr->ifa_data = calloc(1, sizeof (if_data_t)); 485 if (curr->ifa_data == NULL) 486 goto fail; 487 488 curr->ifa_addr->sa_family = AF_LINK; 489 ifa_addr = (struct sockaddr_dl *)curr->ifa_addr; 490 ifa_data = curr->ifa_data; 491 492 (void) memcpy(ifa_addr->sdl_data, dmip->dmi_addr, 493 dmip->dmi_addrlen); 494 ifa_addr->sdl_alen = dmip->dmi_addrlen; 495 496 ifa_data->ifi_mtu = dia.dia_max_sdu; 497 ifa_data->ifi_type = dlpi_iftype(next_retval.lr_media); 498 499 /* 500 * get interface index 501 * This is only possible if the link has been plumbed. 502 */ 503 if (strlcpy(lifrl.lifr_name, name_retval.lr_link, 504 sizeof (lifrl.lifr_name)) >= 505 sizeof (lifrl.lifr_name)) 506 continue; 507 508 if (ioctl(sock4, SIOCGLIFINDEX, (caddr_t)&lifrl) >= 0) { 509 ifa_addr->sdl_index = lifrl.lifr_index; 510 } else if (ioctl(sock6, SIOCGLIFINDEX, 511 (caddr_t)&lifrl) >= 0) { 512 /* retry for IPv6 */ 513 ifa_addr->sdl_index = lifrl.lifr_index; 514 } 515 } 516 } 517 nolink: 518 free(buf); 519 free(iomp); 520 (void) close(sock4); 521 (void) close(sock6); 522 if (door_fd >= 0) 523 (void) close(door_fd); 524 if (dld_fd >= 0) 525 (void) close(dld_fd); 526 return (0); 527 fail: 528 err = errno; 529 free(buf); 530 buf = NULL; 531 freeifaddrs(*ifap); 532 *ifap = NULL; 533 if (err == ENXIO) 534 goto retry; 535 free(iomp); 536 537 if (sock4 >= 0) 538 (void) close(sock4); 539 if (sock6 >= 0) 540 (void) close(sock6); 541 if (door_fd >= 0) 542 (void) close(door_fd); 543 if (dld_fd >= 0) 544 (void) close(dld_fd); 545 errno = err; 546 return (-1); 547 } 548 549 /* 550 * Do a SIOCGLIFCONF and store all the interfaces in `buf'. 551 */ 552 int 553 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs, 554 int64_t lifc_flags) 555 { 556 struct lifnum lifn; 557 struct lifconf lifc; 558 size_t bufsize; 559 char *tmp; 560 caddr_t *buf = (caddr_t *)lifr; 561 562 lifn.lifn_family = af; 563 lifn.lifn_flags = lifc_flags; 564 565 *buf = NULL; 566 retry: 567 if (ioctl(s, SIOCGLIFNUM, &lifn) < 0) 568 goto fail; 569 570 /* 571 * When calculating the buffer size needed, add a small number 572 * of interfaces to those we counted. We do this to capture 573 * the interface status of potential interfaces which may have 574 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. 575 */ 576 bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq); 577 578 if ((tmp = realloc(*buf, bufsize)) == NULL) 579 goto fail; 580 581 *buf = tmp; 582 lifc.lifc_family = af; 583 lifc.lifc_flags = lifc_flags; 584 lifc.lifc_len = bufsize; 585 lifc.lifc_buf = *buf; 586 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) 587 goto fail; 588 589 *numifs = lifc.lifc_len / sizeof (struct lifreq); 590 if (*numifs >= (lifn.lifn_count + 4)) { 591 /* 592 * If every entry was filled, there are probably 593 * more interfaces than (lifn.lifn_count + 4). 594 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to 595 * get all the interfaces. 596 */ 597 goto retry; 598 } 599 return (0); 600 fail: 601 free(*buf); 602 *buf = NULL; 603 return (-1); 604 } 605