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