1 /*	$OpenBSD: getifaddrs.c,v 1.9 2002/08/09 06:12:25 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.12 2000/02/23 14:51:59 dab Exp
26  */
27 
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <net/if.h>
32 #include <sys/param.h>
33 #include <net/route.h>
34 #include <sys/sysctl.h>
35 #include <net/if_dl.h>
36 
37 #include <errno.h>
38 #include <ifaddrs.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 __RCSID("$MirOS: src/lib/libc/net/getifaddrs.c,v 1.2 2009/11/09 21:30:50 tg Exp $");
44 
45 #define	SALIGN	(sizeof(long) - 1)
46 #define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1))
47 
48 int
getifaddrs(struct ifaddrs ** pif)49 getifaddrs(struct ifaddrs **pif)
50 {
51 	int icnt = 1;
52 	int dcnt = 0;
53 	int ncnt = 0;
54 	int mib[6];
55 	size_t needed;
56 	char *buf;
57 	char *next;
58 	struct ifaddrs *cif = 0;
59 	char *p, *p0;
60 	struct rt_msghdr *rtm;
61 	struct if_msghdr *ifm;
62 	struct ifa_msghdr *ifam;
63 	struct sockaddr_dl *dl;
64 	struct sockaddr *sa;
65 	u_short index_ = 0;
66 	size_t len, alen;
67 	struct ifaddrs *ifa, *ift;
68 	int i;
69 	char *data;
70 	char *names;
71 
72 	mib[0] = CTL_NET;
73 	mib[1] = PF_ROUTE;
74 	mib[2] = 0;             /* protocol */
75 	mib[3] = 0;             /* wildcard address family */
76 	mib[4] = NET_RT_IFLIST;
77 	mib[5] = 0;             /* no flags */
78 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
79 		return (-1);
80 	if ((buf = malloc(needed)) == NULL)
81 		return (-1);
82 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
83 		free(buf);
84 		return (-1);
85 	}
86 
87 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
88 		rtm = (struct rt_msghdr *)next;
89 		if (rtm->rtm_version != RTM_VERSION)
90 			continue;
91 		switch (rtm->rtm_type) {
92 		case RTM_IFINFO:
93 			ifm = (struct if_msghdr *)rtm;
94 			if (ifm->ifm_addrs & RTA_IFP) {
95 				index_ = ifm->ifm_index;
96 				++icnt;
97 				dl = (struct sockaddr_dl *)(ifm + 1);
98 				dcnt += SA_RLEN((struct sockaddr *)dl) +
99 					ALIGNBYTES;
100 				dcnt += sizeof(ifm->ifm_data);
101 				ncnt += dl->sdl_nlen + 1;
102 			} else
103 				index_ = 0;
104 			break;
105 
106 		case RTM_NEWADDR:
107 			ifam = (struct ifa_msghdr *)rtm;
108 			if (index_ && ifam->ifam_index != index_)
109 				abort();	/* XXX abort illegal in library */
110 
111 #define	RTA_MASKS	(RTA_NETMASK | RTA_IFA | RTA_BRD)
112 			if (index_ == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
113 				break;
114 			p = (char *)(ifam + 1);
115 			++icnt;
116 			/* Scan to look for length of address */
117 			alen = 0;
118 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
119 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
120 				    == 0)
121 					continue;
122 				sa = (struct sockaddr *)p;
123 				len = SA_RLEN(sa);
124 				if (i == RTAX_IFA) {
125 					alen = len;
126 					break;
127 				}
128 				p += len;
129 			}
130 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
131 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
132 				    == 0)
133 					continue;
134 				sa = (struct sockaddr *)p;
135 				len = SA_RLEN(sa);
136 				if (i == RTAX_NETMASK && sa->sa_len == 0)
137 					dcnt += alen;
138 				else
139 					dcnt += len;
140 				p += len;
141 			}
142 			break;
143 		}
144 	}
145 
146 	if (icnt + dcnt + ncnt == 1) {
147 		*pif = NULL;
148 		free(buf);
149 		return (0);
150 	}
151 	data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
152 	if (data == NULL) {
153 		free(buf);
154 		return(-1);
155 	}
156 
157 	ifa = (struct ifaddrs *)data;
158 	data += sizeof(struct ifaddrs) * icnt;
159 	names = data + dcnt;
160 
161 	memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
162 	ift = ifa;
163 
164 	index_ = 0;
165 	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
166 		rtm = (struct rt_msghdr *)next;
167 		if (rtm->rtm_version != RTM_VERSION)
168 			continue;
169 		switch (rtm->rtm_type) {
170 		case RTM_IFINFO:
171 			ifm = (struct if_msghdr *)rtm;
172 			if (ifm->ifm_addrs & RTA_IFP) {
173 				index_ = ifm->ifm_index;
174 				dl = (struct sockaddr_dl *)(ifm + 1);
175 
176 				cif = ift;
177 				ift->ifa_name = names;
178 				ift->ifa_flags = (int)ifm->ifm_flags;
179 				memcpy(names, dl->sdl_data, dl->sdl_nlen);
180 				names[dl->sdl_nlen] = 0;
181 				names += dl->sdl_nlen + 1;
182 
183 				ift->ifa_addr = (struct sockaddr *)data;
184 				memcpy(data, dl,
185 				    ((struct sockaddr *)dl)->sa_len);
186 				data += SA_RLEN((struct sockaddr *)dl);
187 
188 				/* ifm_data needs to be aligned */
189 				ift->ifa_data = data = (void *)ALIGN(data);
190 				memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
191  				data += sizeof(ifm->ifm_data);
192 
193 				ift = (ift->ifa_next = ift + 1);
194 			} else
195 				index_ = 0;
196 			break;
197 
198 		case RTM_NEWADDR:
199 			ifam = (struct ifa_msghdr *)rtm;
200 			if (index_ && ifam->ifam_index != index_)
201 				abort();	/* XXX abort illegal in library */
202 
203 			if (index_ == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
204 				break;
205 			ift->ifa_name = cif->ifa_name;
206 			ift->ifa_flags = cif->ifa_flags;
207 			ift->ifa_data = NULL;
208 			p = (char *)(ifam + 1);
209 			/* Scan to look for length of address */
210 			alen = 0;
211 			for (p0 = p, i = 0; i < RTAX_MAX; i++) {
212 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
213 				    == 0)
214 					continue;
215 				sa = (struct sockaddr *)p;
216 				len = SA_RLEN(sa);
217 				if (i == RTAX_IFA) {
218 					alen = len;
219 					break;
220 				}
221 				p += len;
222 			}
223 			for (p = p0, i = 0; i < RTAX_MAX; i++) {
224 				if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
225 				    == 0)
226 					continue;
227 				sa = (struct sockaddr *)p;
228 				len = SA_RLEN(sa);
229 				switch (i) {
230 				case RTAX_IFA:
231 					ift->ifa_addr = (struct sockaddr *)data;
232 					memcpy(data, p, len);
233 					data += len;
234 					break;
235 
236 				case RTAX_NETMASK:
237 					ift->ifa_netmask =
238 					    (struct sockaddr *)data;
239 					if (sa->sa_len == 0) {
240 						memset(data, 0, alen);
241 						data += alen;
242 						break;
243 					}
244 					memcpy(data, p, len);
245 					data += len;
246 					break;
247 
248 				case RTAX_BRD:
249 					ift->ifa_broadaddr =
250 					    (struct sockaddr *)data;
251 					memcpy(data, p, len);
252 					data += len;
253 					break;
254 				}
255 				p += len;
256 			}
257 
258 
259 			ift = (ift->ifa_next = ift + 1);
260 			break;
261 		}
262 	}
263 
264 	free(buf);
265 	if (--ift >= ifa) {
266 		ift->ifa_next = NULL;
267 		*pif = ifa;
268 	} else {
269 		*pif = NULL;
270 		free(ifa);
271 	}
272 	return (0);
273 }
274 
275 void
freeifaddrs(struct ifaddrs * ifp)276 freeifaddrs(struct ifaddrs *ifp)
277 {
278 	free(ifp);
279 }
280