1/*
2 * Copyright (c) 2001-2002, 2011 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
25/*
26 * LinkAddresses.c
27 * - get the list of link-level addresses
28 */
29/*
30 * Modification History
31 *
32 * November 1, 2001		Dieter Siegmund (dieter@apple.com)
33 * - created
34 */
35
36#include <stdio.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <sys/types.h>
40#include <sys/ioctl.h>
41#include <sys/sysctl.h>
42#include <sys/socket.h>
43#include <sys/filio.h>
44#include <errno.h>
45#include <net/if.h>
46#include <net/if_dl.h>
47#include <net/if_media.h>
48#include <net/route.h>
49#include <string.h>
50
51#include "LinkAddresses.h"
52
53struct LinkAddresses_s {
54    struct sockaddr_dl * *	list;
55    int				count;
56};
57
58void
59sockaddr_dl_print(struct sockaddr_dl * dl_p)
60{
61    int i;
62
63    printf("link: len %d index %d family %d type 0x%x nlen %d alen %d"
64	   " slen %d addr ", dl_p->sdl_len,
65	   dl_p->sdl_index,  dl_p->sdl_family, dl_p->sdl_type,
66	   dl_p->sdl_nlen, dl_p->sdl_alen, dl_p->sdl_slen);
67    for (i = 0; i < dl_p->sdl_alen; i++)
68	printf("%s%x", i ? ":" : "",
69	       ((unsigned char *)dl_p->sdl_data + dl_p->sdl_nlen)[i]);
70    printf("\n");
71}
72
73#ifdef TEST_LINKADDRESSES
74static void
75LinkAddresses_print(LinkAddressesRef link_addrs)
76{
77    int i;
78    for (i = 0; i < link_addrs->count; i++) {
79	struct sockaddr_dl * sdl = link_addrs->list[i];
80	printf("%.*s ", sdl->sdl_nlen, sdl->sdl_data);
81	sockaddr_dl_print(sdl);
82    }
83}
84#endif /* TEST_LINKADDRESSES */
85
86struct sockaddr_dl *
87LinkAddresses_lookup(LinkAddressesRef list, char * ifname)
88{
89    int i;
90    int len = (int)strlen(ifname);
91
92    for (i = 0; i < list->count; i++) {
93	struct sockaddr_dl * sdl = list->list[i];
94
95	if (strncmp(sdl->sdl_data, ifname, sdl->sdl_nlen) == 0
96	    && len == sdl->sdl_nlen) {
97	    return (sdl);
98	}
99    }
100    return (NULL);
101}
102
103int
104LinkAddresses_count(LinkAddressesRef list)
105{
106    return (list->count);
107}
108
109struct sockaddr_dl *
110LinkAddresses_element(LinkAddressesRef list, int i)
111{
112    if (i >= 0 && i < list->count) {
113	return (list->list[i]);
114    }
115    return (NULL);
116}
117
118void
119LinkAddresses_free(LinkAddressesRef * list_p)
120{
121    int i;
122    LinkAddressesRef list = *list_p;
123
124    if (list) {
125	for (i = 0; i < list->count; i++) {
126	    free(list->list[i]);
127	    list->list[i] = NULL;
128	}
129	free(list->list);
130	list->list = NULL;
131	list->count = 0;
132	free(list);
133    }
134    *list_p = NULL;
135    return;
136}
137
138LinkAddressesRef
139LinkAddresses_create()
140{
141    char *			buf = NULL;
142    size_t			buf_len = 0;
143    int				mib[6];
144    int				offset = 0;
145    struct sockaddr_dl * *	list = NULL;
146    int				list_size = 10;
147    int				list_count = 0;
148    LinkAddressesRef		ret_list = NULL;
149
150    mib[0] = CTL_NET;
151    mib[1] = PF_ROUTE;
152    mib[2] = 0;
153    mib[3] = AF_LINK;
154    mib[4] = NET_RT_IFLIST;
155    mib[5] = 0;
156
157    if (sysctl(mib, 6, NULL, &buf_len, NULL, 0) < 0) {
158	fprintf(stderr, "sysctl() size failed: %s", strerror(errno));
159	goto failed;
160    }
161    buf = malloc(buf_len);
162    if (sysctl(mib, 6, buf, &buf_len, NULL, 0) < 0) {
163	fprintf(stderr, "sysctl() failed: %s", strerror(errno));
164	goto failed;
165    }
166    list = (struct sockaddr_dl * *)malloc(sizeof(*list) * list_size);
167    if (list == NULL) {
168	goto failed;
169    }
170    offset = 0;
171    while (offset < buf_len) {
172	struct if_msghdr * 	ifm;
173	struct sockaddr_dl *	sdl;
174	struct sockaddr_dl * 	new_p;
175
176	/* ALIGN: buf is aligned to at least sizeof(int) bytes */
177	ifm = (struct if_msghdr *)(void *)&buf[offset];
178
179	switch (ifm->ifm_type) {
180	case RTM_IFINFO:
181	    /* get interface name */
182	    sdl = (struct sockaddr_dl *)(ifm + 1);
183	    if (list_count == list_size) {
184		list_size *= 2;
185		list = realloc(list, sizeof(*list) * list_size);
186		if (list == NULL) {
187		    goto failed;
188		}
189	    }
190	    new_p = (void *)malloc(sdl->sdl_len);
191	    if (new_p == NULL) {
192		break;
193	    }
194	    bcopy(sdl, new_p, sdl->sdl_len);
195	    list[list_count++] = new_p;
196	    break;
197
198	default:
199	    break;
200	}
201	/* advance to next address/interface */
202	offset += ifm->ifm_msglen;
203    }
204    if (list_count == 0) {
205	free(list);
206	list = NULL;
207    }
208    else if (list_count < list_size) {
209	list = reallocf(list, sizeof(*list) * list_count);
210    }
211    if (list != NULL) {
212	ret_list = (void *)malloc(sizeof(*ret_list));
213	if (ret_list) {
214	    ret_list->list = list;
215	    ret_list->count = list_count;
216	}
217	else {
218	    free(list);
219	    list = NULL;
220	}
221    }
222 failed:
223    if (buf != NULL) {
224	free(buf);
225    }
226    return (ret_list);
227}
228
229#ifdef TEST_LINKADDRESSES
230int
231main()
232{
233    LinkAddressesRef	link_addrs;
234
235    link_addrs = LinkAddresses_create();
236    if (link_addrs) {
237	LinkAddresses_print(link_addrs);
238	LinkAddresses_free(&link_addrs);
239    }
240    exit(0);
241}
242#endif /* TEST_LINKADDRESSES */
243