xref: /netbsd-src/sbin/ifconfig/util.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: util.c,v 1.15 2010/07/01 16:44:05 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.15 2010/07/01 16:44:05 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     bool hexok)
83 {
84 	int len;
85 	bool hexstr;
86 	u_int8_t *p;
87 
88 	len = *lenp;
89 	p = buf;
90 	hexstr = hexok && val[0] == '0' && tolower((u_char)val[1]) == 'x';
91 	if (hexstr)
92 		val += 2;
93 	for (;;) {
94 		if (*val == '\0')
95 			break;
96 		if (sep != NULL && strchr(sep, *val) != NULL) {
97 			val++;
98 			break;
99 		}
100 		if (hexstr) {
101 			if (!isxdigit((u_char)val[0]) ||
102 			    !isxdigit((u_char)val[1])) {
103 				warnx("bad hexadecimal digits");
104 				return NULL;
105 			}
106 		}
107 		if (p >= buf + len) {
108 			if (hexstr)
109 				warnx("hexadecimal digits too long");
110 			else
111 				warnx("strings too long");
112 			return NULL;
113 		}
114 		if (hexstr) {
115 #define	tohex(x)	(isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
116 			*p++ = (tohex((u_char)val[0]) << 4) |
117 			    tohex((u_char)val[1]);
118 #undef tohex
119 			val += 2;
120 		} else
121 			*p++ = *val++;
122 	}
123 	len = p - buf;
124 	if (len < *lenp)
125 		memset(p, 0, *lenp - len);
126 	*lenp = len;
127 	return val;
128 }
129 
130 void
131 print_string(const u_int8_t *buf, int len)
132 {
133 	int i;
134 	bool hasspc;
135 
136 	i = 0;
137 	hasspc = false;
138 	if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
139 		for (; i < len; i++) {
140 			if (!isprint(buf[i]))
141 				break;
142 			if (isspace(buf[i]))
143 				hasspc = true;
144 		}
145 	}
146 	if (i == len) {
147 		if (hasspc || len == 0)
148 			printf("\"%.*s\"", len, buf);
149 		else
150 			printf("%.*s", len, buf);
151 	} else {
152 		printf("0x");
153 		for (i = 0; i < len; i++)
154 			printf("%02x", buf[i]);
155 	}
156 }
157 
158 struct paddr_prefix *
159 prefixlen_to_mask(int af, int plen)
160 {
161 	union {
162 		struct sockaddr sa;
163 		struct sockaddr_in sin;
164 		struct sockaddr_in6 sin6;
165 	} u;
166 	struct paddr_prefix *pfx;
167 	size_t addrlen;
168 	uint8_t *addr;
169 	int nbit;
170 
171 	memset(&u, 0, sizeof(u));
172 
173 	switch (af) {
174 	case AF_INET:
175 		addrlen = sizeof(u.sin.sin_addr);
176 		addr = (uint8_t *)&u.sin.sin_addr;
177 		u.sa.sa_len = sizeof(u.sin);
178 		break;
179 	case AF_INET6:
180 		addrlen = sizeof(u.sin6.sin6_addr);
181 		addr = (uint8_t *)&u.sin6.sin6_addr;
182 		u.sa.sa_len = sizeof(u.sin6);
183 		break;
184 	default:
185 		errno = EINVAL;
186 		return NULL;
187 	}
188 	u.sa.sa_family = af;
189 
190 	if (plen < 0 || (size_t)plen > addrlen * NBBY) {
191 		errno = EINVAL;
192 		return NULL;
193 	}
194 
195 	if (plen == 0)
196 		plen = addrlen * NBBY;
197 
198 	memset(addr, 0xff, (plen + NBBY - 1) / NBBY);
199 
200 	nbit = plen % NBBY;
201 	if (nbit != 0)
202 		addr[plen / NBBY] &= ~((uint8_t)0xff >> nbit);
203 	pfx = malloc(offsetof(struct paddr_prefix, pfx_addr) + u.sa.sa_len);
204 	if (pfx == NULL)
205 		return NULL;
206 	pfx->pfx_len = plen;
207 	memcpy(&pfx->pfx_addr, &u.sa, u.sa.sa_len);
208 
209 	return pfx;
210 }
211 
212 int
213 direct_ioctl(prop_dictionary_t env, unsigned long cmd, void *data)
214 {
215 	const char *ifname;
216 	int s;
217 
218 	if ((s = getsock(AF_UNSPEC)) == -1)
219 		err(EXIT_FAILURE, "getsock");
220 
221 	if ((ifname = getifname(env)) == NULL)
222 		err(EXIT_FAILURE, "getifname");
223 
224 	estrlcpy(data, ifname, IFNAMSIZ);
225 
226 	return ioctl(s, cmd, data);
227 }
228 
229 int
230 indirect_ioctl(prop_dictionary_t env, unsigned long cmd, void *data)
231 {
232 	struct ifreq ifr;
233 
234 	memset(&ifr, 0, sizeof(ifr));
235 
236 	ifr.ifr_data = data;
237 
238 	return direct_ioctl(env, cmd, &ifr);
239 }
240 
241 void
242 print_link_addresses(prop_dictionary_t env, bool print_active_only)
243 {
244 	char hbuf[NI_MAXHOST];
245 	const char *ifname;
246 	int s;
247 	struct ifaddrs *ifa, *ifap;
248 	const struct sockaddr_dl *sdl;
249 	struct if_laddrreq iflr;
250 
251 	if ((ifname = getifname(env)) == NULL)
252 		err(EXIT_FAILURE, "%s: getifname", __func__);
253 
254 	if ((s = getsock(AF_LINK)) == -1)
255 		err(EXIT_FAILURE, "%s: getsock", __func__);
256 
257 	if (getifaddrs(&ifap) == -1)
258 		err(EXIT_FAILURE, "%s: getifaddrs", __func__);
259 
260 	memset(&iflr, 0, sizeof(iflr));
261 
262 	strlcpy(iflr.iflr_name, ifname, sizeof(iflr.iflr_name));
263 
264 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
265 		if (strcmp(ifname, ifa->ifa_name) != 0)
266 			continue;
267 		if (ifa->ifa_addr->sa_family != AF_LINK)
268 			continue;
269 
270 		sdl = satocsdl(ifa->ifa_addr);
271 
272 		memcpy(&iflr.addr, ifa->ifa_addr, MIN(ifa->ifa_addr->sa_len,
273 		    sizeof(iflr.addr)));
274 		iflr.flags = IFLR_PREFIX;
275 		iflr.prefixlen = sdl->sdl_alen * NBBY;
276 
277 		if (ioctl(s, SIOCGLIFADDR, &iflr) == -1)
278 			err(EXIT_FAILURE, "%s: ioctl", __func__);
279 
280 		if (((iflr.flags & IFLR_ACTIVE) != 0) != print_active_only)
281 			continue;
282 
283 		if (getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len,
284 			hbuf, sizeof(hbuf), NULL, 0,
285 			Nflag ? 0 : NI_NUMERICHOST) == 0 &&
286 		    hbuf[0] != '\0') {
287 			printf("\t%s %s\n",
288 			    print_active_only ? "address:" : "link", hbuf);
289 		}
290 	}
291 	freeifaddrs(ifap);
292 }
293 
294 int16_t
295 ifa_get_preference(const char *ifname, const struct sockaddr *sa)
296 {
297 	struct if_addrprefreq ifap;
298 	int s;
299 
300 	if ((s = getsock(sa->sa_family)) == -1) {
301 		if (errno == EPROTONOSUPPORT)
302 			return 0;
303 		err(EXIT_FAILURE, "socket");
304 	}
305 	memset(&ifap, 0, sizeof(ifap));
306 	estrlcpy(ifap.ifap_name, ifname, sizeof(ifap.ifap_name));
307 	memcpy(&ifap.ifap_addr, sa, MIN(sizeof(ifap.ifap_addr), sa->sa_len));
308 	if (ioctl(s, SIOCGIFADDRPREF, &ifap) == -1) {
309 		if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)
310 			return 0;
311 		warn("SIOCGIFADDRPREF");
312 	}
313 	return ifap.ifap_preference;
314 }
315 
316 void
317 ifa_print_preference(const char *ifname, const struct sockaddr *sa)
318 {
319 	int16_t preference;
320 
321 	if (lflag)
322 		return;
323 
324 	preference = ifa_get_preference(ifname, sa);
325 	printf(" preference %" PRId16, preference);
326 }
327 
328 bool
329 ifa_any_preferences(const char *ifname, struct ifaddrs *ifap, int family)
330 {
331 	struct ifaddrs *ifa;
332 
333 	/* Print address preference numbers if any address has a non-zero
334 	 * preference assigned.
335 	 */
336 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
337 		if (strcmp(ifname, ifa->ifa_name) != 0)
338 			continue;
339 		if (ifa->ifa_addr->sa_family != family)
340 			continue;
341 		if (ifa_get_preference(ifa->ifa_name, ifa->ifa_addr) != 0)
342 			return true;
343 	}
344 	return false;
345 }
346 
347 
348 #ifdef INET6
349 /* KAME idiosyncrasy */
350 void
351 in6_fillscopeid(struct sockaddr_in6 *sin6)
352 {
353 	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
354 		sin6->sin6_scope_id =
355 			ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
356 		sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
357 	}
358 }
359 #endif /* INET6	*/
360