1/*
2 * Copyright (c) 2003 Bruce M. Simpson.
3 * 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 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include "namespace.h"
31#include <sys/param.h>
32#include <sys/sysctl.h>
33#include <sys/ioctl.h>
34#include <sys/socket.h>
35#include <net/if.h>
36#include <net/if_dl.h>
37#include <net/route.h>
38
39#include <errno.h>
40#include <ifaddrs.h>
41#include <stdlib.h>
42#include <string.h>
43#include "un-namespace.h"
44
45#define	SALIGN	(sizeof(long) - 1)
46#define	SA_RLEN(sa)	((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
47			    (SALIGN + 1))
48#define	MAX_SYSCTL_TRY	5
49#define	RTA_MASKS	(RTA_GATEWAY | RTA_IFP | RTA_IFA)
50
51int
52getifmaddrs(struct ifmaddrs **pif)
53{
54	int icnt = 1;
55	int dcnt = 0;
56	int ntry = 0;
57	size_t len;
58	size_t needed;
59	int mib[6];
60	int i;
61	char *buf;
62	char *data;
63	char *next;
64	char *p;
65	struct ifma_msghdr *ifmam;
66	struct ifmaddrs *ifa, *ift;
67	struct rt_msghdr *rtm;
68	struct sockaddr *sa;
69
70	mib[0] = CTL_NET;
71	mib[1] = PF_ROUTE;
72	mib[2] = 0;             /* protocol */
73	mib[3] = 0;             /* wildcard address family */
74	mib[4] = NET_RT_IFMALIST;
75	mib[5] = 0;             /* no flags */
76	do {
77		if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
78			return (-1);
79		if ((buf = malloc(needed)) == NULL)
80			return (-1);
81		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
82			if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
83				free(buf);
84				return (-1);
85			}
86			free(buf);
87			buf = NULL;
88		}
89	} while (buf == NULL);
90
91	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
92		rtm = (struct rt_msghdr *)(void *)next;
93		if (rtm->rtm_version != RTM_VERSION)
94			continue;
95		switch (rtm->rtm_type) {
96		case RTM_NEWMADDR:
97			ifmam = (struct ifma_msghdr *)(void *)rtm;
98			if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
99				break;
100			icnt++;
101			p = (char *)(ifmam + 1);
102			for (i = 0; i < RTAX_MAX; i++) {
103				if ((RTA_MASKS & ifmam->ifmam_addrs &
104				    (1 << i)) == 0)
105					continue;
106				sa = (struct sockaddr *)(void *)p;
107				len = SA_RLEN(sa);
108				dcnt += len;
109				p += len;
110			}
111			break;
112		}
113	}
114
115	data = malloc(sizeof(struct ifmaddrs) * icnt + dcnt);
116	if (data == NULL) {
117		free(buf);
118		return (-1);
119	}
120
121	ifa = (struct ifmaddrs *)(void *)data;
122	data += sizeof(struct ifmaddrs) * icnt;
123
124	memset(ifa, 0, sizeof(struct ifmaddrs) * icnt);
125	ift = ifa;
126
127	for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
128		rtm = (struct rt_msghdr *)(void *)next;
129		if (rtm->rtm_version != RTM_VERSION)
130			continue;
131
132		switch (rtm->rtm_type) {
133		case RTM_NEWMADDR:
134			ifmam = (struct ifma_msghdr *)(void *)rtm;
135			if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
136				break;
137
138			p = (char *)(ifmam + 1);
139			for (i = 0; i < RTAX_MAX; i++) {
140				if ((RTA_MASKS & ifmam->ifmam_addrs &
141				    (1 << i)) == 0)
142					continue;
143				sa = (struct sockaddr *)(void *)p;
144				len = SA_RLEN(sa);
145				switch (i) {
146				case RTAX_GATEWAY:
147					ift->ifma_lladdr =
148					    (struct sockaddr *)(void *)data;
149					memcpy(data, p, len);
150					data += len;
151					break;
152
153				case RTAX_IFP:
154					ift->ifma_name =
155					    (struct sockaddr *)(void *)data;
156					memcpy(data, p, len);
157					data += len;
158					break;
159
160				case RTAX_IFA:
161					ift->ifma_addr =
162					    (struct sockaddr *)(void *)data;
163					memcpy(data, p, len);
164					data += len;
165					break;
166
167				default:
168					data += len;
169					break;
170				}
171				p += len;
172			}
173			ift->ifma_next = ift + 1;
174			ift = ift->ifma_next;
175			break;
176		}
177	}
178
179	free(buf);
180
181	if (ift > ifa) {
182		ift--;
183		ift->ifma_next = NULL;
184		*pif = ifa;
185	} else {
186		*pif = NULL;
187		free(ifa);
188	}
189	return (0);
190}
191
192void
193freeifmaddrs(struct ifmaddrs *ifmp)
194{
195
196	free(ifmp);
197}
198