xref: /freebsd/lib/libifconfig/libifconfig.c (revision b37f6c9805edb4b89f0a8c2b78f78a3dcfc0647b)
1 /*
2  * Copyright (c) 2016, Marie Helene Kvello-Aune
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * thislist of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 /*
34  * Copyright (c) 1983, 1993
35  *  The Regents of the University of California.  All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. Neither the name of the University nor the names of its contributors
46  *    may be used to endorse or promote products derived from this software
47  *    without specific prior written permission.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  *
61  * $FreeBSD$
62  */
63 
64  /*
65  * Copyright 1996 Massachusetts Institute of Technology
66  *
67  * Permission to use, copy, modify, and distribute this software and
68  * its documentation for any purpose and without fee is hereby
69  * granted, provided that both the above copyright notice and this
70  * permission notice appear in all copies, that both the above
71  * copyright notice and this permission notice appear in all
72  * supporting documentation, and that the name of M.I.T. not be used
73  * in advertising or publicity pertaining to distribution of the
74  * software without specific, written prior permission.  M.I.T. makes
75  * no representations about the suitability of this software for any
76  * purpose.  It is provided "as is" without express or implied
77  * warranty.
78  *
79  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
80  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
81  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
82  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
83  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
84  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
85  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
86  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
87  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
88  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
89  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
90  * SUCH DAMAGE.
91  *
92  * $FreeBSD$
93  */
94 
95 #include <sys/types.h>
96 #include <sys/ioctl.h>
97 #include <sys/sysctl.h>
98 
99 #include <net/if.h>
100 #include <net/if_mib.h>
101 
102 #include <err.h>
103 #include <errno.h>
104 #include <fcntl.h>
105 #include <stdio.h>
106 #include <stdlib.h>
107 #include <string.h>
108 #include <unistd.h>
109 
110 #include "libifconfig.h"
111 #include "libifconfig_internal.h"
112 
113 
114 ifconfig_handle_t *
115 ifconfig_open(void)
116 {
117 	struct ifconfig_handle *h;
118 
119 	h = calloc(1, sizeof(*h));
120 	for (int i = 0; i <= AF_MAX; i++) {
121 		h->sockets[i] = -1;
122 	}
123 	return (h);
124 }
125 
126 void
127 ifconfig_close(ifconfig_handle_t *h)
128 {
129 
130 	for (int i = 0; i <= AF_MAX; i++) {
131 		if (h->sockets[i] != -1) {
132 			(void)close(h->sockets[i]);
133 		}
134 	}
135 	free(h);
136 }
137 
138 ifconfig_errtype
139 ifconfig_err_errtype(ifconfig_handle_t *h)
140 {
141 
142 	return (h->error.errtype);
143 }
144 
145 int
146 ifconfig_err_errno(ifconfig_handle_t *h)
147 {
148 
149 	return (h->error.errcode);
150 }
151 
152 unsigned long
153 ifconfig_err_ioctlreq(ifconfig_handle_t *h)
154 {
155 
156 	return (h->error.ioctl_request);
157 }
158 
159 int
160 ifconfig_get_description(ifconfig_handle_t *h, const char *name,
161     char **description)
162 {
163 	struct ifreq ifr;
164 	char *descr;
165 	size_t descrlen;
166 
167 	descr = NULL;
168 	descrlen = 64;
169 	memset(&ifr, 0, sizeof(ifr));
170 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
171 
172 	for (;;) {
173 		if ((descr = reallocf(descr, descrlen)) == NULL) {
174 			h->error.errtype = OTHER;
175 			h->error.errcode = ENOMEM;
176 			return (-1);
177 		}
178 
179 		ifr.ifr_buffer.buffer = descr;
180 		ifr.ifr_buffer.length = descrlen;
181 		if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFDESCR, &ifr) != 0) {
182 			return (-1);
183 		}
184 
185 		if (ifr.ifr_buffer.buffer == descr) {
186 			if (strlen(descr) > 0) {
187 				*description = strdup(descr);
188 				free(descr);
189 				return (0);
190 			}
191 		} else if (ifr.ifr_buffer.length > descrlen) {
192 			descrlen = ifr.ifr_buffer.length;
193 			continue;
194 		}
195 		break;
196 	}
197 	free(descr);
198 	h->error.errtype = OTHER;
199 	h->error.errcode = 0;
200 	return (-1);
201 }
202 
203 int
204 ifconfig_set_description(ifconfig_handle_t *h, const char *name,
205     const char *newdescription)
206 {
207 	struct ifreq ifr;
208 	int desclen;
209 
210 	memset(&ifr, 0, sizeof(ifr));
211 	desclen = strlen(newdescription);
212 
213 	/*
214 	 * Unset description if the new description is 0 characters long.
215 	 * TODO: Decide whether this should be an error condition instead.
216 	 */
217 	if (desclen == 0) {
218 		return (ifconfig_unset_description(h, name));
219 	}
220 
221 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
222 	ifr.ifr_buffer.length = desclen + 1;
223 	ifr.ifr_buffer.buffer = strdup(newdescription);
224 
225 	if (ifr.ifr_buffer.buffer == NULL) {
226 		h->error.errtype = OTHER;
227 		h->error.errcode = ENOMEM;
228 		return (-1);
229 	}
230 
231 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR,
232 	    &ifr) != 0) {
233 		free(ifr.ifr_buffer.buffer);
234 		return (-1);
235 	}
236 
237 	free(ifr.ifr_buffer.buffer);
238 	return (0);
239 }
240 
241 int
242 ifconfig_unset_description(ifconfig_handle_t *h, const char *name)
243 {
244 	struct ifreq ifr;
245 
246 	memset(&ifr, 0, sizeof(ifr));
247 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
248 	ifr.ifr_buffer.length = 0;
249 	ifr.ifr_buffer.buffer = NULL;
250 
251 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR,
252 	    &ifr) < 0) {
253 		return (-1);
254 	}
255 	return (0);
256 }
257 
258 int
259 ifconfig_set_name(ifconfig_handle_t *h, const char *name, const char *newname)
260 {
261 	struct ifreq ifr;
262 	char *tmpname;
263 
264 	memset(&ifr, 0, sizeof(ifr));
265 	tmpname = strdup(newname);
266 	if (tmpname == NULL) {
267 		h->error.errtype = OTHER;
268 		h->error.errcode = ENOMEM;
269 		return (-1);
270 	}
271 
272 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
273 	ifr.ifr_data = tmpname;
274 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFNAME,
275 	    &ifr) != 0) {
276 		free(tmpname);
277 		return (-1);
278 	}
279 
280 	free(tmpname);
281 	return (0);
282 }
283 
284 int
285 ifconfig_get_orig_name(ifconfig_handle_t *h, const char *ifname,
286     char **orig_name)
287 {
288 	struct ifmibdata ifmd;
289 	size_t len;
290 	int name[6];
291 	int i, maxifno;
292 
293 	name[0] = CTL_NET;
294 	name[1] = PF_LINK;
295 	name[2] = NETLINK_GENERIC;
296 	name[3] = IFMIB_SYSTEM;
297 	name[4] = IFMIB_IFCOUNT;
298 
299 	len = sizeof maxifno;
300 	if (sysctl(name, 5, &maxifno, &len, 0, 0) < 0) {
301 		h->error.errtype = OTHER;
302 		h->error.errcode = errno;
303 		return (-1);
304 	}
305 
306 	name[3] = IFMIB_IFDATA;
307 	name[5] = IFDATA_GENERAL;
308 	for (i = 1; i <= maxifno; i++) {
309 		len = sizeof ifmd;
310 		name[4] = i;
311 		if (sysctl(name, 6, &ifmd, &len, 0, 0) < 0) {
312 			if (errno == ENOENT)
313 				continue;
314 
315 			goto fail;
316 		}
317 
318 		if (strncmp(ifmd.ifmd_name, ifname, IFNAMSIZ) != 0)
319 			continue;
320 
321 		len = 0;
322 		name[5] = IFDATA_DRIVERNAME;
323 		if (sysctl(name, 6, NULL, &len, 0, 0) < 0)
324 			goto fail;
325 
326 		*orig_name = malloc(len);
327 		if (*orig_name == NULL)
328 			goto fail;
329 
330 		if (sysctl(name, 6, *orig_name, &len, 0, 0) < 0) {
331 			free(*orig_name);
332 			*orig_name = NULL;
333 			goto fail;
334 		}
335 
336 		return (0);
337 	}
338 
339 fail:
340 	h->error.errtype = OTHER;
341 	h->error.errcode = (i <= maxifno) ? errno : ENOENT;
342 	return (-1);
343 }
344 
345 int
346 ifconfig_set_mtu(ifconfig_handle_t *h, const char *name, const int mtu)
347 {
348 	struct ifreq ifr;
349 
350 	memset(&ifr, 0, sizeof(ifr));
351 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
352 	ifr.ifr_mtu = mtu;
353 
354 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMTU,
355 	    &ifr) < 0) {
356 		return (-1);
357 	}
358 
359 	return (0);
360 }
361 
362 int
363 ifconfig_get_mtu(ifconfig_handle_t *h, const char *name, int *mtu)
364 {
365 	struct ifreq ifr;
366 
367 	memset(&ifr, 0, sizeof(ifr));
368 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
369 
370 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMTU,
371 	    &ifr) == -1) {
372 		return (-1);
373 	}
374 
375 	*mtu = ifr.ifr_mtu;
376 	return (0);
377 }
378 
379 int
380 ifconfig_set_metric(ifconfig_handle_t *h, const char *name, const int mtu)
381 {
382 	struct ifreq ifr;
383 
384 	memset(&ifr, 0, sizeof(ifr));
385 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
386 	ifr.ifr_mtu = mtu;
387 
388 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMETRIC,
389 	    &ifr) < 0) {
390 		return (-1);
391 	}
392 
393 	return (0);
394 }
395 
396 int
397 ifconfig_get_metric(ifconfig_handle_t *h, const char *name, int *metric)
398 {
399 	struct ifreq ifr;
400 
401 	memset(&ifr, 0, sizeof(ifr));
402 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
403 
404 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMETRIC,
405 	    &ifr) == -1) {
406 		return (-1);
407 	}
408 
409 	*metric = ifr.ifr_metric;
410 	return (0);
411 }
412 
413 int
414 ifconfig_set_capability(ifconfig_handle_t *h, const char *name,
415     const int capability)
416 {
417 	struct ifreq ifr;
418 	struct ifconfig_capabilities ifcap;
419 	int flags, value;
420 
421 	memset(&ifr, 0, sizeof(ifr));
422 
423 	if (ifconfig_get_capability(h, name,
424 	    &ifcap) != 0) {
425 		return (-1);
426 	}
427 
428 	value = capability;
429 	flags = ifcap.curcap;
430 	if (value < 0) {
431 		value = -value;
432 		flags &= ~value;
433 	} else {
434 		flags |= value;
435 	}
436 	flags &= ifcap.reqcap;
437 
438 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
439 
440 	/*
441 	 * TODO: Verify that it's safe to not have ifr.ifr_curcap
442 	 * set for this request.
443 	 */
444 	ifr.ifr_reqcap = flags;
445 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFCAP,
446 	    &ifr) < 0) {
447 		return (-1);
448 	}
449 	return (0);
450 }
451 
452 int
453 ifconfig_get_capability(ifconfig_handle_t *h, const char *name,
454     struct ifconfig_capabilities *capability)
455 {
456 	struct ifreq ifr;
457 
458 	memset(&ifr, 0, sizeof(ifr));
459 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
460 
461 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFCAP,
462 	    &ifr) < 0) {
463 		return (-1);
464 	}
465 	capability->curcap = ifr.ifr_curcap;
466 	capability->reqcap = ifr.ifr_reqcap;
467 	return (0);
468 }
469 
470 int
471 ifconfig_destroy_interface(ifconfig_handle_t *h, const char *name)
472 {
473 	struct ifreq ifr;
474 
475 	memset(&ifr, 0, sizeof(ifr));
476 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
477 
478 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFDESTROY,
479 	    &ifr) < 0) {
480 		return (-1);
481 	}
482 	return (0);
483 }
484 
485 int
486 ifconfig_create_interface(ifconfig_handle_t *h, const char *name, char **ifname)
487 {
488 	struct ifreq ifr;
489 
490 	memset(&ifr, 0, sizeof(ifr));
491 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
492 
493 	/*
494 	 * TODO:
495 	 * Insert special snowflake handling here. See GitHub issue #12 for details.
496 	 * In the meantime, hard-nosupport interfaces that need special handling.
497 	 */
498 	if ((strncmp(name, "wlan",
499 	    strlen("wlan")) == 0) ||
500 	    (strncmp(name, "vlan",
501 	    strlen("vlan")) == 0) ||
502 	    (strncmp(name, "vxlan",
503 	    strlen("vxlan")) == 0)) {
504 		h->error.errtype = OTHER;
505 		h->error.errcode = ENOSYS;
506 		return (-1);
507 	}
508 
509 	/* No special handling for this interface type. */
510 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFCREATE2, &ifr) < 0) {
511 		return (-1);
512 	}
513 
514 	*ifname = strdup(ifr.ifr_name);
515 	return (0);
516 }
517