xref: /freebsd/lib/libifconfig/libifconfig.c (revision 13464e4a44fc58490a03bb8bfc7e3c972e9c30b2)
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 #include <sys/ioctl.h>
65 
66 #include <net/if.h>
67 
68 #include <err.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
75 
76 #include "libifconfig.h"
77 #include "libifconfig_internal.h"
78 
79 
80 ifconfig_handle_t *
81 ifconfig_open(void)
82 {
83 	struct ifconfig_handle *h;
84 
85 	h = calloc(1, sizeof(*h));
86 	for (int i = 0; i <= AF_MAX; i++) {
87 		h->sockets[i] = -1;
88 	}
89 	return (h);
90 }
91 
92 void
93 ifconfig_close(ifconfig_handle_t *h)
94 {
95 
96 	for (int i = 0; i <= AF_MAX; i++) {
97 		if (h->sockets[i] != -1) {
98 			(void)close(h->sockets[i]);
99 		}
100 	}
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_get_description(ifconfig_handle_t *h, const char *name,
127     char **description)
128 {
129 	struct ifreq ifr;
130 	char *descr;
131 	size_t descrlen;
132 
133 	descr = NULL;
134 	descrlen = 64;
135 	memset(&ifr, 0, sizeof(ifr));
136 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
137 
138 	for (;;) {
139 		if ((descr = reallocf(descr, descrlen)) == NULL) {
140 			h->error.errtype = OTHER;
141 			h->error.errcode = ENOMEM;
142 			return (-1);
143 		}
144 
145 		ifr.ifr_buffer.buffer = descr;
146 		ifr.ifr_buffer.length = descrlen;
147 		if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFDESCR, &ifr) != 0) {
148 			return (-1);
149 		}
150 
151 		if (ifr.ifr_buffer.buffer == descr) {
152 			if (strlen(descr) > 0) {
153 				*description = strdup(descr);
154 				free(descr);
155 				return (0);
156 			}
157 		} else if (ifr.ifr_buffer.length > descrlen) {
158 			descrlen = ifr.ifr_buffer.length;
159 			continue;
160 		}
161 		break;
162 	}
163 	free(descr);
164 	h->error.errtype = OTHER;
165 	h->error.errcode = 0;
166 	return (-1);
167 }
168 
169 int
170 ifconfig_set_description(ifconfig_handle_t *h, const char *name,
171     const char *newdescription)
172 {
173 	struct ifreq ifr;
174 	int desclen;
175 
176 	memset(&ifr, 0, sizeof(ifr));
177 	desclen = strlen(newdescription);
178 
179 	/*
180 	 * Unset description if the new description is 0 characters long.
181 	 * TODO: Decide whether this should be an error condition instead.
182 	 */
183 	if (desclen == 0) {
184 		return (ifconfig_unset_description(h, name));
185 	}
186 
187 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
188 	ifr.ifr_buffer.length = desclen + 1;
189 	ifr.ifr_buffer.buffer = strdup(newdescription);
190 
191 	if (ifr.ifr_buffer.buffer == NULL) {
192 		h->error.errtype = OTHER;
193 		h->error.errcode = ENOMEM;
194 		return (-1);
195 	}
196 
197 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR,
198 	    &ifr) != 0) {
199 		free(ifr.ifr_buffer.buffer);
200 		return (-1);
201 	}
202 
203 	free(ifr.ifr_buffer.buffer);
204 	return (0);
205 }
206 
207 int
208 ifconfig_unset_description(ifconfig_handle_t *h, const char *name)
209 {
210 	struct ifreq ifr;
211 
212 	memset(&ifr, 0, sizeof(ifr));
213 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
214 	ifr.ifr_buffer.length = 0;
215 	ifr.ifr_buffer.buffer = NULL;
216 
217 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR,
218 	    &ifr) < 0) {
219 		return (-1);
220 	}
221 	return (0);
222 }
223 
224 int
225 ifconfig_set_name(ifconfig_handle_t *h, const char *name, const char *newname)
226 {
227 	struct ifreq ifr;
228 	char *tmpname;
229 
230 	memset(&ifr, 0, sizeof(ifr));
231 	tmpname = strdup(newname);
232 	if (tmpname == NULL) {
233 		h->error.errtype = OTHER;
234 		h->error.errcode = ENOMEM;
235 		return (-1);
236 	}
237 
238 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
239 	ifr.ifr_data = tmpname;
240 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFNAME,
241 	    &ifr) != 0) {
242 		free(tmpname);
243 		return (-1);
244 	}
245 
246 	free(tmpname);
247 	return (0);
248 }
249 
250 int
251 ifconfig_set_mtu(ifconfig_handle_t *h, const char *name, const int mtu)
252 {
253 	struct ifreq ifr;
254 
255 	memset(&ifr, 0, sizeof(ifr));
256 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
257 	ifr.ifr_mtu = mtu;
258 
259 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMTU,
260 	    &ifr) < 0) {
261 		return (-1);
262 	}
263 
264 	return (0);
265 }
266 
267 int
268 ifconfig_get_mtu(ifconfig_handle_t *h, const char *name, int *mtu)
269 {
270 	struct ifreq ifr;
271 
272 	memset(&ifr, 0, sizeof(ifr));
273 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
274 
275 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMTU,
276 	    &ifr) == -1) {
277 		return (-1);
278 	}
279 
280 	*mtu = ifr.ifr_mtu;
281 	return (0);
282 }
283 
284 int
285 ifconfig_set_metric(ifconfig_handle_t *h, const char *name, const int mtu)
286 {
287 	struct ifreq ifr;
288 
289 	memset(&ifr, 0, sizeof(ifr));
290 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
291 	ifr.ifr_mtu = mtu;
292 
293 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMETRIC,
294 	    &ifr) < 0) {
295 		return (-1);
296 	}
297 
298 	return (0);
299 }
300 
301 int
302 ifconfig_get_metric(ifconfig_handle_t *h, const char *name, int *metric)
303 {
304 	struct ifreq ifr;
305 
306 	memset(&ifr, 0, sizeof(ifr));
307 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
308 
309 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMETRIC,
310 	    &ifr) == -1) {
311 		return (-1);
312 	}
313 
314 	*metric = ifr.ifr_metric;
315 	return (0);
316 }
317 
318 int
319 ifconfig_set_capability(ifconfig_handle_t *h, const char *name,
320     const int capability)
321 {
322 	struct ifreq ifr;
323 	struct ifconfig_capabilities ifcap;
324 	int flags, value;
325 
326 	memset(&ifr, 0, sizeof(ifr));
327 
328 	if (ifconfig_get_capability(h, name,
329 	    &ifcap) != 0) {
330 		return (-1);
331 	}
332 
333 	value = capability;
334 	flags = ifcap.curcap;
335 	if (value < 0) {
336 		value = -value;
337 		flags &= ~value;
338 	} else {
339 		flags |= value;
340 	}
341 	flags &= ifcap.reqcap;
342 
343 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
344 
345 	/*
346 	 * TODO: Verify that it's safe to not have ifr.ifr_curcap
347 	 * set for this request.
348 	 */
349 	ifr.ifr_reqcap = flags;
350 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFCAP,
351 	    &ifr) < 0) {
352 		return (-1);
353 	}
354 	return (0);
355 }
356 
357 int
358 ifconfig_get_capability(ifconfig_handle_t *h, const char *name,
359     struct ifconfig_capabilities *capability)
360 {
361 	struct ifreq ifr;
362 
363 	memset(&ifr, 0, sizeof(ifr));
364 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
365 
366 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFCAP,
367 	    &ifr) < 0) {
368 		return (-1);
369 	}
370 	capability->curcap = ifr.ifr_curcap;
371 	capability->reqcap = ifr.ifr_reqcap;
372 	return (0);
373 }
374 
375 int
376 ifconfig_destroy_interface(ifconfig_handle_t *h, const char *name)
377 {
378 	struct ifreq ifr;
379 
380 	memset(&ifr, 0, sizeof(ifr));
381 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
382 
383 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFDESTROY,
384 	    &ifr) < 0) {
385 		return (-1);
386 	}
387 	return (0);
388 }
389 
390 int
391 ifconfig_create_interface(ifconfig_handle_t *h, const char *name, char **ifname)
392 {
393 	struct ifreq ifr;
394 
395 	memset(&ifr, 0, sizeof(ifr));
396 	(void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
397 
398 	/*
399 	 * TODO:
400 	 * Insert special snowflake handling here. See GitHub issue #12 for details.
401 	 * In the meantime, hard-nosupport interfaces that need special handling.
402 	 */
403 	if ((strncmp(name, "wlan",
404 	    strlen("wlan")) == 0) ||
405 	    (strncmp(name, "vlan",
406 	    strlen("vlan")) == 0) ||
407 	    (strncmp(name, "vxlan",
408 	    strlen("vxlan")) == 0)) {
409 		h->error.errtype = OTHER;
410 		h->error.errcode = ENOSYS;
411 		return (-1);
412 	}
413 
414 	/* No special handling for this interface type. */
415 	if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFCREATE2, &ifr) < 0) {
416 		return (-1);
417 	}
418 
419 	*ifname = strdup(ifr.ifr_name);
420 	return (0);
421 }
422