Index: head/lib/libifconfig/libifconfig.c =================================================================== --- head/lib/libifconfig/libifconfig.c (revision 366905) +++ head/lib/libifconfig/libifconfig.c (revision 366906) @@ -1,630 +1,662 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2016-2017, Marie Helene Kvello-Aune. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libifconfig.h" #include "libifconfig_internal.h" #define NOTAG ((u_short) -1) static bool isnd6defif(ifconfig_handle_t *h, const char *name) { struct in6_ndifreq ndifreq; unsigned int ifindex; memset(&ndifreq, 0, sizeof(ndifreq)); strlcpy(ndifreq.ifname, name, sizeof(ndifreq.ifname)); ifindex = if_nametoindex(ndifreq.ifname); if (ifconfig_ioctlwrap(h, AF_INET6, SIOCGDEFIFACE_IN6, &ndifreq) < 0) { return (false); } h->error.errtype = OK; return (ndifreq.ifindex == ifindex); } ifconfig_handle_t * ifconfig_open(void) { ifconfig_handle_t *h; h = calloc(1, sizeof(*h)); if (h == NULL) { return (NULL); } for (int i = 0; i <= AF_MAX; i++) { h->sockets[i] = -1; } return (h); } void ifconfig_close(ifconfig_handle_t *h) { for (int i = 0; i <= AF_MAX; i++) { if (h->sockets[i] != -1) { (void)close(h->sockets[i]); } } freeifaddrs(h->ifap); free(h); } ifconfig_errtype ifconfig_err_errtype(ifconfig_handle_t *h) { return (h->error.errtype); } int ifconfig_err_errno(ifconfig_handle_t *h) { return (h->error.errcode); } unsigned long ifconfig_err_ioctlreq(ifconfig_handle_t *h) { return (h->error.ioctl_request); } int ifconfig_foreach_iface(ifconfig_handle_t *h, ifconfig_foreach_func_t cb, void *udata) { int ret; ret = ifconfig_getifaddrs(h); if (ret == 0) { struct ifaddrs *ifa; char *ifname = NULL; for (ifa = h->ifap; ifa; ifa = ifa->ifa_next) { if (ifname != ifa->ifa_name) { ifname = ifa->ifa_name; cb(h, ifa, udata); } } } /* Free ifaddrs so we don't accidentally cache stale data */ freeifaddrs(h->ifap); h->ifap = NULL; return (ret); } void ifconfig_foreach_ifaddr(ifconfig_handle_t *h, struct ifaddrs *ifa, ifconfig_foreach_func_t cb, void *udata) { struct ifaddrs *ift; for (ift = ifa; ift != NULL && ift->ifa_addr != NULL && strcmp(ift->ifa_name, ifa->ifa_name) == 0; ift = ift->ifa_next) { cb(h, ift, udata); } } int ifconfig_get_description(ifconfig_handle_t *h, const char *name, char **description) { struct ifreq ifr; char *descr; size_t descrlen; descr = NULL; descrlen = 64; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); for (;;) { if ((descr = reallocf(descr, descrlen)) == NULL) { h->error.errtype = OTHER; h->error.errcode = ENOMEM; return (-1); } ifr.ifr_buffer.buffer = descr; ifr.ifr_buffer.length = descrlen; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFDESCR, &ifr) != 0) { free(descr); return (-1); } if (ifr.ifr_buffer.buffer == descr) { if (strlen(descr) > 0) { *description = strdup(descr); free(descr); if (description == NULL) { h->error.errtype = OTHER; h->error.errcode = ENOMEM; return (-1); } return (0); } } else if (ifr.ifr_buffer.length > descrlen) { descrlen = ifr.ifr_buffer.length; continue; } break; } free(descr); h->error.errtype = OTHER; h->error.errcode = 0; return (-1); } int ifconfig_set_description(ifconfig_handle_t *h, const char *name, const char *newdescription) { struct ifreq ifr; int desclen; memset(&ifr, 0, sizeof(ifr)); desclen = strlen(newdescription); /* * Unset description if the new description is 0 characters long. * TODO: Decide whether this should be an error condition instead. */ if (desclen == 0) { return (ifconfig_unset_description(h, name)); } (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_buffer.length = desclen + 1; ifr.ifr_buffer.buffer = strdup(newdescription); if (ifr.ifr_buffer.buffer == NULL) { h->error.errtype = OTHER; h->error.errcode = ENOMEM; return (-1); } if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR, &ifr) != 0) { free(ifr.ifr_buffer.buffer); return (-1); } free(ifr.ifr_buffer.buffer); return (0); } int ifconfig_unset_description(ifconfig_handle_t *h, const char *name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_buffer.length = 0; ifr.ifr_buffer.buffer = NULL; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFDESCR, &ifr) < 0) { return (-1); } return (0); } int ifconfig_set_name(ifconfig_handle_t *h, const char *name, const char *newname) { struct ifreq ifr; char *tmpname; memset(&ifr, 0, sizeof(ifr)); tmpname = strdup(newname); if (tmpname == NULL) { h->error.errtype = OTHER; h->error.errcode = ENOMEM; return (-1); } (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_data = tmpname; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFNAME, &ifr) != 0) { free(tmpname); return (-1); } free(tmpname); return (0); } int ifconfig_get_orig_name(ifconfig_handle_t *h, const char *ifname, char **orig_name) { size_t len; unsigned int ifindex; int name[6]; ifindex = if_nametoindex(ifname); if (ifindex == 0) { goto fail; } name[0] = CTL_NET; name[1] = PF_LINK; name[2] = NETLINK_GENERIC; name[3] = IFMIB_IFDATA; name[4] = ifindex; name[5] = IFDATA_DRIVERNAME; len = 0; if (sysctl(name, 6, NULL, &len, 0, 0) < 0) { goto fail; } *orig_name = malloc(len); if (*orig_name == NULL) { goto fail; } if (sysctl(name, 6, *orig_name, &len, 0, 0) < 0) { free(*orig_name); *orig_name = NULL; goto fail; } return (0); fail: h->error.errtype = OTHER; h->error.errcode = (errno != 0) ? errno : ENOENT; return (-1); } int ifconfig_get_fib(ifconfig_handle_t *h, const char *name, int *fib) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFFIB, &ifr) == -1) { return (-1); } *fib = ifr.ifr_fib; return (0); } int ifconfig_set_mtu(ifconfig_handle_t *h, const char *name, const int mtu) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_mtu = mtu; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMTU, &ifr) < 0) { return (-1); } return (0); } int ifconfig_get_mtu(ifconfig_handle_t *h, const char *name, int *mtu) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMTU, &ifr) == -1) { return (-1); } *mtu = ifr.ifr_mtu; return (0); } int ifconfig_get_nd6(ifconfig_handle_t *h, const char *name, struct in6_ndireq *nd) { memset(nd, 0, sizeof(*nd)); strlcpy(nd->ifname, name, sizeof(nd->ifname)); if (ifconfig_ioctlwrap(h, AF_INET6, SIOCGIFINFO_IN6, nd) == -1) { return (-1); } if (isnd6defif(h, name)) { nd->ndi.flags |= ND6_IFF_DEFAULTIF; } else if (h->error.errtype != OK) { return (-1); } return (0); } int ifconfig_set_metric(ifconfig_handle_t *h, const char *name, const int metric) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_metric = metric; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFMETRIC, &ifr) < 0) { return (-1); } return (0); } int ifconfig_get_metric(ifconfig_handle_t *h, const char *name, int *metric) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFMETRIC, &ifr) == -1) { return (-1); } *metric = ifr.ifr_metric; return (0); } int ifconfig_set_capability(ifconfig_handle_t *h, const char *name, const int capability) { struct ifreq ifr; struct ifconfig_capabilities ifcap; int flags, value; memset(&ifr, 0, sizeof(ifr)); if (ifconfig_get_capability(h, name, &ifcap) != 0) { return (-1); } value = capability; flags = ifcap.curcap; if (value < 0) { value = -value; flags &= ~value; } else { flags |= value; } flags &= ifcap.reqcap; (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); /* * TODO: Verify that it's safe to not have ifr.ifr_curcap * set for this request. */ ifr.ifr_reqcap = flags; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSIFCAP, &ifr) < 0) { return (-1); } return (0); } int ifconfig_get_capability(ifconfig_handle_t *h, const char *name, struct ifconfig_capabilities *capability) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFCAP, &ifr) < 0) { return (-1); } capability->curcap = ifr.ifr_curcap; capability->reqcap = ifr.ifr_reqcap; return (0); } int ifconfig_get_groups(ifconfig_handle_t *h, const char *name, struct ifgroupreq *ifgr) { int len; memset(ifgr, 0, sizeof(*ifgr)); strlcpy(ifgr->ifgr_name, name, IFNAMSIZ); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFGROUP, ifgr) == -1) { if ((h->error.errcode == EINVAL) || (h->error.errcode == ENOTTY)) { return (0); } else { return (-1); } } len = ifgr->ifgr_len; ifgr->ifgr_groups = (struct ifg_req *)malloc(len); if (ifgr->ifgr_groups == NULL) { return (1); } bzero(ifgr->ifgr_groups, len); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFGROUP, ifgr) == -1) { return (-1); } return (0); } int ifconfig_get_ifstatus(ifconfig_handle_t *h, const char *name, struct ifstat *ifs) { strlcpy(ifs->ifs_name, name, sizeof(ifs->ifs_name)); return (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFSTATUS, ifs)); } int ifconfig_destroy_interface(ifconfig_handle_t *h, const char *name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFDESTROY, &ifr) < 0) { return (-1); } return (0); } int ifconfig_create_interface(ifconfig_handle_t *h, const char *name, char **ifname) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); /* * TODO: * Insert special snowflake handling here. See GitHub issue #12 for details. * In the meantime, hard-nosupport interfaces that need special handling. */ if ((strncmp(name, "wlan", strlen("wlan")) == 0) || (strncmp(name, "vlan", strlen("vlan")) == 0) || (strncmp(name, "vxlan", strlen("vxlan")) == 0)) { h->error.errtype = OTHER; h->error.errcode = ENOSYS; return (-1); } /* No special handling for this interface type. */ if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFCREATE2, &ifr) < 0) { return (-1); } *ifname = strdup(ifr.ifr_name); if (ifname == NULL) { h->error.errtype = OTHER; h->error.errcode = ENOMEM; return (-1); } return (0); } int ifconfig_create_interface_vlan(ifconfig_handle_t *h, const char *name, char **ifname, const char *vlandev, const unsigned short vlantag) { struct ifreq ifr; struct vlanreq params; if ((vlantag == NOTAG) || (vlandev[0] == '\0')) { // TODO: Add proper error tracking here return (-1); } bzero(¶ms, sizeof(params)); (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); params.vlr_tag = vlantag; (void)strlcpy(params.vlr_parent, vlandev, sizeof(params.vlr_parent)); ifr.ifr_data = (caddr_t)¶ms; if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFCREATE2, &ifr) < 0) { // TODO: Add proper error tracking here return (-1); } *ifname = strdup(ifr.ifr_name); return (0); } int ifconfig_set_vlantag(ifconfig_handle_t *h, const char *name, const char *vlandev, const unsigned short vlantag) { struct ifreq ifr; struct vlanreq params; bzero(¶ms, sizeof(params)); params.vlr_tag = vlantag; strlcpy(params.vlr_parent, vlandev, sizeof(params.vlr_parent)); ifr.ifr_data = (caddr_t)¶ms; (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCSETVLAN, &ifr) == -1) { return (-1); } return (0); } + +int +ifconfig_list_cloners(ifconfig_handle_t *h, char **bufp, size_t *lenp) +{ + struct if_clonereq ifcr; + char *buf; + + memset(&ifcr, 0, sizeof(ifcr)); + *bufp = NULL; + *lenp = 0; + + if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFGCLONERS, &ifcr) < 0) + return (-1); + + buf = malloc(ifcr.ifcr_total * IFNAMSIZ); + if (buf == NULL) { + h->error.errtype = OTHER; + h->error.errcode = ENOMEM; + return (-1); + } + + ifcr.ifcr_count = ifcr.ifcr_total; + ifcr.ifcr_buffer = buf; + if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCIFGCLONERS, &ifcr) < 0) { + free(buf); + return (-1); + } + + *bufp = buf; + *lenp = ifcr.ifcr_total; + return (0); +} Index: head/lib/libifconfig/libifconfig.h =================================================================== --- head/lib/libifconfig/libifconfig.h (revision 366905) +++ head/lib/libifconfig/libifconfig.h (revision 366906) @@ -1,281 +1,291 @@ /* * Copyright (c) 2016-2017, Marie Helene Kvello-Aune * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * thislist of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #pragma once #include #include #include #include #define ND6_IFF_DEFAULTIF 0x8000 typedef enum { OK = 0, OTHER, IOCTL, SOCKET } ifconfig_errtype; /* * Opaque definition so calling application can just pass a * pointer to it for library use. */ struct ifconfig_handle; typedef struct ifconfig_handle ifconfig_handle_t; struct carpreq; struct ifaddrs; struct ifbropreq; struct ifbreq; struct in6_ndireq; struct lagg_reqall; struct lagg_reqflags; struct lagg_reqopts; struct lagg_reqport; /** Stores extra info associated with a bridge(4) interface */ struct ifconfig_bridge_status { struct ifbropreq *params; /**< current operational parameters */ struct ifbreq *members; /**< list of bridge members */ size_t members_count; /**< how many member interfaces */ uint32_t cache_size; /**< size of address cache */ uint32_t cache_lifetime; /**< address cache entry lifetime */ }; struct ifconfig_capabilities { /** Current capabilities (ifconfig prints this as 'options')*/ int curcap; /** Requested capabilities (ifconfig prints this as 'capabilities')*/ int reqcap; }; /** Stores extra info associated with an inet address */ struct ifconfig_inet_addr { const struct sockaddr_in *sin; const struct sockaddr_in *netmask; const struct sockaddr_in *dst; const struct sockaddr_in *broadcast; int prefixlen; uint8_t vhid; }; /** Stores extra info associated with an inet6 address */ struct ifconfig_inet6_addr { struct sockaddr_in6 *sin6; struct sockaddr_in6 *dstin6; struct in6_addrlifetime lifetime; int prefixlen; uint32_t flags; uint8_t vhid; }; /** Stores extra info associated with a lagg(4) interface */ struct ifconfig_lagg_status { struct lagg_reqall *ra; struct lagg_reqopts *ro; struct lagg_reqflags *rf; }; /** Retrieves a new state object for use in other API calls. * Example usage: *{@code * // Create state object * ifconfig_handle_t *lifh; * lifh = ifconfig_open(); * if (lifh == NULL) { * // Handle error * } * * // Do stuff with the handle * * // Dispose of the state object * ifconfig_close(lifh); * lifh = NULL; *} */ ifconfig_handle_t *ifconfig_open(void); /** Frees resources held in the provided state object. * @param h The state object to close. * @see #ifconfig_open(void) */ void ifconfig_close(ifconfig_handle_t *h); /** Identifies what kind of error occured. */ ifconfig_errtype ifconfig_err_errtype(ifconfig_handle_t *h); /** Retrieves the errno associated with the error, if any. */ int ifconfig_err_errno(ifconfig_handle_t *h); typedef void (*ifconfig_foreach_func_t)(ifconfig_handle_t *h, struct ifaddrs *ifa, void *udata); /** Iterate over every network interface * @param h An open ifconfig state object * @param cb A callback function to call with a pointer to each interface * @param udata An opaque value that will be passed to the callback. * @return 0 on success, nonzero if the list could not be iterated */ int ifconfig_foreach_iface(ifconfig_handle_t *h, ifconfig_foreach_func_t cb, void *udata); /** Iterate over every address on a single network interface * @param h An open ifconfig state object * @param ifa A pointer that was supplied by a previous call to * ifconfig_foreach_iface * @param udata An opaque value that will be passed to the callback. * @param cb A callback function to call with a pointer to each ifaddr */ void ifconfig_foreach_ifaddr(ifconfig_handle_t *h, struct ifaddrs *ifa, ifconfig_foreach_func_t cb, void *udata); /** If error type was IOCTL, this identifies which request failed. */ unsigned long ifconfig_err_ioctlreq(ifconfig_handle_t *h); int ifconfig_get_description(ifconfig_handle_t *h, const char *name, char **description); int ifconfig_set_description(ifconfig_handle_t *h, const char *name, const char *newdescription); int ifconfig_unset_description(ifconfig_handle_t *h, const char *name); int ifconfig_set_name(ifconfig_handle_t *h, const char *name, const char *newname); int ifconfig_get_orig_name(ifconfig_handle_t *h, const char *ifname, char **orig_name); int ifconfig_set_fib(ifconfig_handle_t *h, const char *name, int fib); int ifconfig_get_fib(ifconfig_handle_t *h, const char *name, int *fib); int ifconfig_set_mtu(ifconfig_handle_t *h, const char *name, const int mtu); int ifconfig_get_mtu(ifconfig_handle_t *h, const char *name, int *mtu); int ifconfig_get_nd6(ifconfig_handle_t *h, const char *name, struct in6_ndireq *nd); int ifconfig_set_metric(ifconfig_handle_t *h, const char *name, const int metric); int ifconfig_get_metric(ifconfig_handle_t *h, const char *name, int *metric); int ifconfig_set_capability(ifconfig_handle_t *h, const char *name, const int capability); int ifconfig_get_capability(ifconfig_handle_t *h, const char *name, struct ifconfig_capabilities *capability); /** Retrieve the list of groups to which this interface belongs * @param h An open ifconfig state object * @param name The interface name * @param ifgr return argument. The caller is responsible for freeing * ifgr->ifgr_groups * @return 0 on success, nonzero on failure */ int ifconfig_get_groups(ifconfig_handle_t *h, const char *name, struct ifgroupreq *ifgr); int ifconfig_get_ifstatus(ifconfig_handle_t *h, const char *name, struct ifstat *stat); /** Retrieve the interface media information * @param h An open ifconfig state object * @param name The interface name * @param ifmr Return argument. The caller is responsible for freeing it * @return 0 on success, nonzero on failure */ int ifconfig_media_get_mediareq(ifconfig_handle_t *h, const char *name, struct ifmediareq **ifmr); const char *ifconfig_media_get_type(int ifmw); const char *ifconfig_media_get_subtype(int ifmw); const char *ifconfig_media_get_status(const struct ifmediareq *ifmr); void ifconfig_media_get_options_string(int ifmw, char *buf, size_t buflen); int ifconfig_carp_get_info(ifconfig_handle_t *h, const char *name, struct carpreq *carpr, int ncarpr); /** Retrieve additional information about an inet address * @param h An open ifconfig state object * @param name The interface name * @param ifa Pointer to the the address structure of interest * @param addr Return argument. It will be filled with additional information * about the address. * @return 0 on success, nonzero on failure. */ int ifconfig_inet_get_addrinfo(ifconfig_handle_t *h, const char *name, struct ifaddrs *ifa, struct ifconfig_inet_addr *addr); /** Retrieve additional information about an inet6 address * @param h An open ifconfig state object * @param name The interface name * @param ifa Pointer to the the address structure of interest * @param addr Return argument. It will be filled with additional information * about the address. * @return 0 on success, nonzero on failure. */ int ifconfig_inet6_get_addrinfo(ifconfig_handle_t *h, const char *name, struct ifaddrs *ifa, struct ifconfig_inet6_addr *addr); /** Retrieve additional information about a bridge(4) interface */ int ifconfig_bridge_get_bridge_status(ifconfig_handle_t *h, const char *name, struct ifconfig_bridge_status **bridge); /** Frees the structure returned by ifconfig_bridge_get_bridge_status. Does * nothing if the argument is NULL * @param bridge Pointer to the structure to free */ void ifconfig_bridge_free_bridge_status(struct ifconfig_bridge_status *bridge); /** Retrieve additional information about a lagg(4) interface */ int ifconfig_lagg_get_lagg_status(ifconfig_handle_t *h, const char *name, struct ifconfig_lagg_status **lagg_status); /** Retrieve additional information about a member of a lagg(4) interface */ int ifconfig_lagg_get_laggport_status(ifconfig_handle_t *h, const char *name, struct lagg_reqport *rp); /** Frees the structure returned by ifconfig_lagg_get_lagg_status. Does * nothing if the argument is NULL * @param laggstat Pointer to the structure to free */ void ifconfig_lagg_free_lagg_status(struct ifconfig_lagg_status *laggstat); /** Destroy a virtual interface * @param name Interface to destroy */ int ifconfig_destroy_interface(ifconfig_handle_t *h, const char *name); /** Creates a (virtual) interface * @param name Name of interface to create. Example: bridge or bridge42 * @param name ifname Is set to actual name of created interface */ int ifconfig_create_interface(ifconfig_handle_t *h, const char *name, char **ifname); /** Creates a (virtual) interface * @param name Name of interface to create. Example: vlan0 or ix0.50 * @param name ifname Is set to actual name of created interface * @param vlandev Name of interface to attach to * @param vlanid VLAN ID/Tag. Must not be 0. */ int ifconfig_create_interface_vlan(ifconfig_handle_t *h, const char *name, char **ifname, const char *vlandev, const unsigned short vlantag); int ifconfig_set_vlantag(ifconfig_handle_t *h, const char *name, const char *vlandev, const unsigned short vlantag); + +/** Gets the names of all interface cloners available on the system + * @param bufp Set to the address of the names buffer on success or NULL + * if an error occurs. This buffer must be freed when done. + * @param lenp Set to the number of names in the returned buffer or 0 + * if an error occurs. Each name is contained within an + * IFNAMSIZ length slice of the buffer, for a total buffer + * length of *lenp * IFNAMSIZ bytes. + */ +int ifconfig_list_cloners(ifconfig_handle_t *h, char **bufp, size_t *lenp); Index: head/sbin/ifconfig/ifclone.c =================================================================== --- head/sbin/ifconfig/ifclone.c (revision 366905) +++ head/sbin/ifconfig/ifclone.c (revision 366906) @@ -1,196 +1,179 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include +#include #include #include #include #include #include "ifconfig.h" static void list_cloners(void) { - struct if_clonereq ifcr; - char *cp, *buf; - int idx; - int s; + ifconfig_handle_t *lifh; + char *cloners; + size_t cloners_count; - s = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (s == -1) - err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); + lifh = ifconfig_open(); + if (lifh == NULL) + return; - memset(&ifcr, 0, sizeof(ifcr)); + if (ifconfig_list_cloners(lifh, &cloners, &cloners_count) < 0) + errc(1, ifconfig_err_errno(lifh), "unable to list cloners"); + ifconfig_close(lifh); - if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) - err(1, "SIOCIFGCLONERS for count"); - - buf = malloc(ifcr.ifcr_total * IFNAMSIZ); - if (buf == NULL) - err(1, "unable to allocate cloner name buffer"); - - ifcr.ifcr_count = ifcr.ifcr_total; - ifcr.ifcr_buffer = buf; - - if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) - err(1, "SIOCIFGCLONERS for names"); - - /* - * In case some disappeared in the mean time, clamp it down. - */ - if (ifcr.ifcr_count > ifcr.ifcr_total) - ifcr.ifcr_count = ifcr.ifcr_total; - - for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) { - if (idx > 0) + for (const char *name = cloners; + name < cloners + cloners_count * IFNAMSIZ; + name += IFNAMSIZ) { + if (name > cloners) putchar(' '); - printf("%s", cp); + printf("%s", name); } - putchar('\n'); - free(buf); - close(s); + free(cloners); } struct clone_defcb { char ifprefix[IFNAMSIZ]; clone_callback_func *clone_cb; SLIST_ENTRY(clone_defcb) next; }; static SLIST_HEAD(, clone_defcb) clone_defcbh = SLIST_HEAD_INITIALIZER(clone_defcbh); void clone_setdefcallback(const char *ifprefix, clone_callback_func *p) { struct clone_defcb *dcp; dcp = malloc(sizeof(*dcp)); strlcpy(dcp->ifprefix, ifprefix, IFNAMSIZ-1); dcp->clone_cb = p; SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); } /* * Do the actual clone operation. Any parameters must have been * setup by now. If a callback has been setup to do the work * then defer to it; otherwise do a simple create operation with * no parameters. */ static void ifclonecreate(int s, void *arg) { struct ifreq ifr; struct clone_defcb *dcp; clone_callback_func *clone_cb = NULL; memset(&ifr, 0, sizeof(ifr)); (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (clone_cb == NULL) { /* Try to find a default callback */ SLIST_FOREACH(dcp, &clone_defcbh, next) { if (strncmp(dcp->ifprefix, ifr.ifr_name, strlen(dcp->ifprefix)) == 0) { clone_cb = dcp->clone_cb; break; } } } if (clone_cb == NULL) { /* NB: no parameters */ if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) err(1, "SIOCIFCREATE2"); } else { clone_cb(s, &ifr); } /* * If we get a different name back than we put in, update record and * indicate it should be printed later. */ if (strncmp(name, ifr.ifr_name, sizeof(name)) != 0) { strlcpy(name, ifr.ifr_name, sizeof(name)); printifname = 1; } } static DECL_CMD_FUNC(clone_create, arg, d) { callback_register(ifclonecreate, NULL); } static DECL_CMD_FUNC(clone_destroy, arg, d) { (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) err(1, "SIOCIFDESTROY"); } static struct cmd clone_cmds[] = { DEF_CLONE_CMD("create", 0, clone_create), DEF_CMD("destroy", 0, clone_destroy), DEF_CLONE_CMD("plumb", 0, clone_create), DEF_CMD("unplumb", 0, clone_destroy), }; static void clone_Copt_cb(const char *optarg __unused) { list_cloners(); exit(exit_code); } static struct option clone_Copt = { .opt = "C", .opt_usage = "[-C]", .cb = clone_Copt_cb }; static __constructor void clone_ctor(void) { size_t i; for (i = 0; i < nitems(clone_cmds); i++) cmd_register(&clone_cmds[i]); opt_register(&clone_Copt); }