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