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
47int
48getifaddrs(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}
291DEF_WEAK(getifaddrs);
292
293void
294freeifaddrs(struct ifaddrs *ifp)
295{
296	free(ifp);
297}
298DEF_WEAK(freeifaddrs);
299