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
60static bool
61isnd6defif(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
76ifconfig_handle_t *
77ifconfig_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
93void
94ifconfig_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
106ifconfig_errtype
107ifconfig_err_errtype(ifconfig_handle_t *h)
108{
109
110	return (h->error.errtype);
111}
112
113int
114ifconfig_err_errno(ifconfig_handle_t *h)
115{
116
117	return (h->error.errcode);
118}
119
120unsigned long
121ifconfig_err_ioctlreq(ifconfig_handle_t *h)
122{
123
124	return (h->error.ioctl_request);
125}
126
127int
128ifconfig_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
152void
153ifconfig_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
167int
168ifconfig_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
219int
220ifconfig_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
256int
257ifconfig_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
272int
273ifconfig_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
297int
298ifconfig_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
335fail:
336	h->error.errtype = OTHER;
337	h->error.errcode = (errno != 0) ? errno : ENOENT;
338	return (-1);
339}
340
341int
342ifconfig_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
357int
358ifconfig_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
373int
374ifconfig_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
389int
390ifconfig_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
407int
408ifconfig_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
423int
424ifconfig_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
439int
440ifconfig_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
476int
477ifconfig_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
493int
494ifconfig_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
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