xref: /netbsd-src/sbin/ifconfig/util.c (revision bbde328be4e75ea9ad02e9715ea13ca54b797ada)
1 /*	$NetBSD: util.c,v 1.13 2009/09/11 22:06:29 dyoung Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008 David Young.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: util.c,v 1.13 2009/09/11 22:06:29 dyoung Exp $");
31 #endif /* not lint */
32 
33 #include <ctype.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <util.h>
43 
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <ifaddrs.h>
48 
49 #include <sys/ioctl.h>
50 #include <net/if.h>
51 #include <net/if_dl.h>
52 #include <netinet/in.h>		/* XXX */
53 
54 #include "env.h"
55 #include "extern.h"
56 #include "util.h"
57 
58 int
59 getsock(int naf)
60 {
61 	static int oaf = -1, s;
62 
63 	if (oaf == naf || (oaf != -1 && naf == AF_UNSPEC))
64 		return s;
65 
66 	if (oaf != -1)
67 		close(s);
68 
69 	if (naf == AF_UNSPEC)
70 		naf = AF_INET;
71 
72 	s = socket(naf, SOCK_DGRAM, 0);
73 	if (s == -1)
74 		oaf = -1;
75 	else
76 		oaf = naf;
77 	return s;
78 }
79 
80 const char *
81 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
82 {
83 	int len;
84 	bool hexstr;
85 	u_int8_t *p;
86 
87 	len = *lenp;
88 	p = buf;
89 	hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
90 	if (hexstr)
91 		val += 2;
92 	for (;;) {
93 		if (*val == '\0')
94 			break;
95 		if (sep != NULL && strchr(sep, *val) != NULL) {
96 			val++;
97 			break;
98 		}
99 		if (hexstr) {
100 			if (!isxdigit((u_char)val[0]) ||
101 			    !isxdigit((u_char)val[1])) {
102 				warnx("bad hexadecimal digits");
103 				return NULL;
104 			}
105 		}
106 		if (p > buf + len) {
107 			if (hexstr)
108 				warnx("hexadecimal digits too long");
109 			else
110 				warnx("strings too long");
111 			return NULL;
112 		}
113 		if (hexstr) {
114 #define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
115 			*p++ = (tohex((u_char)val[0]) << 4) |
116 			    tohex((u_char)val[1]);
117 #undef tohex
118 			val += 2;
119 		} else
120 			*p++ = *val++;
121 	}
122 	len = p - buf;
123 	if (len < *lenp)
124 		memset(p, 0, *lenp - len);
125 	*lenp = len;
126 	return val;
127 }
128 
129 void
130 print_string(const u_int8_t *buf, int len)
131 {
132 	int i;
133 	bool hasspc;
134 
135 	i = 0;
136 	hasspc = false;
137 	if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
138 		for (; i < len; i++) {
139 			if (!isprint(buf[i]))
140 				break;
141 			if (isspace(buf[i]))
142 				hasspc = true;
143 		}
144 	}
145 	if (i == len) {
146 		if (hasspc || len == 0)
147 			printf("\"%.*s\"", len, buf);
148 		else
149 			printf("%.*s", len, buf);
150 	} else {
151 		printf("0x");
152 		for (i = 0; i < len; i++)
153 			printf("%02x", buf[i]);
154 	}
155 }
156 
157 struct paddr_prefix *
158 prefixlen_to_mask(int af, int plen)
159 {
160 	union {
161 		struct sockaddr sa;
162 		struct sockaddr_in sin;
163 		struct sockaddr_in6 sin6;
164 	} u;
165 	struct paddr_prefix *pfx;
166 	size_t addrlen;
167 	uint8_t *addr;
168 	int nbit;
169 
170 	memset(&u, 0, sizeof(u));
171 
172 	switch (af) {
173 	case AF_INET:
174 		addrlen = sizeof(u.sin.sin_addr);
175 		addr = (uint8_t *)&u.sin.sin_addr;
176 		u.sa.sa_len = sizeof(u.sin);
177 		break;
178 	case AF_INET6:
179 		addrlen = sizeof(u.sin6.sin6_addr);
180 		addr = (uint8_t *)&u.sin6.sin6_addr;
181 		u.sa.sa_len = sizeof(u.sin6);
182 		break;
183 	default:
184 		errno = EINVAL;
185 		return NULL;
186 	}
187 	u.sa.sa_family = af;
188 
189 	if (plen < 0 || (size_t)plen > addrlen * NBBY) {
190 		errno = EINVAL;
191 		return NULL;
192 	}
193 
194 	if (plen == 0)
195 		plen = addrlen * NBBY;
196 
197 	memset(addr, 0xff, (plen + NBBY - 1) / NBBY);
198 
199 	nbit = plen % NBBY;
200 	if (nbit != 0)
201 		addr[plen / NBBY] &= ~((uint8_t)0xff >> nbit);
202 	pfx = malloc(offsetof(struct paddr_prefix, pfx_addr) + u.sa.sa_len);
203 	if (pfx == NULL)
204 		return NULL;
205 	pfx->pfx_len = plen;
206 	memcpy(&pfx->pfx_addr, &u.sa, u.sa.sa_len);
207 
208 	return pfx;
209 }
210 
211 int
212 direct_ioctl(prop_dictionary_t env, unsigned long cmd, void *data)
213 {
214 	const char *ifname;
215 	int s;
216 
217 	if ((s = getsock(AF_UNSPEC)) == -1)
218 		err(EXIT_FAILURE, "getsock");
219 
220 	if ((ifname = getifname(env)) == NULL)
221 		err(EXIT_FAILURE, "getifname");
222 
223 	estrlcpy(data, ifname, IFNAMSIZ);
224 
225 	return ioctl(s, cmd, data);
226 }
227 
228 int
229 indirect_ioctl(prop_dictionary_t env, unsigned long cmd, void *data)
230 {
231 	struct ifreq ifr;
232 
233 	memset(&ifr, 0, sizeof(ifr));
234 
235 	ifr.ifr_data = data;
236 
237 	return direct_ioctl(env, cmd, &ifr);
238 }
239 
240 void
241 print_link_addresses(prop_dictionary_t env, bool print_active_only)
242 {
243 	char hbuf[NI_MAXHOST];
244 	const char *ifname;
245 	int s;
246 	struct ifaddrs *ifa, *ifap;
247 	const struct sockaddr_dl *sdl;
248 	struct if_laddrreq iflr;
249 
250 	if ((ifname = getifname(env)) == NULL)
251 		err(EXIT_FAILURE, "%s: getifname", __func__);
252 
253 	if ((s = getsock(AF_LINK)) == -1)
254 		err(EXIT_FAILURE, "%s: getsock", __func__);
255 
256 	if (getifaddrs(&ifap) == -1)
257 		err(EXIT_FAILURE, "%s: getifaddrs", __func__);
258 
259 	memset(&iflr, 0, sizeof(iflr));
260 
261 	strlcpy(iflr.iflr_name, ifname, sizeof(iflr.iflr_name));
262 
263 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
264 		if (strcmp(ifname, ifa->ifa_name) != 0)
265 			continue;
266 		if (ifa->ifa_addr->sa_family != AF_LINK)
267 			continue;
268 
269 		sdl = satocsdl(ifa->ifa_addr);
270 
271 		memcpy(&iflr.addr, ifa->ifa_addr, MIN(ifa->ifa_addr->sa_len,
272 		    sizeof(iflr.addr)));
273 		iflr.flags = IFLR_PREFIX;
274 		iflr.prefixlen = sdl->sdl_alen * NBBY;
275 
276 		if (ioctl(s, SIOCGLIFADDR, &iflr) == -1)
277 			err(EXIT_FAILURE, "%s: ioctl", __func__);
278 
279 		if (((iflr.flags & IFLR_ACTIVE) != 0) != print_active_only)
280 			continue;
281 
282 		if (getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len,
283 			hbuf, sizeof(hbuf), NULL, 0,
284 			Nflag ? 0 : NI_NUMERICHOST) == 0 &&
285 		    hbuf[0] != '\0') {
286 			printf("\t%s %s\n",
287 			    print_active_only ? "address:" : "link", hbuf);
288 		}
289 	}
290 	freeifaddrs(ifap);
291 }
292 
293 int16_t
294 ifa_get_preference(const char *ifname, const struct sockaddr *sa)
295 {
296 	struct if_addrprefreq ifap;
297 	int s;
298 
299 	if ((s = getsock(sa->sa_family)) == -1) {
300 		if (errno == EPROTONOSUPPORT)
301 			return 0;
302 		err(EXIT_FAILURE, "socket");
303 	}
304 	memset(&ifap, 0, sizeof(ifap));
305 	estrlcpy(ifap.ifap_name, ifname, sizeof(ifap.ifap_name));
306 	memcpy(&ifap.ifap_addr, sa, MIN(sizeof(ifap.ifap_addr), sa->sa_len));
307 	if (ioctl(s, SIOCGIFADDRPREF, &ifap) == -1) {
308 		if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)
309 			return 0;
310 		warn("SIOCGIFADDRPREF");
311 	}
312 	return ifap.ifap_preference;
313 }
314 
315 void
316 ifa_print_preference(const char *ifname, const struct sockaddr *sa)
317 {
318 	int16_t preference;
319 
320 	if (lflag)
321 		return;
322 
323 	preference = ifa_get_preference(ifname, sa);
324 	printf(" preference %" PRId16, preference);
325 }
326 
327 bool
328 ifa_any_preferences(const char *ifname, struct ifaddrs *ifap, int family)
329 {
330 	struct ifaddrs *ifa;
331 
332 	/* Print address preference numbers if any address has a non-zero
333 	 * preference assigned.
334 	 */
335 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
336 		if (strcmp(ifname, ifa->ifa_name) != 0)
337 			continue;
338 		if (ifa->ifa_addr->sa_family != family)
339 			continue;
340 		if (ifa_get_preference(ifa->ifa_name, ifa->ifa_addr) != 0)
341 			return true;
342 	}
343 	return false;
344 }
345 
346 
347 #ifdef INET6
348 /* KAME idiosyncrasy */
349 void
350 in6_fillscopeid(struct sockaddr_in6 *sin6)
351 {
352 	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
353 		sin6->sin6_scope_id =
354 			ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
355 		sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
356 	}
357 }
358 #endif /* INET6	*/
359