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