1/*
2 * Copyright (c) 1995, 1999
3 *	Berkeley Software Design, Inc.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *
11 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
12 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14 * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
15 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
17 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
19 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
20 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
21 * SUCH DAMAGE.
22 *
23 *	BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
24 */
25/*
26 * NOTE: SIOCGIFCONF case is not LP64 friendly.  it also does not perform
27 * try-and-error for region size.
28 */
29#include <sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/socket.h>
32#include <net/if.h>
33#ifdef	NET_RT_IFLIST
34#include <sys/param.h>
35#include <net/route.h>
36#include <sys/sysctl.h>
37#include <net/if_dl.h>
38#endif
39
40#include <errno.h>
41#include <ifaddrs.h>
42#include <netinet/in.h>
43#include <stdlib.h>
44#include <string.h>
45#include <stdint.h>
46
47#if !defined(AF_LINK)
48#define	SA_LEN(sa)	sizeof(struct sockaddr)
49#endif
50
51#if !defined(SA_LEN)
52#define	SA_LEN(sa)	(sa)->sa_len
53#endif
54
55#define	SALIGN	(sizeof(int32_t) - 1)
56#define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
57
58#ifndef	ALIGNBYTES
59/*
60 * On systems with a routing socket, ALIGNBYTES should match the value
61 * that the kernel uses when building the messages.
62 */
63#define	ALIGNBYTES	XXX
64#endif
65#ifndef	ALIGN
66#define	ALIGN(p)	(((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
67#endif
68
69#if	_BSDI_VERSION >= 199701
70#define	HAVE_IFM_DATA
71#endif
72
73#if	_BSDI_VERSION >= 199802
74/* ifam_data is very specific to recent versions of bsdi */
75#define	HAVE_IFAM_DATA
76#endif
77
78#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
79#define	HAVE_IFM_DATA
80#endif
81
82#ifdef __APPLE__
83#define HAVE_IFM_DATA
84#endif
85
86#define MEMORY_MIN 2048
87#define MEMORY_MAX 16777216
88
89int
90getifaddrs(struct ifaddrs **pif)
91{
92	int icnt = 1;
93	int dcnt = 0;
94	int ncnt = 0;
95	struct ifaddrs *ifa, *ift;
96	struct sockaddr_in6 *sin6;
97	uint16_t esid;
98#ifdef	NET_RT_IFLIST
99	int mib[6];
100	size_t needed;
101	char *buf;
102	char *next;
103	struct ifaddrs *cif = 0;
104	char *p, *p0;
105	struct rt_msghdr *rtm;
106	struct if_msghdr *ifm;
107	struct ifa_msghdr *ifam;
108	struct sockaddr_dl *dl;
109	struct sockaddr *sa;
110	u_short index = 0;
111#else	/* NET_RT_IFLIST */
112	char buf[1024];
113	int m, sock;
114	struct ifconf ifc;
115	struct ifreq *ifr;
116	struct ifreq *lifr;
117#endif	/* NET_RT_IFLIST */
118	int i, status;
119	size_t len, alen;
120	char *data;
121	char *names;
122
123#ifdef	NET_RT_IFLIST
124	mib[0] = CTL_NET;
125	mib[1] = PF_ROUTE;
126	mib[2] = 0;             /* protocol */
127	mib[3] = 0;             /* wildcard address family */
128	mib[4] = NET_RT_IFLIST;
129	mib[5] = 0;             /* no flags */
130	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) return (-1);
131
132	if (needed < MEMORY_MIN) needed = MEMORY_MIN;
133	needed *= 2;
134	if (needed > MEMORY_MAX) needed = MEMORY_MAX;
135
136	buf = NULL;
137
138	while (needed <= MEMORY_MAX)
139	{
140		buf = malloc(needed);
141		if (buf == NULL) return (-1);
142
143		status = sysctl(mib, 6, buf, &needed, NULL, 0);
144		if (status >= 0) break;
145
146		free(buf);
147		buf = NULL;
148		needed *= 2;
149	}
150
151	if (buf == NULL)
152	{
153		errno = ENOBUFS;
154		return (-1);
155	}
156
157	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
158		rtm = (struct rt_msghdr *)next;
159		if (rtm->rtm_version != RTM_VERSION)
160			continue;
161		switch (rtm->rtm_type) {
162		case RTM_IFINFO:
163			ifm = (struct if_msghdr *)rtm;
164			if (ifm->ifm_addrs & RTA_IFP) {
165				index = ifm->ifm_index;
166				++icnt;
167				dl = (struct sockaddr_dl *)(ifm + 1);
168				dcnt += SA_RLEN((struct sockaddr *)dl) +
169					ALIGNBYTES;
170#ifdef	HAVE_IFM_DATA
171				dcnt += sizeof(ifm->ifm_data);
172#endif	/* HAVE_IFM_DATA */
173				ncnt += dl->sdl_nlen + 1;
174			} else
175				index = 0;
176			break;
177
178		case RTM_NEWADDR:
179			ifam = (struct ifa_msghdr *)rtm;
180			if (index && ifam->ifam_index != index)
181				abort();	/* this cannot happen */
182
183#define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
184			if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
185				break;
186			p = (char *)(ifam + 1);
187			++icnt;
188#ifdef	HAVE_IFAM_DATA
189			dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES;
190#endif	/* HAVE_IFAM_DATA */
191			/* Scan to look for length of address */
192			alen = 0;
193			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
194				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
195				    == 0)
196					continue;
197				sa = (struct sockaddr *)p;
198				len = SA_RLEN(sa);
199				if (i == RTAX_IFA) {
200					alen = len;
201					break;
202				}
203				p += len;
204			}
205			for (p = p0, i = 0; i < RTAX_MAX; i++) {
206				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
207				    == 0)
208					continue;
209				sa = (struct sockaddr *)p;
210				len = SA_RLEN(sa);
211				if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
212					dcnt += alen;
213				else
214					dcnt += len;
215				p += len;
216			}
217			break;
218		}
219	}
220#else	/* NET_RT_IFLIST */
221	ifc.ifc_buf = buf;
222	ifc.ifc_len = sizeof(buf);
223
224	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
225		free(buf);
226		return (-1);
227	}
228
229	i =  ioctl(sock, SIOCGIFCONF, (char *)&ifc);
230	close(sock);
231	if (i < 0) {
232		free(buf);
233		return (-1);
234	}
235
236	ifr = ifc.ifc_req;
237	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
238
239	while (ifr < lifr) {
240		struct sockaddr *sa;
241
242		sa = &ifr->ifr_addr;
243		++icnt;
244		dcnt += SA_RLEN(sa);
245		ncnt += sizeof(ifr->ifr_name) + 1;
246
247		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
248	}
249#endif	/* NET_RT_IFLIST */
250
251	if (icnt + dcnt + ncnt == 1) {
252		*pif = NULL;
253		free(buf);
254		return (0);
255	}
256	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
257	if (data == NULL) {
258		free(buf);
259		return(-1);
260	}
261
262	ifa = (struct ifaddrs *)data;
263	data += sizeof(struct ifaddrs) * icnt;
264	names = data + dcnt;
265
266	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
267	ift = ifa;
268
269#ifdef	NET_RT_IFLIST
270	index = 0;
271	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
272		rtm = (struct rt_msghdr *)next;
273		if (rtm->rtm_version != RTM_VERSION)
274			continue;
275		switch (rtm->rtm_type) {
276		case RTM_IFINFO:
277			ifm = (struct if_msghdr *)rtm;
278			if (ifm->ifm_addrs & RTA_IFP) {
279				index = ifm->ifm_index;
280				dl = (struct sockaddr_dl *)(ifm + 1);
281
282				cif = ift;
283				ift->ifa_name = names;
284				ift->ifa_flags = (int)ifm->ifm_flags;
285				memcpy(names, dl->sdl_data, dl->sdl_nlen);
286				names[dl->sdl_nlen] = 0;
287				names += dl->sdl_nlen + 1;
288
289				ift->ifa_addr = (struct sockaddr *)data;
290				memcpy(data, dl, SA_LEN((struct sockaddr *)dl));
291				data += SA_RLEN((struct sockaddr *)dl);
292
293#ifdef	HAVE_IFM_DATA
294				/* ifm_data needs to be aligned */
295				ift->ifa_data = data = (void *)ALIGN(data);
296				memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
297 				data += sizeof(ifm->ifm_data);
298#else	/* HAVE_IFM_DATA */
299				ift->ifa_data = NULL;
300#endif	/* HAVE_IFM_DATA */
301
302				ift = (ift->ifa_next = ift + 1);
303			} else
304				index = 0;
305			break;
306
307		case RTM_NEWADDR:
308			ifam = (struct ifa_msghdr *)rtm;
309			if (index && ifam->ifam_index != index)
310				abort();	/* this cannot happen */
311
312			if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
313				break;
314			ift->ifa_name = cif->ifa_name;
315			ift->ifa_flags = cif->ifa_flags;
316			ift->ifa_data = NULL;
317			p = (char *)(ifam + 1);
318			/* Scan to look for length of address */
319			alen = 0;
320			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
321				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
322				    == 0)
323					continue;
324				sa = (struct sockaddr *)p;
325				len = SA_RLEN(sa);
326				if (i == RTAX_IFA) {
327					alen = len;
328					break;
329				}
330				p += len;
331			}
332			for (p = p0, i = 0; i < RTAX_MAX; i++) {
333				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
334				    == 0)
335					continue;
336				sa = (struct sockaddr *)p;
337				len = SA_RLEN(sa);
338				switch (i) {
339				case RTAX_IFA:
340					ift->ifa_addr = (struct sockaddr *)data;
341					memcpy(data, p, len);
342					data += len;
343					break;
344
345				case RTAX_NETMASK:
346					ift->ifa_netmask =
347					    (struct sockaddr *)data;
348					if (SA_LEN(sa) == 0) {
349						memset(data, 0, alen);
350						data += alen;
351						break;
352					}
353					memcpy(data, p, len);
354					data += len;
355					break;
356
357				case RTAX_BRD:
358					ift->ifa_broadaddr =
359					    (struct sockaddr *)data;
360					memcpy(data, p, len);
361					data += len;
362					break;
363				}
364				p += len;
365			}
366
367#ifdef	HAVE_IFAM_DATA
368			/* ifam_data needs to be aligned */
369			ift->ifa_data = data = (void *)ALIGN(data);
370			memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data));
371			data += sizeof(ifam->ifam_data);
372#endif	/* HAVE_IFAM_DATA */
373
374			ift = (ift->ifa_next = ift + 1);
375			break;
376		}
377	}
378
379	free(buf);
380#else	/* NET_RT_IFLIST */
381	ifr = ifc.ifc_req;
382	lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
383
384	while (ifr < lifr) {
385		struct sockaddr *sa;
386
387		ift->ifa_name = names;
388		names[sizeof(ifr->ifr_name)] = 0;
389		strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name));
390		while (*names++)
391			;
392
393		ift->ifa_addr = (struct sockaddr *)data;
394		sa = &ifr->ifr_addr;
395		memcpy(data, sa, SA_LEN(sa));
396		data += SA_RLEN(sa);
397
398		ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa));
399		ift = (ift->ifa_next = ift + 1);
400	}
401#endif	/* NET_RT_IFLIST */
402	if (--ift >= ifa) {
403		ift->ifa_next = NULL;
404		*pif = ifa;
405	} else {
406		*pif = NULL;
407		free(ifa);
408	}
409
410	for (ift = ifa; ift != NULL; ift = ift->ifa_next)
411	{
412		if (ift->ifa_addr->sa_family == AF_INET6)
413		{
414			sin6 = (struct sockaddr_in6 *)ift->ifa_addr;
415			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
416			{
417				esid = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]);
418				sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
419				if (sin6->sin6_scope_id == 0) sin6->sin6_scope_id = esid;
420			}
421		}
422	}
423
424	return (0);
425}
426
427void
428freeifaddrs(struct ifaddrs *ifp)
429{
430	free(ifp);
431}
432