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 (door_fd = open(DLMGMT_DOOR, O_RDONLY)) < 0 || 323 (dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 324 goto fail; 325 326 bufsize = sizeof (dld_ioc_macaddrget_t) + nmacaddr * 327 sizeof (dld_macaddrinfo_t); 328 if ((iomp = calloc(1, bufsize)) == NULL) 329 goto fail; 330 331 retry: 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 linkid = DATALINK_INVALID_LINKID; 421 for (;;) { 422 if (dl_get_next(door_fd, linkid, DATALINK_CLASS_ALL, 423 DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE, 424 &next_retval) != 0) { 425 break; 426 } 427 428 linkid = next_retval.lr_linkid; 429 if (linkid == DATALINK_INVALID_LINKID) 430 break; 431 432 /* get mac addr */ 433 iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t); 434 iomp->dig_linkid = linkid; 435 436 if (ioctl(dld_fd, DLDIOC_MACADDRGET, iomp) < 0) 437 continue; 438 439 dmip = (dld_macaddrinfo_t *)(iomp + 1); 440 441 /* get name */ 442 if (dl_get_name(door_fd, linkid, &name_retval) != 0) 443 continue; 444 445 /* get MTU */ 446 dia.dia_linkid = linkid; 447 if (ioctl(dld_fd, DLDIOC_ATTR, &dia) < 0) 448 continue; 449 450 curr = calloc(1, sizeof (struct ifaddrs)); 451 if (curr == NULL) 452 goto fail; 453 454 if (prev != NULL) { 455 prev->ifa_next = curr; 456 } else { 457 /* First node in the linked list */ 458 *ifap = curr; 459 } 460 prev = curr; 461 462 if ((curr->ifa_name = strdup(name_retval.lr_link)) == 463 NULL) 464 goto fail; 465 466 curr->ifa_addr = 467 calloc(1, sizeof (struct sockaddr_storage)); 468 if (curr->ifa_addr == NULL) 469 goto fail; 470 471 curr->ifa_data = calloc(1, sizeof (if_data_t)); 472 if (curr->ifa_data == NULL) 473 goto fail; 474 475 curr->ifa_addr->sa_family = AF_LINK; 476 ifa_addr = (struct sockaddr_dl *)curr->ifa_addr; 477 ifa_data = curr->ifa_data; 478 479 (void) memcpy(ifa_addr->sdl_data, dmip->dmi_addr, 480 dmip->dmi_addrlen); 481 ifa_addr->sdl_alen = dmip->dmi_addrlen; 482 483 ifa_data->ifi_mtu = dia.dia_max_sdu; 484 ifa_data->ifi_type = dlpi_iftype(next_retval.lr_media); 485 486 /* 487 * get interface index 488 * This is only possible if the link has been plumbed. 489 */ 490 if (strlcpy(lifrl.lifr_name, name_retval.lr_link, 491 sizeof (lifrl.lifr_name)) >= 492 sizeof (lifrl.lifr_name)) 493 continue; 494 495 if (ioctl(sock4, SIOCGLIFINDEX, (caddr_t)&lifrl) >= 0) { 496 ifa_addr->sdl_index = lifrl.lifr_index; 497 } else if (ioctl(sock6, SIOCGLIFINDEX, 498 (caddr_t)&lifrl) >= 0) { 499 /* retry for IPv6 */ 500 ifa_addr->sdl_index = lifrl.lifr_index; 501 } 502 } 503 } 504 free(buf); 505 free(iomp); 506 (void) close(sock4); 507 (void) close(sock6); 508 (void) close(door_fd); 509 (void) close(dld_fd); 510 return (0); 511 fail: 512 err = errno; 513 free(buf); 514 free(iomp); 515 freeifaddrs(*ifap); 516 *ifap = NULL; 517 if (err == ENXIO) 518 goto retry; 519 520 if (sock4 != -1) 521 (void) close(sock4); 522 if (sock6 != -1) 523 (void) close(sock6); 524 if (door_fd != -1) 525 (void) close(door_fd); 526 if (dld_fd != -1) 527 (void) close(dld_fd); 528 errno = err; 529 return (-1); 530 } 531 532 /* 533 * Do a SIOCGLIFCONF and store all the interfaces in `buf'. 534 */ 535 int 536 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs, 537 int64_t lifc_flags) 538 { 539 struct lifnum lifn; 540 struct lifconf lifc; 541 size_t bufsize; 542 char *tmp; 543 caddr_t *buf = (caddr_t *)lifr; 544 545 lifn.lifn_family = af; 546 lifn.lifn_flags = lifc_flags; 547 548 *buf = NULL; 549 retry: 550 if (ioctl(s, SIOCGLIFNUM, &lifn) < 0) 551 goto fail; 552 553 /* 554 * When calculating the buffer size needed, add a small number 555 * of interfaces to those we counted. We do this to capture 556 * the interface status of potential interfaces which may have 557 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. 558 */ 559 bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq); 560 561 if ((tmp = realloc(*buf, bufsize)) == NULL) 562 goto fail; 563 564 *buf = tmp; 565 lifc.lifc_family = af; 566 lifc.lifc_flags = lifc_flags; 567 lifc.lifc_len = bufsize; 568 lifc.lifc_buf = *buf; 569 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) 570 goto fail; 571 572 *numifs = lifc.lifc_len / sizeof (struct lifreq); 573 if (*numifs >= (lifn.lifn_count + 4)) { 574 /* 575 * If every entry was filled, there are probably 576 * more interfaces than (lifn.lifn_count + 4). 577 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to 578 * get all the interfaces. 579 */ 580 goto retry; 581 } 582 return (0); 583 fail: 584 free(*buf); 585 *buf = NULL; 586 return (-1); 587 } 588