xref: /freebsd/lib/libifconfig/libifconfig.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /*
2  * Copyright (c) 1983, 1993
3  *  The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2016-2017, Marie Helene Kvello-Aune.  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  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/sysctl.h>
36 
37 #include <net/if.h>
38 #include <net/if_mib.h>
39 #include <netinet/in.h>
40 #include <netinet6/in6_var.h>
41 #include <netinet6/nd6.h>
42 
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <ifaddrs.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include <net/if_vlan_var.h>
54 
55 #include "libifconfig.h"
56 #include "libifconfig_internal.h"
57 
58 #define NOTAG    ((u_short) -1)
59 
60 static bool
61 isnd6defif(ifconfig_handle_t *h, const char *name)
62 {
63 	struct in6_ndifreq ndifreq;
64 	unsigned int ifindex;
65 
66 	memset(&ndifreq, 0, sizeof(ndifreq));
67 	strlcpy(ndifreq.ifname, name, sizeof(ndifreq.ifname));
68 	ifindex = if_nametoindex(ndifreq.ifname);
69 	if (ifconfig_ioctlwrap(h, AF_INET6, SIOCGDEFIFACE_IN6, &ndifreq) < 0) {
70 		return (false);
71 	}
72 	h->error.errtype = OK;
73 	return (ndifreq.ifindex == ifindex);
74 }
75 
76 ifconfig_handle_t *
77 ifconfig_open(void)
78 {
79 	ifconfig_handle_t *h;
80 
81 	h = calloc(1, sizeof(*h));
82 
83 	if (h == NULL) {
84 		return (NULL);
85 	}
86 	for (int i = 0; i <= AF_MAX; i++) {
87 		h->sockets[i] = -1;
88 	}
89 
90 	return (h);
91 }
92 
93 void
94 ifconfig_close(ifconfig_handle_t *h)
95 {
96 
97 	for (int i = 0; i <= AF_MAX; i++) {
98 		if (h->sockets[i] != -1) {
99 			(void)close(h->sockets[i]);
100 		}
101 	}
102 	freeifaddrs(h->ifap);
103 	free(h);
104 }
105 
106 ifconfig_errtype
107 ifconfig_err_errtype(ifconfig_handle_t *h)
108 {
109 
110 	return (h->error.errtype);
111 }
112 
113 int
114 ifconfig_err_errno(ifconfig_handle_t *h)
115 {
116 
117 	return (h->error.errcode);
118 }
119 
120 unsigned long
121 ifconfig_err_ioctlreq(ifconfig_handle_t *h)
122 {
123 
124 	return (h->error.ioctl_request);
125 }
126 
127 int
128 ifconfig_foreach_iface(ifconfig_handle_t *h,
129     ifconfig_foreach_func_t cb, void *udata)
130 {
131 	int ret;
132 
133 	ret = ifconfig_getifaddrs(h);
134 	if (ret == 0) {
135 		struct ifaddrs *ifa;
136 		char *ifname = NULL;
137 
138 		for (ifa = h->ifap; ifa; ifa = ifa->ifa_next) {
139 			if (ifname != ifa->ifa_name) {
140 				ifname = ifa->ifa_name;
141 				cb(h, ifa, udata);
142 			}
143 		}
144 	}
145 	/* Free ifaddrs so we don't accidentally cache stale data */
146 	freeifaddrs(h->ifap);
147 	h->ifap = NULL;
148 
149 	return (ret);
150 }
151 
152 void
153 ifconfig_foreach_ifaddr(ifconfig_handle_t *h, struct ifaddrs *ifa,
154     ifconfig_foreach_func_t cb, void *udata)
155 {
156 	struct ifaddrs *ift;
157 
158 	for (ift = ifa;
159 	    ift != NULL &&
160 	    ift->ifa_addr != NULL &&
161 	    strcmp(ift->ifa_name, ifa->ifa_name) == 0;
162 	    ift = ift->ifa_next) {
163 		cb(h, ift, udata);
164 	}
165 }
166 
167 int
168 ifconfig_get_description(ifconfig_handle_t *h, const char *name,
169     char **description)
170 {
171 	struct ifreq ifr;
172 	char *descr;
173 	size_t descrlen;
174 
175 	descr = NULL;
176 	descrlen = 64;
177 	memset(&ifr, 0, sizeof(ifr));
178 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
179 
180 	for (;;) {
181 		if ((descr = reallocf(descr, descrlen)) == NULL) {
182 			h->error.errtype = OTHER;
183 			h->error.errcode = ENOMEM;
184 			return (-1);
185 		}
186 
187 		ifr.ifr_buffer.buffer = descr;
188 		ifr.ifr_buffer.length = descrlen;
189 		if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFDESCR, &ifr) != 0) {
190 			free(descr);
191 			return (-1);
192 		}
193 
194 		if (ifr.ifr_buffer.buffer == descr) {
195 			if (strlen(descr) > 0) {
196 				*description = strdup(descr);
197 				free(descr);
198 
199 				if (description == NULL) {
200 					h->error.errtype = OTHER;
201 					h->error.errcode = ENOMEM;
202 					return (-1);
203 				}
204 
205 				return (0);
206 			}
207 		} else if (ifr.ifr_buffer.length > descrlen) {
208 			descrlen = ifr.ifr_buffer.length;
209 			continue;
210 		}
211 		break;
212 	}
213 	free(descr);
214 	h->error.errtype = OTHER;
215 	h->error.errcode = 0;
216 	return (-1);
217 }
218 
219 int
220 ifconfig_set_description(ifconfig_handle_t *h, const char *name,
221     const char *newdescription)
222 {
223 	struct ifreq ifr;
224 	int desclen;
225 
226 	memset(&ifr, 0, sizeof(ifr));
227 	desclen = strlen(newdescription);
228 
229 	/*
230 	 * Unset description if the new description is 0 characters long.
231 	 * TODO: Decide whether this should be an error condition instead.
232 	 */
233 	if (desclen == 0) {
234 		return (ifconfig_unset_description(h, name));
235 	}
236 
237 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
238 	ifr.ifr_buffer.length = desclen + 1;
239 	ifr.ifr_buffer.buffer = strdup(newdescription);
240 
241 	if (ifr.ifr_buffer.buffer == NULL) {
242 		h->error.errtype = OTHER;
243 		h->error.errcode = ENOMEM;
244 		return (-1);
245 	}
246 
247 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR, &ifr) != 0) {
248 		free(ifr.ifr_buffer.buffer);
249 		return (-1);
250 	}
251 
252 	free(ifr.ifr_buffer.buffer);
253 	return (0);
254 }
255 
256 int
257 ifconfig_unset_description(ifconfig_handle_t *h, const char *name)
258 {
259 	struct ifreq ifr;
260 
261 	memset(&ifr, 0, sizeof(ifr));
262 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
263 	ifr.ifr_buffer.length = 0;
264 	ifr.ifr_buffer.buffer = NULL;
265 
266 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR, &ifr) < 0) {
267 		return (-1);
268 	}
269 	return (0);
270 }
271 
272 int
273 ifconfig_set_name(ifconfig_handle_t *h, const char *name, const char *newname)
274 {
275 	struct ifreq ifr;
276 	char *tmpname;
277 
278 	memset(&ifr, 0, sizeof(ifr));
279 	tmpname = strdup(newname);
280 	if (tmpname == NULL) {
281 		h->error.errtype = OTHER;
282 		h->error.errcode = ENOMEM;
283 		return (-1);
284 	}
285 
286 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
287 	ifr.ifr_data = tmpname;
288 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFNAME, &ifr) != 0) {
289 		free(tmpname);
290 		return (-1);
291 	}
292 
293 	free(tmpname);
294 	return (0);
295 }
296 
297 int
298 ifconfig_get_orig_name(ifconfig_handle_t *h, const char *ifname,
299     char **orig_name)
300 {
301 	size_t len;
302 	unsigned int ifindex;
303 	int name[6];
304 
305 	ifindex = if_nametoindex(ifname);
306 	if (ifindex == 0) {
307 		goto fail;
308 	}
309 
310 	name[0] = CTL_NET;
311 	name[1] = PF_LINK;
312 	name[2] = NETLINK_GENERIC;
313 	name[3] = IFMIB_IFDATA;
314 	name[4] = ifindex;
315 	name[5] = IFDATA_DRIVERNAME;
316 
317 	len = 0;
318 	if (sysctl(name, 6, NULL, &len, 0, 0) < 0) {
319 		goto fail;
320 	}
321 
322 	*orig_name = malloc(len);
323 	if (*orig_name == NULL) {
324 		goto fail;
325 	}
326 
327 	if (sysctl(name, 6, *orig_name, &len, 0, 0) < 0) {
328 		free(*orig_name);
329 		*orig_name = NULL;
330 		goto fail;
331 	}
332 
333 	return (0);
334 
335 fail:
336 	h->error.errtype = OTHER;
337 	h->error.errcode = (errno != 0) ? errno : ENOENT;
338 	return (-1);
339 }
340 
341 int
342 ifconfig_get_fib(ifconfig_handle_t *h, const char *name, int *fib)
343 {
344 	struct ifreq ifr;
345 
346 	memset(&ifr, 0, sizeof(ifr));
347 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
348 
349 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFFIB, &ifr) == -1) {
350 		return (-1);
351 	}
352 
353 	*fib = ifr.ifr_fib;
354 	return (0);
355 }
356 
357 int
358 ifconfig_set_mtu(ifconfig_handle_t *h, const char *name, const int mtu)
359 {
360 	struct ifreq ifr;
361 
362 	memset(&ifr, 0, sizeof(ifr));
363 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
364 	ifr.ifr_mtu = mtu;
365 
366 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMTU, &ifr) < 0) {
367 		return (-1);
368 	}
369 
370 	return (0);
371 }
372 
373 int
374 ifconfig_get_mtu(ifconfig_handle_t *h, const char *name, int *mtu)
375 {
376 	struct ifreq ifr;
377 
378 	memset(&ifr, 0, sizeof(ifr));
379 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
380 
381 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMTU, &ifr) == -1) {
382 		return (-1);
383 	}
384 
385 	*mtu = ifr.ifr_mtu;
386 	return (0);
387 }
388 
389 int
390 ifconfig_get_nd6(ifconfig_handle_t *h, const char *name,
391     struct in6_ndireq *nd)
392 {
393 	memset(nd, 0, sizeof(*nd));
394 	strlcpy(nd->ifname, name, sizeof(nd->ifname));
395 	if (ifconfig_ioctlwrap(h, AF_INET6, SIOCGIFINFO_IN6, nd) == -1) {
396 		return (-1);
397 	}
398 	if (isnd6defif(h, name)) {
399 		nd->ndi.flags |= ND6_IFF_DEFAULTIF;
400 	} else if (h->error.errtype != OK) {
401 		return (-1);
402 	}
403 
404 	return (0);
405 }
406 
407 int
408 ifconfig_set_metric(ifconfig_handle_t *h, const char *name, const int metric)
409 {
410 	struct ifreq ifr;
411 
412 	memset(&ifr, 0, sizeof(ifr));
413 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
414 	ifr.ifr_metric = metric;
415 
416 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMETRIC, &ifr) < 0) {
417 		return (-1);
418 	}
419 
420 	return (0);
421 }
422 
423 int
424 ifconfig_get_metric(ifconfig_handle_t *h, const char *name, int *metric)
425 {
426 	struct ifreq ifr;
427 
428 	memset(&ifr, 0, sizeof(ifr));
429 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
430 
431 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMETRIC, &ifr) == -1) {
432 		return (-1);
433 	}
434 
435 	*metric = ifr.ifr_metric;
436 	return (0);
437 }
438 
439 int
440 ifconfig_set_capability(ifconfig_handle_t *h, const char *name,
441     const int capability)
442 {
443 	struct ifreq ifr;
444 	struct ifconfig_capabilities ifcap;
445 	int flags, value;
446 
447 	memset(&ifr, 0, sizeof(ifr));
448 
449 	if (ifconfig_get_capability(h, name, &ifcap) != 0) {
450 		return (-1);
451 	}
452 
453 	value = capability;
454 	flags = ifcap.curcap;
455 	if (value < 0) {
456 		value = -value;
457 		flags &= ~value;
458 	} else {
459 		flags |= value;
460 	}
461 	flags &= ifcap.reqcap;
462 
463 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
464 
465 	/*
466 	 * TODO: Verify that it's safe to not have ifr.ifr_curcap
467 	 * set for this request.
468 	 */
469 	ifr.ifr_reqcap = flags;
470 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFCAP, &ifr) < 0) {
471 		return (-1);
472 	}
473 	return (0);
474 }
475 
476 int
477 ifconfig_get_capability(ifconfig_handle_t *h, const char *name,
478     struct ifconfig_capabilities *capability)
479 {
480 	struct ifreq ifr;
481 
482 	memset(&ifr, 0, sizeof(ifr));
483 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
484 
485 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFCAP, &ifr) < 0) {
486 		return (-1);
487 	}
488 	capability->curcap = ifr.ifr_curcap;
489 	capability->reqcap = ifr.ifr_reqcap;
490 	return (0);
491 }
492 
493 int
494 ifconfig_get_groups(ifconfig_handle_t *h, const char *name,
495     struct ifgroupreq *ifgr)
496 {
497 	int len;
498 
499 	memset(ifgr, 0, sizeof(*ifgr));
500 	strlcpy(ifgr->ifgr_name, name, IFNAMSIZ);
501 
502 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFGROUP, ifgr) == -1) {
503 		if ((h->error.errcode == EINVAL) ||
504 		    (h->error.errcode == ENOTTY)) {
505 			return (0);
506 		} else {
507 			return (-1);
508 		}
509 	}
510 
511 	len = ifgr->ifgr_len;
512 	ifgr->ifgr_groups = (struct ifg_req *)malloc(len);
513 	if (ifgr->ifgr_groups == NULL) {
514 		h->error.errtype = OTHER;
515 		h->error.errcode = ENOMEM;
516 		return (-1);
517 	}
518 	bzero(ifgr->ifgr_groups, len);
519 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFGROUP, ifgr) == -1) {
520 		return (-1);
521 	}
522 
523 	return (0);
524 }
525 
526 int
527 ifconfig_get_ifstatus(ifconfig_handle_t *h, const char *name,
528     struct ifstat *ifs)
529 {
530 	strlcpy(ifs->ifs_name, name, sizeof(ifs->ifs_name));
531 	return (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFSTATUS, ifs));
532 }
533 
534 int
535 ifconfig_destroy_interface(ifconfig_handle_t *h, const char *name)
536 {
537 	struct ifreq ifr;
538 
539 	memset(&ifr, 0, sizeof(ifr));
540 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
541 
542 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFDESTROY, &ifr) < 0) {
543 		return (-1);
544 	}
545 	return (0);
546 }
547 
548 int
549 ifconfig_create_interface(ifconfig_handle_t *h, const char *name, char **ifname)
550 {
551 	struct ifreq ifr;
552 
553 	memset(&ifr, 0, sizeof(ifr));
554 
555 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
556 
557 	/*
558 	 * TODO:
559 	 * Insert special snowflake handling here. See GitHub issue #12 for details.
560 	 * In the meantime, hard-nosupport interfaces that need special handling.
561 	 */
562 	if ((strncmp(name, "wlan",
563 	    strlen("wlan")) == 0) ||
564 	    (strncmp(name, "vlan",
565 	    strlen("vlan")) == 0) ||
566 	    (strncmp(name, "vxlan",
567 	    strlen("vxlan")) == 0)) {
568 		h->error.errtype = OTHER;
569 		h->error.errcode = ENOSYS;
570 		return (-1);
571 	}
572 
573 	/* No special handling for this interface type. */
574 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFCREATE2, &ifr) < 0) {
575 		return (-1);
576 	}
577 
578 	*ifname = strdup(ifr.ifr_name);
579 	if (ifname == NULL) {
580 		h->error.errtype = OTHER;
581 		h->error.errcode = ENOMEM;
582 		return (-1);
583 	}
584 
585 	return (0);
586 }
587 
588 int
589 ifconfig_create_interface_vlan(ifconfig_handle_t *h, const char *name,
590     char **ifname, const char *vlandev, const unsigned short vlantag)
591 {
592 	struct ifreq ifr;
593 	struct vlanreq params;
594 
595 	if ((vlantag == NOTAG) || (vlandev[0] == '\0')) {
596 		// TODO: Add proper error tracking here
597 		return (-1);
598 	}
599 
600 	bzero(&params, sizeof(params));
601 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
602 	params.vlr_tag = vlantag;
603 	(void)strlcpy(params.vlr_parent, vlandev, sizeof(params.vlr_parent));
604 	ifr.ifr_data = (caddr_t)&params;
605 
606 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFCREATE2, &ifr) < 0) {
607 		// TODO: Add proper error tracking here
608 		return (-1);
609 	}
610 
611 	*ifname = strdup(ifr.ifr_name);
612 	return (0);
613 }
614 
615 int
616 ifconfig_set_vlantag(ifconfig_handle_t *h, const char *name,
617     const char *vlandev, const unsigned short vlantag)
618 {
619 	struct ifreq ifr;
620 	struct vlanreq params;
621 
622 	bzero(&params, sizeof(params));
623 	params.vlr_tag = vlantag;
624 	strlcpy(params.vlr_parent, vlandev, sizeof(params.vlr_parent));
625 
626 	ifr.ifr_data = (caddr_t)&params;
627 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
628 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSETVLAN, &ifr) == -1) {
629 		return (-1);
630 	}
631 	return (0);
632 }
633 
634 int
635 ifconfig_list_cloners(ifconfig_handle_t *h, char **bufp, size_t *lenp)
636 {
637 	struct if_clonereq ifcr;
638 	char *buf;
639 
640 	memset(&ifcr, 0, sizeof(ifcr));
641 	*bufp = NULL;
642 	*lenp = 0;
643 
644 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFGCLONERS, &ifcr) < 0)
645 		return (-1);
646 
647 	buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
648 	if (buf == NULL) {
649 		h->error.errtype = OTHER;
650 		h->error.errcode = ENOMEM;
651 		return (-1);
652 	}
653 
654 	ifcr.ifcr_count = ifcr.ifcr_total;
655 	ifcr.ifcr_buffer = buf;
656 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFGCLONERS, &ifcr) < 0) {
657 		free(buf);
658 		return (-1);
659 	}
660 
661 	*bufp = buf;
662 	*lenp = ifcr.ifcr_total;
663 	return (0);
664 }
665