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
58static bool
59isnd6defif(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
74ifconfig_handle_t *
75ifconfig_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
91void
92ifconfig_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
104ifconfig_errtype
105ifconfig_err_errtype(ifconfig_handle_t *h)
106{
107
108	return (h->error.errtype);
109}
110
111int
112ifconfig_err_errno(ifconfig_handle_t *h)
113{
114
115	return (h->error.errcode);
116}
117
118unsigned long
119ifconfig_err_ioctlreq(ifconfig_handle_t *h)
120{
121
122	return (h->error.ioctl_request);
123}
124
125int
126ifconfig_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
150void
151ifconfig_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
165int
166ifconfig_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
217int
218ifconfig_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
254int
255ifconfig_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
270int
271ifconfig_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
295int
296ifconfig_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
333fail:
334	h->error.errtype = OTHER;
335	h->error.errcode = (errno != 0) ? errno : ENOENT;
336	return (-1);
337}
338
339int
340ifconfig_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
355int
356ifconfig_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
371int
372ifconfig_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
387int
388ifconfig_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
405int
406ifconfig_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
421int
422ifconfig_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
437int
438ifconfig_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
474int
475ifconfig_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
491int
492ifconfig_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
524int
525ifconfig_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
532int
533ifconfig_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
546int
547ifconfig_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
586int
587ifconfig_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
613int
614ifconfig_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
632int
633ifconfig_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