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