]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libifconfig/libifconfig.c
ident(1): Normalizing date format
[FreeBSD/FreeBSD.git] / lib / libifconfig / libifconfig.c
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
632 int
633 ifconfig_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 }