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