1/*
2 * Copyright (c) 1999-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * interfaces.c
25 * - get the list of interfaces in the system
26 */
27
28/*
29 * Modification History
30 * 02/23/98	Dieter Siegmund (dieter@apple.com)
31 * - initial version
32 */
33
34#include <stdio.h>
35#include <unistd.h>
36#include <stdlib.h>
37#include <sys/ioctl.h>
38#include <sys/types.h>
39#include <strings.h>
40#include <netdb.h>
41#include "interfaces.h"
42#include <arpa/inet.h>
43#include <net/if_types.h>
44#include <net/if_var.h>
45#include <sys/socket.h>
46#include <sys/sysctl.h>
47#include <net/if_media.h>
48#include <net/route.h>
49#include <ifaddrs.h>
50
51#include "util.h"
52#include "IPConfigurationLog.h"
53
54static boolean_t
55S_get_ifmediareq(const char * name, struct ifmediareq * ifmr_p);
56
57static boolean_t
58S_get_ifmediareq_s(int s, const char * name, struct ifmediareq * ifmr_p);
59
60static boolean_t
61S_is_awdl(int s, const char * name);
62
63static boolean_t
64S_ifmediareq_get_is_wireless(struct ifmediareq * ifmr_p);
65
66static link_status_t
67S_ifmediareq_get_link_status(struct ifmediareq * ifmr_p);
68
69void *
70inet_addrinfo_copy(void * p)
71{
72    inet_addrinfo_t * src = (inet_addrinfo_t *)p;
73    inet_addrinfo_t * dest;
74
75    dest = malloc(sizeof(*dest));
76    if (dest) {
77	bcopy(src, dest, sizeof(*dest));
78    }
79    return (dest);
80}
81
82void
83inet_addrinfo_free(void * p)
84{
85    free(p);
86    return;
87}
88
89static interface_t *
90S_next_entry(interface_list_t * interfaces, const char * name)
91{
92    interface_t * entry;
93
94    if (interfaces->count >= interfaces->size)
95	return (NULL);
96
97    entry = interfaces->list + interfaces->count++;
98    bzero(entry, sizeof(*entry));
99    strlcpy(entry->name, name, sizeof(entry->name));
100    dynarray_init(&entry->inet, inet_addrinfo_free,
101		  inet_addrinfo_copy);
102    return (entry);
103}
104
105static __inline__ int
106count_ifaddrs(const struct ifaddrs * ifap)
107{
108    int		count;
109
110    for (count = 0; ifap != NULL && ifap->ifa_addr != NULL;
111	 ifap = ifap->ifa_next) {
112	count++;
113    }
114    return (count);
115}
116
117static boolean_t
118S_build_interface_list(interface_list_t * interfaces)
119{
120    struct ifaddrs *	addrs = NULL;
121    struct ifaddrs *	ifap = NULL;
122    struct ifmediareq	ifmr;
123    int			s = -1;
124    int			size;
125    boolean_t		success = FALSE;
126
127    if (getifaddrs(&addrs) < 0) {
128	goto done;
129    }
130    size = count_ifaddrs(addrs);
131    interfaces->list
132	= (interface_t *)malloc(size * sizeof(*(interfaces->list)));
133    if (interfaces->list == NULL) {
134	goto done;
135    }
136
137    s = socket(AF_INET, SOCK_DGRAM, 0);
138    if (s < 0) {
139	goto done;
140    }
141    interfaces->count = 0;
142    interfaces->size = size;
143
144    for (ifap = addrs; ifap != NULL; ifap = ifap->ifa_next) {
145	const char *	name;
146	if (ifap->ifa_addr == NULL) {
147	    continue;
148	}
149	name = ifap->ifa_name;
150	switch (ifap->ifa_addr->sa_family) {
151	  case AF_INET: {
152	      inet_addrinfo_t	info;
153	      interface_t *	entry;
154
155	      entry = ifl_find_name(interfaces, name);
156	      if (entry == NULL) { /* new entry */
157		  entry = S_next_entry(interfaces, name);
158		  if (entry == NULL) {
159		      /* NOT REACHED */
160		      IPConfigLog(LOG_ERR,
161				  "interfaces: S_next_entry returns NULL");
162		      continue;
163		  }
164		  entry->flags = ifap->ifa_flags;
165	      }
166	      bzero(&info, sizeof(info));
167
168	      /* ALIGN: getifaddrs should align, cast ok. */
169	      info.addr = ((struct sockaddr_in *)
170			   (void *)ifap->ifa_addr)->sin_addr;
171
172	      if (ifap->ifa_netmask != NULL) {
173		  /* ALIGN: getifaddrs should align, cast ok. */
174		  info.mask
175		      = ((struct sockaddr_in *)
176			 (void *)ifap->ifa_netmask)->sin_addr;
177	      }
178	      if (entry->flags & IFF_BROADCAST && ifap->ifa_broadaddr != NULL) {
179		  /* ALIGN: getifaddrs should align, cast ok. */
180		  info.broadcast
181		      = ((struct sockaddr_in *)(void *)
182				ifap->ifa_broadaddr)->sin_addr;
183	      }
184	      info.netaddr.s_addr = htonl(iptohl(info.addr)
185					  & iptohl(info.mask));
186	      dynarray_add(&entry->inet, inet_addrinfo_copy(&info));
187	      break;
188	  }
189	  case AF_LINK: {
190	      struct sockaddr_dl * dl_p;
191	      interface_t *	entry;
192	      struct if_data *	if_data;
193
194	      /* ALIGN: getifaddrs should align, cast ok. */
195	      dl_p = (struct sockaddr_dl *)
196		     (void *)ifap->ifa_addr;
197
198	      entry = ifl_find_name(interfaces, name);
199	      if (entry == NULL) { /* new entry */
200		  entry = S_next_entry(interfaces, name);
201		  if (entry == NULL) {
202		      /* NOT REACHED */
203		      IPConfigLog(LOG_ERR,
204				  "interfaces: S_next_entry returns NULL");
205		      continue;
206		  }
207		  entry->flags = ifap->ifa_flags;
208	      }
209	      if (dl_p->sdl_alen > sizeof(entry->link_address.addr)) {
210		  IPConfigLog(LOG_ERR,
211			      "%s: link type %d address length %d > %ld", name,
212			      dl_p->sdl_type, dl_p->sdl_alen,
213			      sizeof(entry->link_address.addr));
214		  entry->link_address.length = sizeof(entry->link_address.addr);
215	      }
216	      else {
217		  entry->link_address.length = dl_p->sdl_alen;
218	      }
219	      bcopy(dl_p->sdl_data + dl_p->sdl_nlen,
220		    entry->link_address.addr,
221		    entry->link_address.length);
222	      entry->link_address.type = dl_p->sdl_type;
223	      entry->link_address.index = dl_p->sdl_index;
224	      if_data = (struct if_data *)ifap->ifa_data;
225	      if (if_data != NULL) {
226		  entry->type = if_data->ifi_type;
227	      }
228	      else {
229		  entry->type = dl_p->sdl_type;
230	      }
231	      if (S_get_ifmediareq_s(s, name, &ifmr)) {
232		  if (entry->type == IFT_ETHER) {
233
234		      if (S_ifmediareq_get_is_wireless(&ifmr)) {
235			  entry->type_flags |= kInterfaceTypeFlagIsWireless;
236			  if (S_is_awdl(s, name)) {
237			      entry->type_flags |= kInterfaceTypeFlagIsAWDL;
238			  }
239		      }
240		  }
241		  entry->link_status
242		      = S_ifmediareq_get_link_status(&ifmr);
243	      }
244	      break;
245	  }
246	}
247    }
248    /* make it the "right" size (plus 1 more in case it's 0) */
249    interfaces->list = (interface_t *)
250	realloc(interfaces->list,
251		sizeof(*(interfaces->list)) * (interfaces->count + 1));
252    success = TRUE;
253
254 done:
255    if (addrs != NULL) {
256	freeifaddrs(addrs);
257    }
258    if (success == FALSE) {
259	if (interfaces->list != NULL) {
260	    free(interfaces->list);
261	}
262	interfaces->list = NULL;
263    }
264    if (s >= 0) {
265	close(s);
266    }
267    return (success);
268}
269
270int
271ifl_count(interface_list_t * list_p)
272{
273    return (list_p->count);
274}
275
276interface_t *
277ifl_at_index(interface_list_t * list_p, int i)
278{
279    if (i >= list_p->count || i < 0)
280	return (NULL);
281    return (list_p->list + i);
282}
283
284int
285ifl_index(interface_list_t * list_p, interface_t * if_p)
286{
287    return (if_p - list_p->list);
288}
289
290/*
291 * Function: ifl_first_broadcast_inet
292 *
293 * Purpose:
294 *   Return the first non-loopback, broadcast capable interface.
295 */
296interface_t *
297ifl_first_broadcast_inet(interface_list_t * list_p)
298{
299    int i;
300    for (i = 0; i < list_p->count; i++) {
301	interface_t * if_p = list_p->list + i;
302
303	if (dynarray_count(&if_p->inet) > 0
304	    && !(if_p->flags & IFF_LOOPBACK)
305	    && (if_p->flags & IFF_BROADCAST))
306	    return (list_p->list + i);
307    }
308    return (NULL);
309}
310
311interface_t *
312ifl_find_ip(interface_list_t * list_p, struct in_addr iaddr)
313{
314    int 	i;
315
316    for (i = 0; i < list_p->count; i++) {
317	interface_t * if_p = list_p->list + i;
318	int j;
319
320	for (j = 0; j < dynarray_count(&if_p->inet); j++) {
321	    inet_addrinfo_t *	info;
322
323	    info = dynarray_element(&if_p->inet, j);
324	    if (info->addr.s_addr == iaddr.s_addr)
325		return (if_p);
326	}
327    }
328    return (NULL);
329}
330
331interface_t *
332ifl_find_subnet(interface_list_t * list_p, struct in_addr iaddr)
333{
334    int 	i;
335    u_long	addr_hl = iptohl(iaddr);
336
337    for (i = 0; i < list_p->count; i++) {
338	interface_t * if_p = list_p->list + i;
339	int j;
340
341	for (j = 0; j < dynarray_count(&if_p->inet); j++) {
342	    inet_addrinfo_t *	info = dynarray_element(&if_p->inet, j);
343	    u_long 		ifnetaddr_hl = iptohl(info->netaddr);
344	    u_long 		ifmask_hl = iptohl(info->mask);
345
346	    if ((addr_hl & ifmask_hl) == ifnetaddr_hl)
347		return (if_p);
348	}
349    }
350    return (NULL);
351}
352
353interface_t *
354ifl_find_name(interface_list_t * list_p, const char * name)
355{
356    int i;
357
358    for (i = 0; i < list_p->count; i++) {
359	if (strcmp(list_p->list[i].name, name) == 0)
360	    return (list_p->list + i);
361    }
362    return (NULL);
363}
364
365interface_t *
366ifl_find_link(interface_list_t * list_p, int index)
367{
368    int i;
369
370    for (i = 0; i < list_p->count; i++) {
371	if (list_p->list[i].link_address.type != 0
372	    && list_p->list[i].link_address.index == index)
373	    return (list_p->list + i);
374    }
375    return (NULL);
376}
377
378interface_list_t *
379ifl_init()
380{
381    interface_list_t * list_p = (interface_list_t *)malloc(sizeof(*list_p));
382    if (list_p == NULL
383	|| S_build_interface_list(list_p) == FALSE) {
384	if (list_p)
385	    free(list_p);
386	return (NULL);
387    }
388    return (list_p);
389}
390
391void
392ifl_free(interface_list_t * * iflist)
393{
394    if (iflist != NULL && *iflist != NULL) {
395	int 			i;
396	interface_list_t * 	list_p = *iflist;
397
398	for (i = 0; i < list_p->count; i++) {
399	    dynarray_free(&list_p->list[i].inet);
400	}
401	if (list_p->list)
402	    free(list_p->list);
403	free(list_p);
404	*iflist = NULL;
405    }
406    return;
407}
408
409/*
410 * Functions: if_*
411 * Purpose:
412 *   Interface-specific routines.
413 */
414const char *
415if_name(interface_t * if_p)
416{
417    return (if_p->name);
418}
419
420uint16_t
421if_flags(interface_t * if_p)
422{
423    return (if_p->flags);
424}
425
426int
427if_inet_count(interface_t * if_p)
428{
429    return (dynarray_count(&if_p->inet));
430}
431
432inet_addrinfo_t *
433if_inet_addr_at(interface_t * if_p, int i)
434{
435    return (dynarray_element(&if_p->inet, i));
436}
437
438/* get the primary address, mask, broadcast */
439struct in_addr
440if_inet_addr(interface_t * if_p)
441{
442    inet_addrinfo_t * 	info = if_inet_addr_at(if_p, 0);
443
444    if (info == NULL) {
445	struct in_addr	zeroes = { 0 };
446	return (zeroes);
447    }
448    return (info->addr);
449}
450
451struct in_addr
452if_inet_netmask(interface_t * if_p)
453{
454    inet_addrinfo_t * info = if_inet_addr_at(if_p, 0);
455    return (info->mask);
456}
457
458struct in_addr
459if_inet_netaddr(interface_t * if_p)
460{
461    inet_addrinfo_t * info = if_inet_addr_at(if_p, 0);
462    return (info->netaddr);
463}
464
465struct in_addr
466if_inet_broadcast(interface_t * if_p)
467{
468    inet_addrinfo_t * info = if_inet_addr_at(if_p, 0);
469    return (info->broadcast);
470}
471
472void
473if_setflags(interface_t * if_p, uint16_t flags)
474{
475    if_p->flags = flags;
476    return;
477}
478
479boolean_t
480if_inet_valid(interface_t * if_p)
481{
482    return (dynarray_count(&if_p->inet) > 0);
483}
484
485void
486if_free(interface_t * * if_p_p)
487{
488    interface_t * if_p;
489
490    if (if_p_p == NULL) {
491	return;
492    }
493    if_p = *if_p_p;
494    if (if_p == NULL) {
495	return;
496    }
497    dynarray_free(&if_p->inet);
498    free(if_p);
499    *if_p_p = NULL;
500    return;
501}
502
503interface_t *
504if_dup(interface_t * intface)
505{
506    interface_t * new_p;
507
508    new_p = (interface_t *)calloc(1, sizeof(*new_p));
509    if (new_p == NULL) {
510	return (NULL);
511    }
512    *new_p = *intface;
513    (void)dynarray_dup(&new_p->inet, &intface->inet);
514    return (new_p);
515}
516
517int
518if_inet_find_ip(interface_t * if_p, struct in_addr iaddr)
519{
520    int i;
521    for (i = 0; i < if_inet_count(if_p); i++) {
522	inet_addrinfo_t *	info = if_inet_addr_at(if_p, i);
523	if (info->addr.s_addr == iaddr.s_addr) {
524	    return (i);
525	}
526    }
527    return (INDEX_BAD);
528}
529
530void
531if_link_copy(interface_t * dest, const interface_t * source)
532{
533    dest->link_status = source->link_status;
534    dest->link_address = source->link_address;
535    return;
536}
537
538boolean_t
539if_link_update(interface_t * if_p)
540{
541    void *			addr;
542    int				addr_length;
543    char *			buf = NULL;
544    size_t			buf_len = 0;
545    boolean_t			changed = FALSE;
546    struct sockaddr_dl *	dl_p;
547    struct if_msghdr * 		ifm;
548    int				mib[6];
549    int				type;
550
551    mib[0] = CTL_NET;
552    mib[1] = PF_ROUTE;
553    mib[2] = 0;
554    mib[3] = AF_LINK;
555    mib[4] = NET_RT_IFLIST;
556    mib[5] = if_p->link_address.index; /* ask for exactly one interface */
557
558    if (sysctl(mib, 6, NULL, &buf_len, NULL, 0) < 0) {
559	fprintf(stderr, "sysctl() size failed: %s", strerror(errno));
560	goto failed;
561    }
562    buf = malloc(buf_len);
563    if (sysctl(mib, 6, buf, &buf_len, NULL, 0) < 0) {
564	fprintf(stderr, "sysctl() failed: %s", strerror(errno));
565	goto failed;
566    }
567    /* ALIGN: buf is aligned (from malloc), cast ok. */
568    ifm = (struct if_msghdr *)(void *)buf;
569    switch (ifm->ifm_type) {
570    case RTM_IFINFO:
571	dl_p = (struct sockaddr_dl *)(ifm + 1);
572	addr = dl_p->sdl_data + dl_p->sdl_nlen;
573	addr_length = dl_p->sdl_alen;
574	type = dl_p->sdl_type;
575	if (addr_length > sizeof(if_p->link_address.addr)) {
576	    IPConfigLog(LOG_DEBUG,
577			"%s: link type %d address length %d > %ld",
578			if_name(if_p),
579			type,
580			addr_length,
581			sizeof(if_p->link_address.addr));
582	    addr_length = sizeof(if_p->link_address.addr);
583	}
584	if (if_p->link_address.type != type
585	    || addr_length != if_p->link_address.length
586	    || (addr_length != 0
587		&& bcmp(addr, if_p->link_address.addr, addr_length))) {
588	    changed = TRUE;
589	    if_p->link_address.length = addr_length;
590	    bcopy(addr, if_p->link_address.addr, addr_length);
591	    if_p->link_address.type = dl_p->sdl_type;
592	}
593    }
594 failed:
595    if (buf != NULL) {
596	free(buf);
597    }
598    return (changed);
599}
600
601int
602if_ift_type(interface_t * if_p)
603{
604    return (if_p->type);
605}
606
607int
608if_link_type(interface_t * if_p)
609{
610    return (if_p->link_address.type);
611}
612
613int
614if_link_dhcptype(interface_t * if_p)
615{
616    if (if_p->link_address.type == IFT_IEEE1394) {
617	return (ARPHRD_IEEE1394_EUI64);
618    }
619    else {
620	return (dl_to_arp_hwtype(if_p->link_address.type));
621    }
622}
623
624int
625if_link_arptype(interface_t * if_p)
626{
627    return (dl_to_arp_hwtype(if_p->link_address.type));
628}
629
630
631void *
632if_link_address(interface_t * if_p)
633{
634    return (if_p->link_address.addr);
635}
636
637int
638if_link_length(interface_t * if_p)
639{
640    return (if_p->link_address.length);
641}
642
643boolean_t
644if_is_wireless(interface_t * if_p)
645{
646    return ((if_p->type_flags & kInterfaceTypeFlagIsWireless) != 0);
647}
648
649boolean_t
650if_is_awdl(interface_t * if_p)
651{
652    return ((if_p->type_flags & kInterfaceTypeFlagIsAWDL) != 0);
653}
654
655int
656if_link_index(interface_t * if_p)
657{
658    return (if_p->link_address.index);
659}
660
661link_status_t
662if_get_link_status(interface_t * if_p)
663{
664    return (if_p->link_status);
665}
666
667static int
668siocgifmedia(int sockfd, struct ifmediareq * ifmr_p,
669	     const char * name)
670{
671    (void)memset(ifmr_p, 0, sizeof(*ifmr_p));
672    (void)strlcpy(ifmr_p->ifm_name, name, sizeof(ifmr_p->ifm_name));
673    return (ioctl(sockfd, SIOCGIFMEDIA, (caddr_t)ifmr_p));
674}
675
676static boolean_t
677S_get_ifmediareq_s(int s, const char * name, struct ifmediareq * ifmr_p)
678{
679    boolean_t	ret;
680
681    if (siocgifmedia(s, ifmr_p, name) == -1) {
682	if (errno == EOPNOTSUPP) {
683	    ifmr_p->ifm_status = IFM_ACTIVE | IFM_AVALID;
684    	    ifmr_p->ifm_count = 1;
685	    ret = TRUE;
686	}
687	else {
688	    ret = FALSE;
689	}
690    }
691    else {
692	ret = TRUE;
693    }
694    return (ret);
695}
696
697static boolean_t
698S_get_ifmediareq(const char * name, struct ifmediareq * ifmr_p)
699{
700    int		s;
701    boolean_t	success = FALSE;
702
703    s = socket(AF_INET, SOCK_DGRAM, 0);
704    if (s >= 0) {
705	success = S_get_ifmediareq_s(s, name, ifmr_p);
706	close(s);
707    }
708    return (success);
709}
710
711
712static boolean_t
713S_ifmediareq_get_is_wireless(struct ifmediareq * ifmr_p)
714{
715    return (IFM_TYPE(ifmr_p->ifm_current) == IFM_IEEE80211);
716}
717
718static link_status_t
719S_ifmediareq_get_link_status(struct ifmediareq * ifmr_p)
720{
721    link_status_t 	link;
722
723    bzero(&link, sizeof(link));
724    if (ifmr_p->ifm_count > 0 && (ifmr_p->ifm_status & IFM_AVALID) != 0) {
725	link.valid = TRUE;
726	if ((ifmr_p->ifm_status & IFM_ACTIVE) != 0) {
727	    link.active = TRUE;
728	}
729	link.wake_on_same_network
730	    = ((ifmr_p->ifm_status & IFM_WAKESAMENET) != 0);
731    }
732    return (link);
733}
734
735static int
736siocgifeflags(int sockfd, struct ifreq * ifr, const char * name)
737{
738    (void)memset(ifr, 0, sizeof(*ifr));
739    (void)strlcpy(ifr->ifr_name, name, sizeof(ifr->ifr_name));
740    return (ioctl(sockfd, SIOCGIFEFLAGS, (caddr_t)ifr));
741}
742
743static boolean_t
744S_is_awdl(int sockfd, const char * name)
745{
746    struct ifreq	ifr;
747    boolean_t		is_awdl = FALSE;
748
749    if (siocgifeflags(sockfd, &ifr, name) == -1) {
750	if (errno != ENXIO && errno != EPWROFF && errno != EINVAL) {
751	    IPConfigLogFL(LOG_NOTICE,
752			  "%s: SIOCGIFEFLAGS failed status, %s",
753			  name, strerror(errno));
754	}
755    }
756    else if ((ifr.ifr_eflags & IFEF_AWDL) != 0) {
757	is_awdl = TRUE;
758    }
759    return (is_awdl);
760}
761
762link_status_t
763if_link_status_update(interface_t * if_p)
764{
765    struct ifmediareq 		ifmr;
766
767    if (S_get_ifmediareq(if_name(if_p), &ifmr) == FALSE) {
768	if (errno != ENXIO && errno != EPWROFF && errno != EINVAL) {
769	    IPConfigLogFL(LOG_NOTICE,
770			  "%s: failed to get media status, %s",
771			  if_name(if_p), strerror(errno));
772	}
773    }
774    else {
775	if_p->link_status = S_ifmediareq_get_link_status(&ifmr);
776    }
777    return (if_p->link_status);
778}
779
780
781#ifdef TEST_INTERFACES
782
783#if 0
784void
785sockaddr_dl_print(struct sockaddr_dl * dl_p)
786{
787    int i;
788
789    printf("link: len %d index %d family %d type 0x%x nlen %d alen %d"
790	   " slen %d addr ", dl_p->sdl_len,
791	   dl_p->sdl_index,  dl_p->sdl_family, dl_p->sdl_type,
792	   dl_p->sdl_nlen, dl_p->sdl_alen, dl_p->sdl_slen);
793    for (i = 0; i < dl_p->sdl_alen; i++)
794	printf("%s%x", i ? ":" : "",
795	       ((unsigned char *)dl_p->sdl_data + dl_p->sdl_nlen)[i]);
796    printf("\n");
797}
798#endif
799
800void
801link_addr_print(link_addr_t * link)
802{
803    int i;
804
805    printf("link: index %d type 0x%x alen %d%s", link->index, link->type,
806	   link->length, link->length > 0 ? " addr" : "");
807    for (i = 0; i < link->length; i++) {
808	printf("%c%x", i ? ':' : ' ', link->addr[i]);
809    }
810    printf("\n");
811}
812
813void
814ifl_print(interface_list_t * list_p)
815{
816    int i;
817    int count = 0;
818
819    printf("Interface count = %d\n", list_p->count);
820    for (i = 0; i < list_p->count; i++) {
821	interface_t * 	if_p = list_p->list + i;
822	int		j;
823
824	if (i > 0)
825	    printf("\n");
826
827	printf("%s: type %d\n", if_name(if_p), if_ift_type(if_p));
828
829	for (j = 0; j < if_inet_count(if_p); j++) {
830	    inet_addrinfo_t * info = if_inet_addr_at(if_p, j);
831
832	    printf("inet: %s", inet_ntoa(info->addr));
833	    printf(" netmask %s", inet_ntoa(info->mask));
834	    if (if_flags(if_p) & IFF_BROADCAST)
835		printf(" %s\n", inet_ntoa(info->broadcast));
836	    else
837		printf("\n");
838	}
839	if (if_p->link_address.type != 0) {
840	    link_addr_print(&if_p->link_address);
841	    if (if_p->link_status.valid) {
842		printf("Link is %s%s\n",
843		       if_p->link_status.active ? "active" : "inactive",
844		       if_p->link_status.wake_on_same_network
845		       ? " [wake on same network]" : "");
846	    }
847	    if (if_is_wireless(if_p)) {
848		printf("wireless%s\n",
849		       if_is_awdl(if_p) ? " AWDL" : "");
850	    }
851	}
852	count++;
853    }
854    return;
855}
856
857int
858main()
859{
860    interface_list_t * list_p = ifl_init();
861    if (list_p != NULL) {
862	ifl_print(list_p);
863    }
864    exit(0);
865}
866#endif /* TEST_INTERFACES */
867