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