xref: /freebsd/lib/libifconfig/libifconfig.c (revision d3d381b2b194b4d24853e92eecef55f262688d1a)
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 		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