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
_getifaddrs(struct ifaddrs ** ifap,boolean_t can_handle_links)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
getifaddrs_old(struct ifaddrs ** ifap)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
__getifaddrs(struct ifaddrs ** ifap)129 __getifaddrs(struct ifaddrs **ifap)
130 {
131 return (_getifaddrs(ifap, B_TRUE));
132 }
133
134 void
freeifaddrs(struct ifaddrs * ifa)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
dlpi_iftype(uint_t dlpitype)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
dl_door_call(int door_fd,void * arg,size_t asize,void * rbuf,size_t * rsizep)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
dl_get_name(int door_fd,datalink_id_t linkid,dlmgmt_getname_retval_t * name_retval)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
dl_get_next(int door_fd,datalink_id_t linkid,datalink_class_t class,datalink_media_t dmedia,uint32_t flags,dlmgmt_getnext_retval_t * next_retval)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
getallifaddrs(sa_family_t af,struct ifaddrs ** ifap,int64_t flags)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
getallifs(int s,sa_family_t af,struct lifreq ** lifr,int * numifs,int64_t lifc_flags)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