diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c index cb030dbc711b..ab2b01320d04 100644 --- a/sbin/ifconfig/af_inet.c +++ b/sbin/ifconfig/af_inet.c @@ -1,323 +1,497 @@ /*- * 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 #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" +#ifdef WITHOUT_NETLINK static struct in_aliasreq in_addreq; static struct ifreq in_ridreq; +#else +struct in_px { + struct in_addr addr; + int plen; + bool addrset; + bool maskset; +}; +struct in_pdata { + struct in_px addr; + struct in_px dst_addr; + struct in_px brd_addr; + uint32_t flags; + uint32_t vhid; +}; +static struct in_pdata in_add, in_del; +#endif + static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ extern char *f_inet, *f_addr; static void print_addr(struct sockaddr_in *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error) inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf)); printf("\tinet %s", addr_buf); } #ifdef WITHOUT_NETLINK static void in_status(int s __unused, const struct ifaddrs *ifa) { struct sockaddr_in *sin, null_sin = {}; sin = (struct sockaddr_in *)ifa->ifa_addr; if (sin == NULL) return; print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = (struct sockaddr_in *)ifa->ifa_dstaddr; if (sin == NULL) sin = &null_sin; printf(" --> %s", inet_ntoa(sin->sin_addr)); } sin = (struct sockaddr_in *)ifa->ifa_netmask; if (sin == NULL) sin = &null_sin; if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { int cidr = 32; unsigned long smask; smask = ntohl(sin->sin_addr.s_addr); while ((smask & 1) == 0) { smask = smask >> 1; cidr--; if (cidr == 0) break; } printf("/%d", cidr); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(sin->sin_addr)); else printf(" netmask 0x%lx", (unsigned long)ntohl(sin->sin_addr.s_addr)); if (ifa->ifa_flags & IFF_BROADCAST) { sin = (struct sockaddr_in *)ifa->ifa_broadaddr; if (sin != NULL && sin->sin_addr.s_addr != 0) printf(" broadcast %s", inet_ntoa(sin->sin_addr)); } print_vhid(ifa, " "); putchar('\n'); } #else static struct in_addr get_mask(int plen) { struct in_addr a; a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); return (a); } static struct sockaddr_in * satosin(struct sockaddr *sa) { return ((struct sockaddr_in *)(void *)sa); } static void in_status_nl(struct ifconfig_args *args __unused, struct io_handler *h, if_link_t *link, if_addr_t *ifa) { struct sockaddr_in *sin = satosin(ifa->ifa_local); int plen = ifa->ifa_prefixlen; print_addr(sin); if (link->ifi_flags & IFF_POINTOPOINT) { struct sockaddr_in *dst = satosin(ifa->ifa_address); printf(" --> %s", inet_ntoa(dst->sin_addr)); } if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { printf("/%d", plen); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(get_mask(plen))); else printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr)); if ((link->ifi_flags & IFF_BROADCAST) && plen != 0) { struct sockaddr_in *brd = satosin(ifa->ifa_broadcast); if (brd != NULL) printf(" broadcast %s", inet_ntoa(brd->sin_addr)); } if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } #endif + +#ifdef WITHOUT_NETLINK #define SIN(x) ((struct sockaddr_in *) &(x)) static struct sockaddr_in *sintab[] = { SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) }; static void in_getaddr(const char *s, int which) { struct sockaddr_in *sin = sintab[which]; struct hostent *hp; struct netent *np; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen; struct sockaddr_in *min = sintab[MASK]; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } min->sin_family = AF_INET; min->sin_len = sizeof(*min); min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & 0xffffffff); } } if (inet_aton(s, &sin->sin_addr)) return; if ((hp = gethostbyname(s)) != NULL) bcopy(hp->h_addr, (char *)&sin->sin_addr, MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); else if ((np = getnetbyname(s)) != NULL) sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", s); } +#else + +static struct in_px *sintab_nl[] = { + &in_del.addr, /* RIDADDR */ + &in_add.addr, /* ADDR */ + NULL, /* MASK */ + &in_add.dst_addr, /* DSTADDR*/ + &in_add.brd_addr, /* BRDADDR*/ +}; + +static void +in_getip(const char *addr_str, struct in_addr *ip) +{ + struct hostent *hp; + struct netent *np; + + if (inet_aton(addr_str, ip)) + return; + if ((hp = gethostbyname(addr_str)) != NULL) + bcopy(hp->h_addr, (char *)ip, + MIN((size_t)hp->h_length, sizeof(ip))); + else if ((np = getnetbyname(addr_str)) != NULL) + *ip = inet_makeaddr(np->n_net, INADDR_ANY); + else + errx(1, "%s: bad value", addr_str); +} + +static void +in_getaddr(const char *s, int which) +{ + struct in_px *px = sintab_nl[which]; + + if (which == MASK) { + struct in_px *px_addr = sintab_nl[ADDR]; + struct in_addr mask = {}; + + in_getip(s, &mask); + px_addr->plen = __bitcount32(mask.s_addr); + px_addr->maskset = true; + return; + } + + if (which == ADDR) { + char *p = NULL; + + if((p = strrchr(s, '/')) != NULL) { + const char *errstr; + /* address is `name/masklen' */ + int masklen; + *p = '\0'; + if (!isdigit(*(p + 1))) + errstr = "invalid"; + else + masklen = (int)strtonum(p + 1, 0, 32, &errstr); + if (errstr != NULL) { + *p = '/'; + errx(1, "%s: bad value (width %s)", s, errstr); + } + px->plen = masklen; + px->maskset = true; + } + } + + in_getip(s, &px->addr); + px->addrset = true; +} + + +static int +in_exec_nl(struct io_handler *h, int action, void *data) +{ + struct in_pdata *pdata = (struct in_pdata *)data; + struct snl_writer nw = {}; + + snl_init_writer(h->ss, &nw); + struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); + struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); + + ifahdr->ifa_family = AF_INET; + ifahdr->ifa_prefixlen = pdata->addr.plen; + ifahdr->ifa_index = if_nametoindex_nl(h->ss, name); + + snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr); + if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset) + snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); + if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset) + snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr); + + int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); + snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); + if (pdata->vhid != 0) + snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); + snl_end_attr_nested(&nw, off); + + if (!snl_finalize_msg(&nw) || !snl_send_message(h->ss, hdr)) + return (0); + + struct snl_errmsg_data e = {}; + snl_read_reply_code(h->ss, hdr->nlmsg_seq, &e); + if (e.error_str != NULL) + warnx("%s(): %s", __func__, e.error_str); + + return (e.error); +} + +static void +in_setdefaultmask_nl(void) +{ + struct in_px *px = sintab_nl[ADDR]; + + in_addr_t i = ntohl(px->addr.s_addr); + + /* + * If netmask isn't supplied, use historical default. + * This is deprecated for interfaces other than loopback + * or point-to-point; warn in other cases. In the future + * we should return an error rather than warning. + */ + if (IN_CLASSA(i)) + px->plen = IN_CLASSA_NSHIFT; + else if (IN_CLASSB(i)) + px->plen = IN_CLASSB_NSHIFT; + else + px->plen = IN_CLASSC_NSHIFT; + px->maskset = true; +} +#endif + +static void +warn_nomask(ifflags) +{ + if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { + warnx("WARNING: setting interface address without mask " + "is deprecated,\ndefault mask may not be correct."); + } +} + static void in_postproc(int s, const struct afswtch *afp, int newaddr, int ifflags) { - if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && - newaddr && (ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { - warnx("WARNING: setting interface address without mask " - "is deprecated,\ndefault mask may not be correct."); +#ifdef WITHOUT_NETLINK + if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) { + warn_nomask(ifflags); + } +#else + if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) { + warn_nomask(ifflags); + in_setdefaultmask_nl(); } +#endif } static void in_status_tunnel(int s) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct ifreq ifr; const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, IFNAMSIZ); if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet %s --> %s\n", src, dst); } static void in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) { struct in_aliasreq addreq; memset(&addreq, 0, sizeof(addreq)); strlcpy(addreq.ifra_name, name, IFNAMSIZ); memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0) warn("SIOCSIFPHYADDR"); } static void in_set_vhid(int vhid) { +#ifdef WITHOUT_NETLINK in_addreq.ifra_vhid = vhid; +#else + in_add.vhid = (uint32_t)vhid; +#endif } - static struct afswtch af_inet = { .af_name = "inet", .af_af = AF_INET, #ifdef WITHOUT_NETLINK .af_status = in_status, #else .af_status_nl = in_status_nl, #endif .af_getaddr = in_getaddr, .af_postproc = in_postproc, .af_status_tunnel = in_status_tunnel, .af_settunnel = in_set_tunnel, .af_setvhid = in_set_vhid, +#ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR, .af_aifaddr = SIOCAIFADDR, .af_ridreq = &in_ridreq, .af_addreq = &in_addreq, + .af_exec = af_exec_ioctl, +#else + .af_difaddr = NL_RTM_DELADDR, + .af_aifaddr = NL_RTM_NEWADDR, + .af_ridreq = &in_del, + .af_addreq = &in_add, + .af_exec = in_exec_nl, +#endif }; static __constructor void inet_ctor(void) { #ifndef RESCUE if (!feature_present("inet")) return; #endif af_register(&af_inet); } diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index 1bb08c8a6a5a..c932087b4d86 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -1,646 +1,790 @@ /*- * 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 #include #include #include #include #include /* Define ND6_INFINITE_LIFETIME */ #include "ifconfig.h" #include "ifconfig_netlink.h" +#ifndef WITHOUT_NETLINK +struct in6_px { + struct in6_addr addr; + int plen; + bool set; +}; +struct in6_pdata { + struct in6_px addr; + struct in6_px dst_addr; + struct in6_addrlifetime lifetime; + uint32_t flags; + uint32_t vhid; +}; + +static struct in6_pdata in6_del; +static struct in6_pdata in6_add = { + .lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME }, +}; +#else static struct in6_ifreq in6_ridreq; static struct in6_aliasreq in6_addreq = { .ifra_flags = 0, .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; +#endif static int ip6lifetime; static int prefix(void *, int); static char *sec2str(time_t); static int explicit_prefix = 0; extern char *f_inet6, *f_addr; extern void setnd6flags(const char *, int, int, const struct afswtch *); extern void setnd6defif(const char *, int, int, const struct afswtch *); extern void nd6_status(int); static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ static void setifprefixlen(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { +#ifdef WITHOUT_NETLINK if (afp->af_getprefix != NULL) afp->af_getprefix(addr, MASK); +#else + int plen = strtol(addr, NULL, 10); + + if ((plen < 0) || (plen > 128)) + errx(1, "%s: bad value", addr); + in6_add.addr.plen = plen; +#endif explicit_prefix = 1; } static void setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused, const struct afswtch *afp) { if (afp->af_af != AF_INET6) err(1, "address flags can be set only for inet6 addresses"); +#ifdef WITHOUT_NETLINK if (flag < 0) in6_addreq.ifra_flags &= ~(-flag); else in6_addreq.ifra_flags |= flag; +#else + if (flag < 0) + in6_add.flags &= ~(-flag); + else + in6_add.flags |= flag; +#endif } static void setip6lifetime(const char *cmd, const char *val, int s, const struct afswtch *afp) { struct timespec now; time_t newval; char *ep; +#ifdef WITHOUT_NETLINK + struct in6_addrlifetime *lifetime = &in6_addreq.ifra_lifetime; +#else + struct in6_addrlifetime *lifetime = &in6_add.lifetime; +#endif clock_gettime(CLOCK_MONOTONIC_FAST, &now); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(1, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(1, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { - in6_addreq.ifra_lifetime.ia6t_expire = now.tv_sec + newval; - in6_addreq.ifra_lifetime.ia6t_vltime = newval; + lifetime->ia6t_expire = now.tv_sec + newval; + lifetime->ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { - in6_addreq.ifra_lifetime.ia6t_preferred = now.tv_sec + newval; - in6_addreq.ifra_lifetime.ia6t_pltime = newval; + lifetime->ia6t_preferred = now.tv_sec + newval; + lifetime->ia6t_pltime = newval; } } static void setip6pltime(const char *seconds, int dummy __unused, int s, const struct afswtch *afp) { setip6lifetime("pltime", seconds, s, afp); } static void setip6vltime(const char *seconds, int dummy __unused, int s, const struct afswtch *afp) { setip6lifetime("vltime", seconds, s, afp); } static void setip6eui64(const char *cmd, int dummy __unused, int s, const struct afswtch *afp) { struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); +#ifdef WITHOUT_NETLINK in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; +#else + in6 = &in6_add.addr.addr; +#endif if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, name) == 0) { sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } static void print_addr(struct sockaddr_in6 *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("\tinet6 %s", addr_buf); } static void print_p2p(struct sockaddr_in6 *sin) { int error; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf(" --> %s", addr_buf); } static void print_mask(int plen) { if (f_inet6 != NULL && strcmp(f_inet6, "cidr") == 0) printf("/%d", plen); else printf(" prefixlen %d", plen); } static void print_flags(int flags6) { if ((flags6 & IN6_IFF_ANYCAST) != 0) printf(" anycast"); if ((flags6 & IN6_IFF_TENTATIVE) != 0) printf(" tentative"); if ((flags6 & IN6_IFF_DUPLICATED) != 0) printf(" duplicated"); if ((flags6 & IN6_IFF_DETACHED) != 0) printf(" detached"); if ((flags6 & IN6_IFF_DEPRECATED) != 0) printf(" deprecated"); if ((flags6 & IN6_IFF_AUTOCONF) != 0) printf(" autoconf"); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf(" temporary"); if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) printf(" prefer_source"); } static void print_lifetime(const char *prepend, time_t px_time, struct timespec *now) { printf(" %s", prepend); if (px_time == 0) printf(" infty"); printf(" %s", px_time < now->tv_sec ? "0" : sec2str(px_time - now->tv_sec)); } #ifdef WITHOUT_NETLINK static void in6_status(int s __unused, const struct ifaddrs *ifa) { struct sockaddr_in6 *sin, null_sin = {}; struct in6_ifreq ifr6; int s6; u_int32_t flags6; struct in6_addrlifetime lifetime; sin = (struct sockaddr_in6 *)ifa->ifa_addr; if (sin == NULL) return; strlcpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_INET6,SOCK_DGRAM)"); return; } ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFAFLAG_IN6)"); close(s6); return; } flags6 = ifr6.ifr_ifru.ifru_flags6; memset(&lifetime, 0, sizeof(lifetime)); ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFALIFETIME_IN6)"); close(s6); return; } lifetime = ifr6.ifr_ifru.ifru_lifetime; close(s6); print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr; /* * some of the interfaces do not have valid destination * address. */ if (sin != NULL && sin->sin6_family == AF_INET6) print_p2p(sin); } sin = (struct sockaddr_in6 *)ifa->ifa_netmask; if (sin == NULL) sin = &null_sin; print_mask(prefix(&sin->sin6_addr, sizeof(struct in6_addr))); print_flags(flags6); if (((struct sockaddr_in6 *)(ifa->ifa_addr))->sin6_scope_id) printf(" scopeid 0x%x", ((struct sockaddr_in6 *)(ifa->ifa_addr))->sin6_scope_id); if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", lifetime.ia6t_preferred, &now); print_lifetime("vltime", lifetime.ia6t_expire, &now); } print_vhid(ifa, " "); putchar('\n'); } #else static void show_lifetime(struct ifa_cacheinfo *ci) { struct timespec now; uint32_t pl, vl; if (ci == NULL) return; int count = ci->ifa_prefered != ND6_INFINITE_LIFETIME; count += ci->ifa_valid != ND6_INFINITE_LIFETIME; if (count == 0) return; pl = (ci->ifa_prefered == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_prefered; vl = (ci->ifa_valid == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_valid; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", pl + now.tv_sec, &now); print_lifetime("vltime", vl + now.tv_sec, &now); } static struct sockaddr_in6 * satosin6(struct sockaddr *sa) { return ((struct sockaddr_in6 *)(void *)sa); } static void in6_status_nl(struct ifconfig_args *args __unused, struct io_handler *h, if_link_t *link, if_addr_t *ifa) { int plen = ifa->ifa_prefixlen; uint32_t scopeid; if (ifa->ifa_local == NULL) { /* Non-P2P address */ scopeid = satosin6(ifa->ifa_address)->sin6_scope_id; print_addr(satosin6(ifa->ifa_address)); } else { scopeid = satosin6(ifa->ifa_local)->sin6_scope_id; print_addr(satosin6(ifa->ifa_local)); print_p2p(satosin6(ifa->ifa_address)); } print_mask(plen); print_flags(ifa->ifaf_flags); if (scopeid != 0) printf(" scopeid 0x%x", scopeid); show_lifetime(ifa->ifa_cacheinfo); if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } + +static struct in6_px *sin6tab_nl[] = { + &in6_del.addr, /* RIDADDR */ + &in6_add.addr, /* ADDR */ + NULL, /* MASK */ + &in6_add.dst_addr, /* DSTADDR*/ +}; + +static void +in6_getaddr(const char *addr_str, int which) +{ + struct in6_px *px = sin6tab_nl[which]; + + newaddr &= 1; + + px->set = true; + px->plen = 128; + if (which == ADDR) { + char *p = NULL; + if((p = strrchr(addr_str, '/')) != NULL) { + *p = '\0'; + int plen = strtol(p + 1, NULL, 10); + if (plen < 0 || plen > 128) + errx(1, "%s: bad value", p + 1); + px->plen = plen; + explicit_prefix = 1; + } + } + + struct addrinfo hints = { .ai_family = AF_INET6 }; + struct addrinfo *res; + + int error = getaddrinfo(addr_str, NULL, &hints, &res); + if (error != 0) { + if (inet_pton(AF_INET6, addr_str, &px->addr) != 1) + errx(1, "%s: bad value", addr_str); + } else { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr; + px->addr = sin6->sin6_addr; + freeaddrinfo(res); + } +} + +static int +in6_exec_nl(struct io_handler *h, int action, void *data) +{ + struct in6_pdata *pdata = (struct in6_pdata *)data; + struct snl_writer nw = {}; + + snl_init_writer(h->ss, &nw); + struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); + struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); + + ifahdr->ifa_family = AF_INET6; + ifahdr->ifa_prefixlen = pdata->addr.plen; + ifahdr->ifa_index = if_nametoindex_nl(h->ss, name); + + snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &pdata->addr.addr); + if (action == NL_RTM_NEWADDR && pdata->dst_addr.set) + snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); + + struct ifa_cacheinfo ci = { + .ifa_prefered = pdata->lifetime.ia6t_pltime, + .ifa_valid = pdata->lifetime.ia6t_vltime, + }; + snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci); + + int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); + snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); + if (pdata->vhid != 0) + snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); + snl_end_attr_nested(&nw, off); + + if (!snl_finalize_msg(&nw) || !snl_send_message(h->ss, hdr)) + return (0); + + struct snl_errmsg_data e = {}; + snl_read_reply_code(h->ss, hdr->nlmsg_seq, &e); + + return (e.error); +} #endif +#ifdef WITHOUT_NETLINK #define SIN6(x) ((struct sockaddr_in6 *) &(x)) static struct sockaddr_in6 *sin6tab[] = { SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr) }; static void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *sin = sin6tab[which]; u_char *cp; int len = atoi(plen); if ((len < 0) || (len > 128)) errx(1, "%s: bad value", plen); sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; *cp = 0xff << (8 - len); } static void in6_getaddr(const char *s, int which) { struct sockaddr_in6 *sin = sin6tab[which]; struct addrinfo hints, *res; int error = -1; newaddr &= 1; sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (sin->sin6_family == AF_INET6) { bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; error = getaddrinfo(s, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) errx(1, "%s: bad value", s); } else { bcopy(res->ai_addr, sin, res->ai_addrlen); freeaddrinfo(res); } } } static int prefix(void *val, int size) { u_char *name = (u_char *)val; int byte, bit, plen = 0; for (byte = 0; byte < size; byte++, plen += 8) if (name[byte] != 0xff) break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) if (!(name[byte] & (1 << bit))) break; for (; bit != 0; bit--) if (name[byte] & (1 << bit)) return(0); byte++; for (; byte < size; byte++) if (name[byte]) return(0); return (plen); } +#endif static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; if (0) { days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; p += sprintf(p, "%dd", days); } if (!first || hours) { first = 0; p += sprintf(p, "%dh", hours); } if (!first || mins) { first = 0; p += sprintf(p, "%dm", mins); } sprintf(p, "%ds", secs); } else sprintf(result, "%lu", (unsigned long)total); return(result); } static void in6_postproc(int s, const struct afswtch *afp, int newaddr __unused, int ifflags __unused) { if (explicit_prefix == 0) { /* Aggregatable address architecture defines all prefixes are 64. So, it is convenient to set prefixlen to 64 if it is not specified. */ setifprefixlen("64", 0, s, afp); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(int s) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct in6_ifreq in6_ifr; const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; memset(&in6_ifr, 0, sizeof(in6_ifr)); strlcpy(in6_ifr.ifr_name, name, sizeof(in6_ifr.ifr_name)); if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet6 %s --> %s\n", src, dst); } static void in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_addreq; memset(&in6_addreq, 0, sizeof(in6_addreq)); strlcpy(in6_addreq.ifra_name, name, sizeof(in6_addreq.ifra_name)); memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) warn("SIOCSIFPHYADDR_IN6"); } static void in6_set_vhid(int vhid) { +#ifdef WITHOUT_NETLINK in6_addreq.ifra_vhid = vhid; +#else + in6_add.vhid = (uint32_t)vhid; +#endif } static struct cmd inet6_cmds[] = { DEF_CMD_ARG("prefixlen", setifprefixlen), DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("-no_radr", -ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("defaultif", 1, setnd6defif), DEF_CMD("-defaultif", -1, setnd6defif), DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("no_dad", ND6_IFF_NO_DAD, setnd6flags), DEF_CMD("-no_dad", -ND6_IFF_NO_DAD, setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), #ifdef EXPERIMENTAL DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), #endif }; static struct afswtch af_inet6 = { .af_name = "inet6", .af_af = AF_INET6, #ifdef WITHOUT_NETLINK .af_status = in6_status, #else .af_status_nl = in6_status_nl, #endif .af_getaddr = in6_getaddr, +#ifdef WITHOUT_NETLINK .af_getprefix = in6_getprefix, +#endif .af_other_status = nd6_status, .af_postproc = in6_postproc, .af_status_tunnel = in6_status_tunnel, .af_settunnel = in6_set_tunnel, .af_setvhid = in6_set_vhid, +#ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, .af_ridreq = &in6_addreq, .af_addreq = &in6_addreq, + .af_exec = af_exec_ioctl, +#else + .af_difaddr = NL_RTM_DELADDR, + .af_aifaddr = NL_RTM_NEWADDR, + .af_ridreq = &in6_add, + .af_addreq = &in6_add, + .af_exec = in6_exec_nl, +#endif }; static void in6_Lopt_cb(const char *optarg __unused) { ip6lifetime++; /* print IPv6 address lifetime */ } static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; static __constructor void inet6_ctor(void) { size_t i; #ifndef RESCUE if (!feature_present("inet6")) return; #endif for (i = 0; i < nitems(inet6_cmds); i++) cmd_register(&inet6_cmds[i]); af_register(&af_inet6); opt_register(&in6_Lopt); } diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c index 52295453b4f0..0d1f5b1f6147 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -1,264 +1,267 @@ /*- * 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 #include #include "ifconfig.h" #include "ifconfig_netlink.h" static struct ifreq link_ridreq; extern char *f_ether; static void print_ether(const struct ether_addr *addr, const char *prefix) { char *ether_format = ether_ntoa(addr); if (f_ether != NULL && strcmp(f_ether, "dash") == 0) { char *format_char; while ((format_char = strchr(ether_format, ':')) != NULL) { *format_char = '-'; } } printf("\t%s %s\n", prefix, ether_format); } static void print_lladdr(struct sockaddr_dl *sdl) { if (match_ether(sdl)) { print_ether((struct ether_addr *)LLADDR(sdl), "ether"); } else { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; printf("\tlladdr %s\n", link_ntoa(sdl) + n); } } static void print_pcp(int s) { if (ioctl(s, SIOCGLANPCP, (caddr_t)&ifr) == 0 && ifr.ifr_lan_pcp != IFNET_PCP_NONE) printf("\tpcp %d\n", ifr.ifr_lan_pcp); } #ifdef WITHOUT_NETLINK static void link_status(int s __unused, const struct ifaddrs *ifa) { /* XXX no const 'cuz LLADDR is defined wrong */ struct sockaddr_dl *sdl; struct ifreq ifr; int rc, sock_hw; static const u_char laggaddr[6] = {0}; sdl = (struct sockaddr_dl *) ifa->ifa_addr; if (sdl == NULL || sdl->sdl_alen == 0) return; print_lladdr(sdl); /* * Best-effort (i.e. failures are silent) to get original * hardware address, as read by NIC driver at attach time. Only * applies to Ethernet NICs (IFT_ETHER). However, laggX * interfaces claim to be IFT_ETHER, and re-type their component * Ethernet NICs as IFT_IEEE8023ADLAG. So, check for both. If * the MAC is zeroed, then it's actually a lagg. */ if ((sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_IEEE8023ADLAG) || sdl->sdl_alen != ETHER_ADDR_LEN) return; strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(ifa->ifa_addr->sa_len)); ifr.ifr_addr.sa_family = AF_LOCAL; if ((sock_hw = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_LOCAL,SOCK_DGRAM)"); return; } rc = ioctl(sock_hw, SIOCGHWADDR, &ifr); close(sock_hw); if (rc != 0) return; /* * If this is definitely a lagg device or the hwaddr * matches the link addr, don't bother. */ if (memcmp(ifr.ifr_addr.sa_data, laggaddr, sdl->sdl_alen) == 0 || memcmp(ifr.ifr_addr.sa_data, LLADDR(sdl), sdl->sdl_alen) == 0) goto pcp; print_ether((const struct ether_addr *)&ifr.ifr_addr.sa_data, "hwaddr"); pcp: print_pcp(s); } #else static uint8_t convert_iftype(uint8_t iftype) { switch (iftype) { case IFT_IEEE8023ADLAG: return (IFT_ETHER); case IFT_INFINIBANDLAG: return (IFT_INFINIBAND); } return (iftype); } static void link_status_nl(struct ifconfig_args *args __unused, struct io_handler *h, if_link_t *link, if_addr_t *ifa __unused) { if (link->ifla_address != NULL) { struct sockaddr_dl sdl = { .sdl_len = sizeof(struct sockaddr_dl), .sdl_family = AF_LINK, .sdl_type = convert_iftype(link->ifi_type), .sdl_alen = NLA_DATA_LEN(link->ifla_address), }; memcpy(LLADDR(&sdl), NLA_DATA(link->ifla_address), sdl.sdl_alen); print_lladdr(&sdl); if (link->iflaf_orig_hwaddr != NULL) { struct nlattr *hwaddr = link->iflaf_orig_hwaddr; if (memcmp(NLA_DATA(hwaddr), NLA_DATA(link->ifla_address), sdl.sdl_alen)) print_ether((struct ether_addr *)NLA_DATA(hwaddr), "hwaddr"); } } if (convert_iftype(link->ifi_type) == IFT_ETHER) print_pcp(h->s); } #endif static void link_getaddr(const char *addr, int which) { char *temp; struct sockaddr_dl sdl; struct sockaddr *sa = &link_ridreq.ifr_addr; if (which != ADDR) errx(1, "can't set link-level netmask or broadcast"); if (!strcmp(addr, "random")) { sdl.sdl_len = sizeof(sdl); sdl.sdl_alen = ETHER_ADDR_LEN; sdl.sdl_nlen = 0; sdl.sdl_family = AF_LINK; arc4random_buf(&sdl.sdl_data, ETHER_ADDR_LEN); /* Non-multicast and claim it is locally administered. */ sdl.sdl_data[0] &= 0xfc; sdl.sdl_data[0] |= 0x02; } else { if ((temp = malloc(strlen(addr) + 2)) == NULL) errx(1, "malloc failed"); temp[0] = ':'; strcpy(temp + 1, addr); sdl.sdl_len = sizeof(sdl); link_addr(temp, &sdl); free(temp); } if (sdl.sdl_alen > sizeof(sa->sa_data)) errx(1, "malformed link-level address"); sa->sa_family = AF_LINK; sa->sa_len = sdl.sdl_alen; bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); } static struct afswtch af_link = { .af_name = "link", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status_nl = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, + .af_exec = af_exec_ioctl, }; static struct afswtch af_ether = { .af_name = "ether", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status_nl = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, + .af_exec = af_exec_ioctl, }; static struct afswtch af_lladdr = { .af_name = "lladdr", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status_nl = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, + .af_exec = af_exec_ioctl, }; static __constructor void link_ctor(void) { af_register(&af_link); af_register(&af_ether); af_register(&af_lladdr); } diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index c5e7b7befc72..3b63eac5574a 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,1961 +1,2005 @@ /*- * 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 copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #if 0 static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* IP */ #include #include #include #include #include #include #include #include #include #include #ifdef JAIL #include #endif #include #include #include #include #include #include #include "ifconfig.h" ifconfig_handle_t *lifh; /* * Since "struct ifreq" is composed of various union members, callers * should pay special attention to interpret the value. * (.e.g. little/big endian difference in the structure.) */ struct ifreq ifr; char name[IFNAMSIZ]; char *descr = NULL; size_t descrlen = 64; static int setaddr; static int setmask; static int doalias; static int clearaddr; int newaddr = 1; int verbose; int printifname = 0; struct ifconfig_args args; int printkeys = 0; /* Print keying material for interfaces. */ int exit_code = 0; /* Formatter Strings */ char *f_inet, *f_inet6, *f_ether, *f_addr; static void list_interfaces_ioctl(struct ifconfig_args *args); static void status(struct ifconfig_args *args, const struct sockaddr_dl *sdl, struct ifaddrs *ifa); static _Noreturn void usage(void); +static void Perrorc(const char *cmd, int error); static int getifflags(const char *ifname, int us, bool err_ok); static struct afswtch *af_getbyname(const char *name); void printifnamemaybe(void); static struct option *opts = NULL; struct ifa_order_elt { int if_order; int af_orders[255]; struct ifaddrs *ifa; TAILQ_ENTRY(ifa_order_elt) link; }; TAILQ_HEAD(ifa_queue, ifa_order_elt); static struct module_map_entry { const char *ifname; const char *kldname; } module_map[] = { { .ifname = "tun", .kldname = "if_tuntap", }, { .ifname = "tap", .kldname = "if_tuntap", }, { .ifname = "vmnet", .kldname = "if_tuntap", }, { .ifname = "ipsec", .kldname = "ipsec", }, { /* * This mapping exists because there is a conflicting enc module * in CAM. ifconfig's guessing behavior will attempt to match * the ifname to a module as well as if_${ifname} and clash with * CAM enc. This is an assertion of the correct module to load. */ .ifname = "enc", .kldname = "if_enc", }, }; void opt_register(struct option *p) { p->next = opts; opts = p; } static void usage(void) { char options[1024]; struct option *p; /* XXX not right but close enough for now */ options[0] = '\0'; for (p = opts; p != NULL; p = p->next) { strlcat(options, p->opt_usage, sizeof(options)); strlcat(options, " ", sizeof(options)); } fprintf(stderr, "usage: ifconfig [-f type:format] %sinterface address_family\n" " [address [dest_address]] [parameters]\n" " ifconfig interface create\n" " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n" " ifconfig -l [-d] [-u] [address_family]\n" " ifconfig %s[-d] [-m] [-u] [-v]\n", options, options, options); exit(1); } void ioctl_ifcreate(int s, struct ifreq *ifr) { if (ioctl(s, SIOCIFCREATE2, ifr) < 0) { switch (errno) { case EEXIST: errx(1, "interface %s already exists", ifr->ifr_name); default: err(1, "SIOCIFCREATE2 (%s)", ifr->ifr_name); } } } static int calcorders(struct ifaddrs *ifa, struct ifa_queue *q) { struct ifaddrs *prev; struct ifa_order_elt *cur; unsigned int ord, af, ifa_ord; prev = NULL; cur = NULL; ord = 0; ifa_ord = 0; while (ifa != NULL) { if (prev == NULL || strcmp(ifa->ifa_name, prev->ifa_name) != 0) { cur = calloc(1, sizeof(*cur)); if (cur == NULL) return (-1); TAILQ_INSERT_TAIL(q, cur, link); cur->if_order = ifa_ord ++; cur->ifa = ifa; ord = 0; } if (ifa->ifa_addr) { af = ifa->ifa_addr->sa_family; if (af < nitems(cur->af_orders) && cur->af_orders[af] == 0) cur->af_orders[af] = ++ord; } prev = ifa; ifa = ifa->ifa_next; } return (0); } static int cmpifaddrs(struct ifaddrs *a, struct ifaddrs *b, struct ifa_queue *q) { struct ifa_order_elt *cur, *e1, *e2; unsigned int af1, af2; int ret; e1 = e2 = NULL; ret = strcmp(a->ifa_name, b->ifa_name); if (ret != 0) { TAILQ_FOREACH(cur, q, link) { if (e1 && e2) break; if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) e1 = cur; else if (strcmp(cur->ifa->ifa_name, b->ifa_name) == 0) e2 = cur; } if (!e1 || !e2) return (0); else return (e1->if_order - e2->if_order); } else if (a->ifa_addr != NULL && b->ifa_addr != NULL) { TAILQ_FOREACH(cur, q, link) { if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) { e1 = cur; break; } } if (!e1) return (0); af1 = a->ifa_addr->sa_family; af2 = b->ifa_addr->sa_family; if (af1 < nitems(e1->af_orders) && af2 < nitems(e1->af_orders)) return (e1->af_orders[af1] - e1->af_orders[af2]); } return (0); } static void freeformat(void) { if (f_inet != NULL) free(f_inet); if (f_inet6 != NULL) free(f_inet6); if (f_ether != NULL) free(f_ether); if (f_addr != NULL) free(f_addr); } static void setformat(char *input) { char *formatstr, *category, *modifier; formatstr = strdup(input); while ((category = strsep(&formatstr, ",")) != NULL) { modifier = strchr(category, ':'); if (modifier == NULL || modifier[1] == '\0') { warnx("Skipping invalid format specification: %s\n", category); continue; } /* Split the string on the separator, then seek past it */ modifier[0] = '\0'; modifier++; if (strcmp(category, "addr") == 0) f_addr = strdup(modifier); else if (strcmp(category, "ether") == 0) f_ether = strdup(modifier); else if (strcmp(category, "inet") == 0) f_inet = strdup(modifier); else if (strcmp(category, "inet6") == 0) f_inet6 = strdup(modifier); } free(formatstr); } static struct ifaddrs * sortifaddrs(struct ifaddrs *list, int (*compare)(struct ifaddrs *, struct ifaddrs *, struct ifa_queue *), struct ifa_queue *q) { struct ifaddrs *right, *temp, *last, *result, *next, *tail; right = list; temp = list; last = list; result = NULL; next = NULL; tail = NULL; if (!list || !list->ifa_next) return (list); while (temp && temp->ifa_next) { last = right; right = right->ifa_next; temp = temp->ifa_next->ifa_next; } last->ifa_next = NULL; list = sortifaddrs(list, compare, q); right = sortifaddrs(right, compare, q); while (list || right) { if (!right) { next = list; list = list->ifa_next; } else if (!list) { next = right; right = right->ifa_next; } else if (compare(list, right, q) <= 0) { next = list; list = list->ifa_next; } else { next = right; right = right->ifa_next; } if (!result) result = next; else tail->ifa_next = next; tail = next; } return (result); } void printifnamemaybe() { if (printifname) printf("%s\n", name); } static void list_interfaces(struct ifconfig_args *args) { #ifdef WITHOUT_NETLINK list_interfaces_ioctl(args); #else list_interfaces_nl(args); #endif } static char * args_peek(struct ifconfig_args *args) { if (args->argc > 0) return (args->argv[0]); return (NULL); } static char * args_pop(struct ifconfig_args *args) { if (args->argc == 0) return (NULL); char *arg = args->argv[0]; args->argc--; args->argv++; return (arg); } static void args_parse(struct ifconfig_args *args, int argc, char *argv[]) { char options[1024]; struct option *p; int c; /* Parse leading line options */ strlcpy(options, "G:adf:klmnuv", sizeof(options)); for (p = opts; p != NULL; p = p->next) strlcat(options, p->opt, sizeof(options)); while ((c = getopt(argc, argv, options)) != -1) { switch (c) { case 'a': /* scan all interfaces */ args->all = true; break; case 'd': /* restrict scan to "down" interfaces */ args->downonly = true; break; case 'f': if (optarg == NULL) usage(); setformat(optarg); break; case 'G': if (optarg == NULL || args->all == 0) usage(); args->nogroup = optarg; break; case 'k': args->printkeys = true; break; case 'l': /* scan interface names only */ args->namesonly++; break; case 'm': /* show media choices in status */ args->supmedia = true; break; case 'n': /* suppress module loading */ args->noload = true; break; case 'u': /* restrict scan to "up" interfaces */ args->uponly = true; break; case 'v': args->verbose++; break; case 'g': if (args->all) { if (optarg == NULL) usage(); args->matchgroup = optarg; break; } /* FALLTHROUGH */ default: for (p = opts; p != NULL; p = p->next) if (p->opt[0] == c) { p->cb(optarg); break; } if (p == NULL) usage(); break; } } argc -= optind; argv += optind; /* -l cannot be used with -a or -m */ if (args->namesonly && (args->all || args->supmedia)) usage(); /* nonsense.. */ if (args->uponly && args->downonly) usage(); /* no arguments is equivalent to '-a' */ if (!args->namesonly && argc < 1) args->all = 1; /* -a and -l allow an address family arg to limit the output */ if (args->all || args->namesonly) { if (argc > 1) usage(); if (argc == 1) { const struct afswtch *afp = af_getbyname(*argv); if (afp == NULL) { warnx("Address family '%s' unknown.", *argv); usage(); } if (afp->af_name != NULL) argc--, argv++; /* leave with afp non-zero */ args->afp = afp; } } else { /* not listing, need an argument */ if (argc < 1) usage(); } args->argc = argc; args->argv = argv; /* Sync global variables */ printkeys = args->printkeys; verbose = args->verbose; } +static int +ifconfig_wrapper(struct ifconfig_args *args, int iscreate, + const struct afswtch *uafp) +{ +#ifdef WITHOUT_NETLINK + struct io_handler h = {}; + return (ifconfig(args, &h, iscreate, uafp)); +#else + return (ifconfig_wrapper_nl(args, iscreate, uafp)); +#endif +} + +static bool +isargcreate(const char *arg) +{ + if (arg == NULL) + return (false); + + if (strcmp(arg, "create") == 0 || strcmp(arg, "plumb") == 0) + return (true); + + return (false); +} + int main(int ac, char *av[]) { char *envformat; size_t iflen; int flags; f_inet = f_inet6 = f_ether = f_addr = NULL; lifh = ifconfig_open(); if (lifh == NULL) err(EXIT_FAILURE, "ifconfig_open"); envformat = getenv("IFCONFIG_FORMAT"); if (envformat != NULL) setformat(envformat); /* * Ensure we print interface name when expected to, * even if we terminate early due to error. */ atexit(printifnamemaybe); args_parse(&args, ac, av); if (!args.all && !args.namesonly) { /* not listing, need an argument */ args.ifname = args_pop(&args); /* check and maybe load support for this interface */ ifmaybeload(&args, args.ifname); char *arg = args_peek(&args); if (if_nametoindex(args.ifname) == 0) { /* * NOTE: We must special-case the `create' command * right here as we would otherwise fail when trying * to find the interface. */ - if (arg != NULL && (strcmp(arg, "create") == 0 || - strcmp(arg, "plumb") == 0)) { + if (isargcreate(arg)) { iflen = strlcpy(name, args.ifname, sizeof(name)); if (iflen >= sizeof(name)) errx(1, "%s: cloning name too long", args.ifname); - ifconfig(args.argc, args.argv, 1, NULL); + ifconfig_wrapper(&args, 1, NULL); exit(exit_code); } #ifdef JAIL /* * NOTE: We have to special-case the `-vnet' command * right here as we would otherwise fail when trying * to find the interface as it lives in another vnet. */ if (arg != NULL && (strcmp(arg, "-vnet") == 0)) { iflen = strlcpy(name, args.ifname, sizeof(name)); if (iflen >= sizeof(name)) errx(1, "%s: interface name too long", args.ifname); - ifconfig(args.argc, args.argv, 0, NULL); + ifconfig_wrapper(&args, 0, NULL); exit(exit_code); } #endif errx(1, "interface %s does not exist", args.ifname); } else { /* * Do not allow use `create` command as hostname if * address family is not specified. */ - if (arg != NULL && (strcmp(arg, "create") == 0 || - strcmp(arg, "plumb") == 0)) { + if (isargcreate(arg)) { if (args.argc == 1) errx(1, "interface %s already exists", args.ifname); args_pop(&args); } } } /* Check for address family */ if (args.argc > 0) { args.afp = af_getbyname(args_peek(&args)); if (args.afp != NULL) args_pop(&args); } /* * Check for a requested configuration action on a single interface, * which doesn't require building, sorting, and searching the entire * system address list */ if ((args.argc > 0) && (args.ifname != NULL)) { iflen = strlcpy(name, args.ifname, sizeof(name)); if (iflen >= sizeof(name)) { warnx("%s: interface name too long, skipping", args.ifname); } else { flags = getifflags(name, -1, false); if (!(((flags & IFF_CANTCONFIG) != 0) || (args.downonly && (flags & IFF_UP) != 0) || (args.uponly && (flags & IFF_UP) == 0))) - ifconfig(args.argc, args.argv, 0, args.afp); + ifconfig_wrapper(&args, 0, args.afp); } goto done; } args.allfamilies = args.afp == NULL; list_interfaces(&args); done: freeformat(); ifconfig_close(lifh); exit(exit_code); } bool match_ether(const struct sockaddr_dl *sdl) { switch (sdl->sdl_type) { case IFT_ETHER: case IFT_L2VLAN: case IFT_BRIDGE: if (sdl->sdl_alen == ETHER_ADDR_LEN) return (true); default: return (false); } } static bool match_afp(const struct afswtch *afp, int sa_family, const struct sockaddr_dl *sdl) { if (afp == NULL) return (true); /* special case for "ether" address family */ if (!strcmp(afp->af_name, "ether")) { if (sdl == NULL && !match_ether(sdl)) return (false); return (true); } return (afp->af_af == sa_family); } bool match_if_flags(struct ifconfig_args *args, int if_flags) { if ((if_flags & IFF_CANTCONFIG) != 0) return (false); if (args->downonly && (if_flags & IFF_UP) != 0) return (false); if (args->uponly && (if_flags & IFF_UP) == 0) return (false); return (true); } #ifdef WITHOUT_NETLINK static void list_interfaces_ioctl(struct ifconfig_args *args) { struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); struct ifaddrs *ifap, *sifap, *ifa; struct ifa_order_elt *cur, *tmp; char *namecp = NULL; int ifindex; size_t iflen; if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); char *cp = NULL; if (calcorders(ifap, &q) != 0) err(EXIT_FAILURE, "calcorders"); sifap = sortifaddrs(ifap, cmpifaddrs, &q); TAILQ_FOREACH_SAFE(cur, &q, link, tmp) free(cur); ifindex = 0; for (ifa = sifap; ifa; ifa = ifa->ifa_next) { struct ifreq paifr = {}; const struct sockaddr_dl *sdl; strlcpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { memcpy(&paifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); } if (args->ifname != NULL && strcmp(args->ifname, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family == AF_LINK) sdl = (const struct sockaddr_dl *) ifa->ifa_addr; else sdl = NULL; if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0 && !args->namesonly) continue; iflen = strlcpy(name, ifa->ifa_name, sizeof(name)); if (iflen >= sizeof(name)) { warnx("%s: interface name too long, skipping", ifa->ifa_name); continue; } cp = ifa->ifa_name; if (!match_if_flags(args, ifa->ifa_flags)) continue; if (!group_member(ifa->ifa_name, args->matchgroup, args->nogroup)) continue; /* * Are we just listing the interfaces? */ if (args->namesonly) { if (namecp == cp) continue; if (!match_afp(args->afp, ifa->ifa_addr->sa_family, sdl)) continue; namecp = cp; ifindex++; if (ifindex > 1) printf(" "); fputs(name, stdout); continue; } ifindex++; if (args->argc > 0) - ifconfig(args->argc, args->argv, 0, args->afp); + ifconfig_wrapper(args, 0, args->afp); else status(args, sdl, ifa); } if (args->namesonly) printf("\n"); freeifaddrs(ifap); } #endif /* * Returns true if an interface should be listed because any its groups * matches shell pattern "match" and none of groups matches pattern "nomatch". * If any pattern is NULL, corresponding condition is skipped. */ bool group_member(const char *ifname, const char *match, const char *nomatch) { static int sock = -1; struct ifgroupreq ifgr; struct ifg_req *ifg; int len; bool matched, nomatched; /* Sanity checks. */ if (match == NULL && nomatch == NULL) return (true); if (ifname == NULL) return (false); memset(&ifgr, 0, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ); /* The socket is opened once. Let _exit() close it. */ if (sock == -1) { sock = socket(AF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) errx(1, "%s: socket(AF_LOCAL,SOCK_DGRAM)", __func__); } /* Determine amount of memory for the list of groups. */ if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { if (errno == EINVAL || errno == ENOTTY) return (false); else errx(1, "%s: SIOCGIFGROUP", __func__); } /* Obtain the list of groups. */ len = ifgr.ifgr_len; ifgr.ifgr_groups = (struct ifg_req *)calloc(len / sizeof(*ifg), sizeof(*ifg)); if (ifgr.ifgr_groups == NULL) errx(1, "%s: no memory", __func__); if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) errx(1, "%s: SIOCGIFGROUP", __func__); /* Perform matching. */ matched = false; nomatched = true; for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(*ifg); ifg++) { len -= sizeof(struct ifg_req); if (match) matched |= !fnmatch(match, ifg->ifgrq_group, 0); if (nomatch) nomatched &= fnmatch(nomatch, ifg->ifgrq_group, 0); } free(ifgr.ifgr_groups); if (match && !nomatch) return (matched); if (!match && nomatch) return (nomatched); return (matched && nomatched); } static struct afswtch *afs = NULL; void af_register(struct afswtch *p) { p->af_next = afs; afs = p; } static struct afswtch * af_getbyname(const char *name) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (strcmp(afp->af_name, name) == 0) return afp; return NULL; } struct afswtch * af_getbyfamily(int af) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (afp->af_af == af) return afp; return NULL; } void af_other_status(int s) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_other_status == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_other_status(s); setbit(afmask, afp->af_af); } } static void af_all_tunnel_status(int s) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_status_tunnel == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_status_tunnel(s); setbit(afmask, afp->af_af); } } static struct cmd *cmds = NULL; void cmd_register(struct cmd *p) { p->c_next = cmds; cmds = p; } static const struct cmd * cmd_lookup(const char *name, int iscreate) { const struct cmd *p; for (p = cmds; p != NULL; p = p->c_next) if (strcmp(name, p->c_name) == 0) { if (iscreate) { if (p->c_iscloneop) return p; } else { if (!p->c_iscloneop) return p; } } return NULL; } struct callback { callback_func *cb_func; void *cb_arg; struct callback *cb_next; }; static struct callback *callbacks = NULL; void callback_register(callback_func *func, void *arg) { struct callback *cb; cb = malloc(sizeof(struct callback)); if (cb == NULL) errx(1, "unable to allocate memory for callback"); cb->cb_func = func; cb->cb_arg = arg; cb->cb_next = callbacks; callbacks = cb; } /* specially-handled commands */ static void setifaddr(const char *, int, int, const struct afswtch *); static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); static void setifdstaddr(const char *, int, int, const struct afswtch *); static const struct cmd setifdstaddr_cmd = DEF_CMD("ifdstaddr", 0, setifdstaddr); +int +af_exec_ioctl(struct io_handler *h, int action, void *data) +{ + struct ifreq *req = (struct ifreq *)data; + + strlcpy(req->ifr_name, name, sizeof(req->ifr_name)); + if (ioctl(h->s, action, req) == 0) + return (0); + return (errno); +} + static void -delifaddr(int s, const struct afswtch *afp) +delifaddr(struct io_handler *h, const struct afswtch *afp) { - if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { + int error; + + if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", name, afp->af_name); clearaddr = 0; return; } - strlcpy(((struct ifreq *)afp->af_ridreq)->ifr_name, name, - sizeof ifr.ifr_name); - int ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); - if (ret < 0) { - if (errno == EADDRNOTAVAIL && (doalias >= 0)) { + afp->af_exec(h, afp->af_difaddr, afp->af_ridreq); + if (error != 0) { + if (error == EADDRNOTAVAIL && (doalias >= 0)) { /* means no previous address for interface */ } else - Perror("ioctl (SIOCDIFADDR)"); + Perrorc("ioctl (SIOCDIFADDR)", error); } } static void -addifaddr(int s, const struct afswtch *afp) +addifaddr(struct io_handler *h, const struct afswtch *afp) { - if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { + if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", name, afp->af_name); newaddr = 0; return; } if (setaddr || setmask) { - strlcpy(((struct ifreq *)afp->af_addreq)->ifr_name, name, - sizeof ifr.ifr_name); - if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) - Perror("ioctl (SIOCAIFADDR)"); + int error = afp->af_exec(h, afp->af_aifaddr, afp->af_addreq); + if (error != 0) + Perrorc("ioctl (SIOCAIFADDR)", error); } } int -ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp) +ifconfig(struct ifconfig_args *args, struct io_handler *h, int iscreate, + const struct afswtch *uafp) { const struct afswtch *afp, *nafp; const struct cmd *p; struct callback *cb; int s; + int argc = args->argc; + char *const *argv = args->argv; + strlcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); afp = NULL; if (uafp != NULL) afp = uafp; /* * This is the historical "accident" allowing users to configure IPv4 * addresses without the "inet" keyword which while a nice feature has * proven to complicate other things. We cannot remove this but only * make sure we will never have a similar implicit default for IPv6 or * any other address familiy. We need a fallback though for * ifconfig IF up/down etc. to work without INET support as people * never used ifconfig IF link up/down, etc. either. */ #ifndef RESCUE #ifdef INET if (afp == NULL && feature_present("inet")) afp = af_getbyname("inet"); #endif #endif if (afp == NULL) afp = af_getbyname("link"); if (afp == NULL) { warnx("Please specify an address_family."); usage(); } top: ifr.ifr_addr.sa_family = afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? AF_LOCAL : afp->af_af; if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0 && (uafp != NULL || errno != EAFNOSUPPORT || (s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); while (argc > 0) { p = cmd_lookup(*argv, iscreate); if (iscreate && p == NULL) { /* * Push the clone create callback so the new * device is created and can be used for any * remaining arguments. */ cb = callbacks; if (cb == NULL) errx(1, "internal error, no callback"); callbacks = cb->cb_next; cb->cb_func(s, cb->cb_arg); iscreate = 0; /* * Handle any address family spec that * immediately follows and potentially * recreate the socket. */ nafp = af_getbyname(*argv); if (nafp != NULL) { argc--, argv++; if (nafp != afp) { close(s); afp = nafp; goto top; } } /* * Look for a normal parameter. */ continue; } if (p == NULL) { /* * Not a recognized command, choose between setting * the interface address and the dst address. */ p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); } if (p->c_parameter == NEXTARG && p->c_u.c_func) { if (argv[1] == NULL) errx(1, "'%s' requires argument", p->c_name); p->c_u.c_func(argv[1], 0, s, afp); argc--, argv++; } else if (p->c_parameter == OPTARG && p->c_u.c_func) { p->c_u.c_func(argv[1], 0, s, afp); if (argv[1] != NULL) argc--, argv++; } else if (p->c_parameter == NEXTARG2 && p->c_u.c_func2) { if (argc < 3) errx(1, "'%s' requires 2 arguments", p->c_name); p->c_u.c_func2(argv[1], argv[2], s, afp); argc -= 2, argv += 2; } else if (p->c_parameter == SPARAM && p->c_u.c_func3) { p->c_u.c_func3(*argv, p->c_sparameter, s, afp); } else if (p->c_u.c_func) p->c_u.c_func(*argv, p->c_parameter, s, afp); argc--, argv++; } /* * Do any post argument processing required by the address family. */ if (afp->af_postproc != NULL) afp->af_postproc(s, afp, newaddr, getifflags(name, s, true)); /* * Do deferred callbacks registered while processing * command-line arguments. */ for (cb = callbacks; cb != NULL; cb = cb->cb_next) cb->cb_func(s, cb->cb_arg); /* * Do deferred operations. */ + h->s = s; if (clearaddr) - delifaddr(s, afp); + delifaddr(h, afp); if (newaddr) - addifaddr(s, afp); + addifaddr(h, afp); close(s); return(0); } /*ARGSUSED*/ static void setifaddr(const char *addr, int param, int s, const struct afswtch *afp) { if (afp->af_getaddr == NULL) return; /* * Delay the ioctl to set the interface addr until flags are all set. * The address interpretation may depend on the flags, * and the flags may change when the address is set. */ setaddr++; if (doalias == 0 && afp->af_af != AF_LINK) clearaddr = 1; afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); } static void settunnel(const char *src, const char *dst, int s, const struct afswtch *afp) { struct addrinfo *srcres, *dstres; int ecode; if (afp->af_settunnel == NULL) { warn("address family %s does not support tunnel setup", afp->af_name); return; } if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) errx(1, "source and destination address families do not match"); afp->af_settunnel(s, srcres, dstres); freeaddrinfo(srcres); freeaddrinfo(dstres); } /* ARGSUSED */ static void deletetunnel(const char *vname, int param, int s, const struct afswtch *afp) { if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } #ifdef JAIL static void setifvnet(const char *jname, int dummy __unused, int s, const struct afswtch *afp) { struct ifreq my_ifr; memcpy(&my_ifr, &ifr, sizeof(my_ifr)); my_ifr.ifr_jid = jail_getid(jname); if (my_ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl(s, SIOCSIFVNET, &my_ifr) < 0) err(1, "SIOCSIFVNET"); } static void setifrvnet(const char *jname, int dummy __unused, int s, const struct afswtch *afp) { struct ifreq my_ifr; memcpy(&my_ifr, &ifr, sizeof(my_ifr)); my_ifr.ifr_jid = jail_getid(jname); if (my_ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl(s, SIOCSIFRVNET, &my_ifr) < 0) err(1, "SIOCSIFRVNET(%d, %s)", my_ifr.ifr_jid, my_ifr.ifr_name); } #endif static void setifnetmask(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { if (afp->af_getaddr != NULL) { setmask++; afp->af_getaddr(addr, MASK); } } static void setifbroadaddr(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { if (afp->af_getaddr != NULL) - afp->af_getaddr(addr, DSTADDR); + afp->af_getaddr(addr, BRDADDR); } static void notealias(const char *addr, int param, int s, const struct afswtch *afp) { #define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) if (setaddr && doalias == 0 && param < 0) if (afp->af_addreq != NULL && afp->af_ridreq != NULL) bcopy((caddr_t)rqtosa(af_addreq), (caddr_t)rqtosa(af_ridreq), rqtosa(af_addreq)->sa_len); doalias = param; if (param < 0) { clearaddr = 1; newaddr = 0; } else clearaddr = 0; #undef rqtosa } /*ARGSUSED*/ static void setifdstaddr(const char *addr, int param __unused, int s, const struct afswtch *afp) { if (afp->af_getaddr != NULL) afp->af_getaddr(addr, DSTADDR); } static int getifflags(const char *ifname, int us, bool err_ok) { struct ifreq my_ifr; int s; memset(&my_ifr, 0, sizeof(my_ifr)); (void) strlcpy(my_ifr.ifr_name, ifname, sizeof(my_ifr.ifr_name)); if (us < 0) { if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) err(1, "socket(family AF_LOCAL,SOCK_DGRAM"); } else s = us; if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { if (!err_ok) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } } if (us < 0) close(s); return ((my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16)); } /* * Note: doing an SIOCIGIFFLAGS scribbles on the union portion * of the ifreq structure, which may confuse other parts of ifconfig. * Make a private copy so we can avoid that. */ static void setifflags(const char *vname, int value, int s, const struct afswtch *afp) { struct ifreq my_ifr; int flags; flags = getifflags(name, s, false); if (value < 0) { value = -value; flags &= ~value; } else flags |= value; memset(&my_ifr, 0, sizeof(my_ifr)); (void) strlcpy(my_ifr.ifr_name, name, sizeof(my_ifr.ifr_name)); my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } void setifcap(const char *vname, int value, int s, const struct afswtch *afp) { int flags; if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; if (value < 0) { value = -value; flags &= ~value; } else flags |= value; flags &= ifr.ifr_reqcap; /* Check for no change in capabilities. */ if (ifr.ifr_curcap == flags) return; ifr.ifr_reqcap = flags; if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) Perror(vname); } void setifcapnv(const char *vname, const char *arg, int s, const struct afswtch *afp) { nvlist_t *nvcap; void *buf; char *marg, *mopt; size_t nvbuflen; bool neg; if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) Perror("ioctl (SIOCGIFCAP)"); if ((ifr.ifr_curcap & IFCAP_NV) == 0) { warnx("IFCAP_NV not supported"); return; /* Not exit() */ } marg = strdup(arg); if (marg == NULL) Perror("strdup"); nvcap = nvlist_create(0); if (nvcap == NULL) Perror("nvlist_create"); while ((mopt = strsep(&marg, ",")) != NULL) { neg = *mopt == '-'; if (neg) mopt++; if (strcmp(mopt, "rxtls") == 0) { nvlist_add_bool(nvcap, "rxtls4", !neg); nvlist_add_bool(nvcap, "rxtls6", !neg); } else { nvlist_add_bool(nvcap, mopt, !neg); } } buf = nvlist_pack(nvcap, &nvbuflen); if (buf == NULL) { errx(1, "nvlist_pack error"); exit(1); } ifr.ifr_cap_nv.buf_length = ifr.ifr_cap_nv.length = nvbuflen; ifr.ifr_cap_nv.buffer = buf; if (ioctl(s, SIOCSIFCAPNV, (caddr_t)&ifr) < 0) Perror(vname); free(buf); nvlist_destroy(nvcap); free(marg); } static void setifmetric(const char *val, int dummy __unused, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_metric = atoi(val); if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) err(1, "ioctl SIOCSIFMETRIC (set metric)"); } static void setifmtu(const char *val, int dummy __unused, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_mtu = atoi(val); if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) err(1, "ioctl SIOCSIFMTU (set mtu)"); } static void setifpcp(const char *val, int arg __unused, int s, const struct afswtch *afp) { u_long ul; char *endp; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for pcp"); if (ul > 7) errx(1, "value for pcp out of range"); ifr.ifr_lan_pcp = ul; if (ioctl(s, SIOCSLANPCP, (caddr_t)&ifr) == -1) err(1, "SIOCSLANPCP"); } static void disableifpcp(const char *val, int arg __unused, int s, const struct afswtch *afp) { ifr.ifr_lan_pcp = IFNET_PCP_NONE; if (ioctl(s, SIOCSLANPCP, (caddr_t)&ifr) == -1) err(1, "SIOCSLANPCP"); } static void setifname(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *newname; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); newname = strdup(val); if (newname == NULL) err(1, "no memory to set ifname"); ifr.ifr_data = newname; if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { free(newname); err(1, "ioctl SIOCSIFNAME (set name)"); } printifname = 1; strlcpy(name, newname, sizeof(name)); free(newname); } /* ARGSUSED */ static void setifdescr(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *newdescr; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_buffer.length = strlen(val) + 1; if (ifr.ifr_buffer.length == 1) { ifr.ifr_buffer.buffer = newdescr = NULL; ifr.ifr_buffer.length = 0; } else { newdescr = strdup(val); ifr.ifr_buffer.buffer = newdescr; if (newdescr == NULL) { warn("no memory to set ifdescr"); return; } } if (ioctl(s, SIOCSIFDESCR, (caddr_t)&ifr) < 0) err(1, "ioctl SIOCSIFDESCR (set descr)"); free(newdescr); } /* ARGSUSED */ static void unsetifdescr(const char *val, int value, int s, const struct afswtch *afp) { setifdescr("", 0, s, 0); } #define IFFBITS \ "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \ "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ "\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\25STICKYARP" #define IFCAPBITS \ "\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ "\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ "\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \ "\26RXCSUM_IPV6\27TXCSUM_IPV6\31TXRTLMT\32HWRXTSTMP\33NOMAP\34TXTLS4\35TXTLS6" \ "\36VXLAN_HWCSUM\37VXLAN_HWTSO\40TXTLS_RTLMT" static void print_ifcap_nv(struct ifconfig_args *args, int s) { nvlist_t *nvcap; const char *nvname; void *buf, *cookie; bool first, val; int type; buf = malloc(IFR_CAP_NV_MAXBUFSIZE); if (buf == NULL) Perror("malloc"); ifr.ifr_cap_nv.buffer = buf; ifr.ifr_cap_nv.buf_length = IFR_CAP_NV_MAXBUFSIZE; if (ioctl(s, SIOCGIFCAPNV, (caddr_t)&ifr) != 0) Perror("ioctl (SIOCGIFCAPNV)"); nvcap = nvlist_unpack(ifr.ifr_cap_nv.buffer, ifr.ifr_cap_nv.length, 0); if (nvcap == NULL) Perror("nvlist_unpack"); printf("\toptions"); cookie = NULL; for (first = true;; first = false) { nvname = nvlist_next(nvcap, &type, &cookie); if (nvname == NULL) { printf("\n"); break; } if (type == NV_TYPE_BOOL) { val = nvlist_get_bool(nvcap, nvname); if (val) { printf("%c%s", first ? ' ' : ',', nvname); } } } if (args->supmedia) { printf("\tcapabilities"); cookie = NULL; for (first = true;; first = false) { nvname = nvlist_next(nvcap, &type, &cookie); if (nvname == NULL) { printf("\n"); break; } if (type == NV_TYPE_BOOL) printf("%c%s", first ? ' ' : ',', nvname); } } nvlist_destroy(nvcap); free(buf); if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) != 0) Perror("ioctl (SIOCGIFCAP)"); } void print_ifcap(struct ifconfig_args *args, int s) { if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) != 0) return; if ((ifr.ifr_curcap & IFCAP_NV) != 0) print_ifcap_nv(args, s); else { printb("\toptions", ifr.ifr_curcap, IFCAPBITS); putchar('\n'); if (args->supmedia && ifr.ifr_reqcap != 0) { printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); putchar('\n'); } } } void print_ifstatus(int s) { struct ifstat ifs; strlcpy(ifs.ifs_name, name, sizeof ifs.ifs_name); if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); } void print_metric(int s) { if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) printf(" metric %d", ifr.ifr_metric); } #ifdef WITHOUT_NETLINK static void print_mtu(int s) { if (ioctl(s, SIOCGIFMTU, &ifr) != -1) printf(" mtu %d", ifr.ifr_mtu); } static void print_description(int s) { for (;;) { if ((descr = reallocf(descr, descrlen)) != NULL) { ifr.ifr_buffer.buffer = descr; ifr.ifr_buffer.length = descrlen; if (ioctl(s, SIOCGIFDESCR, &ifr) == 0) { if (ifr.ifr_buffer.buffer == descr) { if (strlen(descr) > 0) printf("\tdescription: %s\n", descr); } else if (ifr.ifr_buffer.length > descrlen) { descrlen = ifr.ifr_buffer.length; continue; } } } else warn("unable to allocate memory for interface" "description"); break; } } /* * Print the status of the interface. If an address family was * specified, show only it; otherwise, show them all. */ static void status(struct ifconfig_args *args, const struct sockaddr_dl *sdl, struct ifaddrs *ifa) { struct ifaddrs *ift; int s; bool allfamilies = args->afp == NULL; if (args->afp == NULL) ifr.ifr_addr.sa_family = AF_LOCAL; else ifr.ifr_addr.sa_family = args->afp->af_af == AF_LINK ? AF_LOCAL : args->afp->af_af; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); if (s < 0) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); printf("%s: ", name); printb("flags", ifa->ifa_flags, IFFBITS); print_metric(s); print_mtu(s); putchar('\n'); print_description(s); print_ifcap(args, s); tunnel_status(s); for (ift = ifa; ift != NULL; ift = ift->ifa_next) { if (ift->ifa_addr == NULL) continue; if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) continue; if (allfamilies) { const struct afswtch *p; p = af_getbyfamily(ift->ifa_addr->sa_family); if (p != NULL && p->af_status != NULL) p->af_status(s, ift); } else if (args->afp->af_af == ift->ifa_addr->sa_family) args->afp->af_status(s, ift); } #if 0 if (allfamilies || afp->af_af == AF_LINK) { const struct afswtch *lafp; /* * Hack; the link level address is received separately * from the routing information so any address is not * handled above. Cobble together an entry and invoke * the status method specially. */ lafp = af_getbyname("lladdr"); if (lafp != NULL) { info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; lafp->af_status(s, &info); } } #endif if (allfamilies) af_other_status(s); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(s); print_ifstatus(s); if (args->verbose > 0) sfp_status(s, &ifr, args->verbose); close(s); return; } #endif void tunnel_status(int s) { af_all_tunnel_status(s); } -void -Perror(const char *cmd) +static void +Perrorc(const char *cmd, int error) { switch (errno) { case ENXIO: errx(1, "%s: no such interface", cmd); break; case EPERM: errx(1, "%s: permission denied", cmd); break; default: - err(1, "%s", cmd); + errc(1, error, "%s", cmd); } } +void +Perror(const char *cmd) +{ + Perrorc(cmd, errno); +} + /* * Print a value a la the %b format of the kernel's printf */ void printb(const char *s, unsigned v, const char *bits) { int i, any = 0; char c; if (bits && *bits == 8) printf("%s=%o", s, v); else printf("%s=%x", s, v); if (bits) { bits++; putchar('<'); while ((i = *bits++) != '\0') { if (v & (1u << (i-1))) { if (any) putchar(','); any = 1; for (; (c = *bits) > 32; bits++) putchar(c); } else for (; *bits > 32; bits++) ; } putchar('>'); } } void print_vhid(const struct ifaddrs *ifa, const char *s) { struct if_data *ifd; if (ifa->ifa_data == NULL) return; ifd = ifa->ifa_data; if (ifd->ifi_vhid == 0) return; printf(" vhid %d", ifd->ifi_vhid); } void ifmaybeload(struct ifconfig_args *args, const char *name) { #define MOD_PREFIX_LEN 3 /* "if_" */ struct module_stat mstat; int i, fileid, modid; char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; const char *cp; struct module_map_entry *mme; bool found; /* loading suppressed by the user */ if (args->noload) return; /* trim the interface number off the end */ strlcpy(ifname, name, sizeof(ifname)); dp = ifname + strlen(ifname) - 1; for (; dp > ifname; dp--) { if (isdigit(*dp)) *dp = '\0'; else break; } /* Either derive it from the map or guess otherwise */ *ifkind = '\0'; found = false; for (i = 0; i < nitems(module_map); ++i) { mme = &module_map[i]; if (strcmp(mme->ifname, ifname) == 0) { strlcpy(ifkind, mme->kldname, sizeof(ifkind)); found = true; break; } } /* We didn't have an alias for it... we'll guess. */ if (!found) { /* turn interface and unit into module name */ strlcpy(ifkind, "if_", sizeof(ifkind)); strlcat(ifkind, ifname, sizeof(ifkind)); } /* scan files in kernel */ mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { /* scan modules in file */ for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; /* strip bus name if present */ if ((cp = strchr(mstat.name, '/')) != NULL) { cp++; } else { cp = mstat.name; } /* * Is it already loaded? Don't compare with ifname if * we were specifically told which kld to use. Doing * so could lead to conflicts not trivially solved. */ if ((!found && strcmp(ifname, cp) == 0) || strcmp(ifkind, cp) == 0) return; } } /* * Try to load the module. But ignore failures, because ifconfig can't * infer the names of all drivers (eg mlx4en(4)). */ (void) kldload(ifkind); } static struct cmd basic_cmds[] = { DEF_CMD("up", IFF_UP, setifflags), DEF_CMD("down", -IFF_UP, setifflags), DEF_CMD("arp", -IFF_NOARP, setifflags), DEF_CMD("-arp", IFF_NOARP, setifflags), DEF_CMD("debug", IFF_DEBUG, setifflags), DEF_CMD("-debug", -IFF_DEBUG, setifflags), DEF_CMD_ARG("description", setifdescr), DEF_CMD_ARG("descr", setifdescr), DEF_CMD("-description", 0, unsetifdescr), DEF_CMD("-descr", 0, unsetifdescr), DEF_CMD("promisc", IFF_PPROMISC, setifflags), DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), DEF_CMD("add", IFF_UP, notealias), DEF_CMD("alias", IFF_UP, notealias), DEF_CMD("-alias", -IFF_UP, notealias), DEF_CMD("delete", -IFF_UP, notealias), DEF_CMD("remove", -IFF_UP, notealias), #ifdef notdef #define EN_SWABIPS 0x1000 DEF_CMD("swabips", EN_SWABIPS, setifflags), DEF_CMD("-swabips", -EN_SWABIPS, setifflags), #endif DEF_CMD_ARG("netmask", setifnetmask), DEF_CMD_ARG("metric", setifmetric), DEF_CMD_ARG("broadcast", setifbroadaddr), DEF_CMD_ARG2("tunnel", settunnel), DEF_CMD("-tunnel", 0, deletetunnel), DEF_CMD("deletetunnel", 0, deletetunnel), #ifdef JAIL DEF_CMD_ARG("vnet", setifvnet), DEF_CMD_ARG("-vnet", setifrvnet), #endif DEF_CMD("link0", IFF_LINK0, setifflags), DEF_CMD("-link0", -IFF_LINK0, setifflags), DEF_CMD("link1", IFF_LINK1, setifflags), DEF_CMD("-link1", -IFF_LINK1, setifflags), DEF_CMD("link2", IFF_LINK2, setifflags), DEF_CMD("-link2", -IFF_LINK2, setifflags), DEF_CMD("monitor", IFF_MONITOR, setifflags), DEF_CMD("-monitor", -IFF_MONITOR, setifflags), DEF_CMD("mextpg", IFCAP_MEXTPG, setifcap), DEF_CMD("-mextpg", -IFCAP_MEXTPG, setifcap), DEF_CMD("staticarp", IFF_STATICARP, setifflags), DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), DEF_CMD("stickyarp", IFF_STICKYARP, setifflags), DEF_CMD("-stickyarp", -IFF_STICKYARP, setifflags), DEF_CMD("rxcsum6", IFCAP_RXCSUM_IPV6, setifcap), DEF_CMD("-rxcsum6", -IFCAP_RXCSUM_IPV6, setifcap), DEF_CMD("txcsum6", IFCAP_TXCSUM_IPV6, setifcap), DEF_CMD("-txcsum6", -IFCAP_TXCSUM_IPV6, setifcap), DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), DEF_CMD("netcons", IFCAP_NETCONS, setifcap), DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), DEF_CMD_ARG("pcp", setifpcp), DEF_CMD("-pcp", 0, disableifpcp), DEF_CMD("polling", IFCAP_POLLING, setifcap), DEF_CMD("-polling", -IFCAP_POLLING, setifcap), DEF_CMD("tso6", IFCAP_TSO6, setifcap), DEF_CMD("-tso6", -IFCAP_TSO6, setifcap), DEF_CMD("tso4", IFCAP_TSO4, setifcap), DEF_CMD("-tso4", -IFCAP_TSO4, setifcap), DEF_CMD("tso", IFCAP_TSO, setifcap), DEF_CMD("-tso", -IFCAP_TSO, setifcap), DEF_CMD("toe", IFCAP_TOE, setifcap), DEF_CMD("-toe", -IFCAP_TOE, setifcap), DEF_CMD("lro", IFCAP_LRO, setifcap), DEF_CMD("-lro", -IFCAP_LRO, setifcap), DEF_CMD("txtls", IFCAP_TXTLS, setifcap), DEF_CMD("-txtls", -IFCAP_TXTLS, setifcap), DEF_CMD_SARG("rxtls", IFCAP2_RXTLS4_NAME "," IFCAP2_RXTLS6_NAME, setifcapnv), DEF_CMD_SARG("-rxtls", "-"IFCAP2_RXTLS4_NAME ",-" IFCAP2_RXTLS6_NAME, setifcapnv), DEF_CMD("wol", IFCAP_WOL, setifcap), DEF_CMD("-wol", -IFCAP_WOL, setifcap), DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), DEF_CMD("txrtlmt", IFCAP_TXRTLMT, setifcap), DEF_CMD("-txrtlmt", -IFCAP_TXRTLMT, setifcap), DEF_CMD("txtlsrtlmt", IFCAP_TXTLS_RTLMT, setifcap), DEF_CMD("-txtlsrtlmt", -IFCAP_TXTLS_RTLMT, setifcap), DEF_CMD("hwrxtstmp", IFCAP_HWRXTSTMP, setifcap), DEF_CMD("-hwrxtstmp", -IFCAP_HWRXTSTMP, setifcap), DEF_CMD("normal", -IFF_LINK0, setifflags), DEF_CMD("compress", IFF_LINK0, setifflags), DEF_CMD("noicmp", IFF_LINK1, setifflags), DEF_CMD_ARG("mtu", setifmtu), DEF_CMD_ARG("name", setifname), }; static __constructor void ifconfig_ctor(void) { size_t i; for (i = 0; i < nitems(basic_cmds); i++) cmd_register(&basic_cmds[i]); } diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index 7b2b88a4dfac..9d37a6e75eba 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -1,282 +1,294 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1997 Peter Wemm. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the FreeBSD Project * by Peter Wemm. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * so there! * * $FreeBSD$ */ #pragma once #include #include #define __constructor __attribute__((constructor)) struct afswtch; struct cmd; typedef void c_func(const char *cmd, int arg, int s, const struct afswtch *afp); typedef void c_func2(const char *arg1, const char *arg2, int s, const struct afswtch *afp); typedef void c_func3(const char *cmd, const char *arg, int s, const struct afswtch *afp); struct cmd { const char *c_name; int c_parameter; #define NEXTARG 0xffffff /* has following arg */ #define NEXTARG2 0xfffffe /* has 2 following args */ #define OPTARG 0xfffffd /* has optional following arg */ #define SPARAM 0xfffffc /* parameter is string c_sparameter */ const char *c_sparameter; union { c_func *c_func; c_func2 *c_func2; c_func3 *c_func3; } c_u; int c_iscloneop; struct cmd *c_next; }; void cmd_register(struct cmd *); typedef void callback_func(int s, void *); void callback_register(callback_func *, void *); /* * Macros for declaring command functions and initializing entries. */ #define DECL_CMD_FUNC(name, cmd, arg) \ void name(const char *cmd, int arg, int s, const struct afswtch *afp) #define DECL_CMD_FUNC2(name, arg1, arg2) \ void name(const char *arg1, const char *arg2, int s, \ const struct afswtch *afp) #define DEF_CMD(name, param, func) { \ .c_name = (name), \ .c_parameter = (param), \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_ARG(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG, \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_OPTARG(name, func) { \ .c_name = (name), \ .c_parameter = OPTARG, \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_ARG2(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG2, \ .c_u = { .c_func2 = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_SARG(name, sparam, func) { \ .c_name = (name), \ .c_parameter = SPARAM, \ .c_sparameter = (sparam), \ .c_u = { .c_func3 = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CLONE_CMD(name, param, func) { \ .c_name = (name), \ .c_parameter = (param), \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 1, \ .c_next = NULL, \ } #define DEF_CLONE_CMD_ARG(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG, \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 1, \ .c_next = NULL, \ } #define DEF_CLONE_CMD_ARG2(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG2, \ .c_u = { .c_func2 = (func) }, \ .c_iscloneop = 1, \ .c_next = NULL, \ } struct ifaddrs; struct addrinfo; enum { - RIDADDR, - ADDR, - MASK, - DSTADDR, + RIDADDR = 0, + ADDR = 1, + MASK = 2, + DSTADDR = 3, +#ifdef WITHOUT_NETLINK + BRDADDR = 3, +#else + BRDADDR = 4, +#endif }; struct snl_state; struct snl_parsed_addr; struct snl_parsed_link; typedef struct snl_parsed_link if_link_t; typedef struct snl_parsed_addr if_addr_t; struct ifconfig_args; struct io_handler { int s; /* socket to use for ioctls */ struct snl_state *ss; /* NETLINK_ROUTE snl(3) socket */ }; typedef void af_setvhid_f(int vhid); typedef void af_status_nl_f(struct ifconfig_args *args, struct io_handler *h, if_link_t *link, if_addr_t *ifa); +typedef int af_exec_f(struct io_handler *h, int action, void *data); struct afswtch { const char *af_name; /* as given on cmd line, e.g. "inet" */ short af_af; /* AF_* */ /* * Status is handled one of two ways; if there is an * address associated with the interface then the * associated address family af_status method is invoked * with the appropriate addressin info. Otherwise, if * all possible info is to be displayed and af_other_status * is defined then it is invoked after all address status * is presented. */ #ifndef WITHOUT_NETLINK af_status_nl_f *af_status_nl; #else void (*af_status)(int, const struct ifaddrs *); #endif void (*af_other_status)(int); /* parse address method */ void (*af_getaddr)(const char *, int); /* parse prefix method (IPv6) */ void (*af_getprefix)(const char *, int); void (*af_postproc)(int s, const struct afswtch *, int newaddr, int ifflags); af_setvhid_f *af_setvhid; /* Set CARP vhid for an address */ + af_exec_f *af_exec; /* Handler to interact with kernel */ u_long af_difaddr; /* set dst if address ioctl */ u_long af_aifaddr; /* set if address ioctl */ void *af_ridreq; /* */ void *af_addreq; /* */ struct afswtch *af_next; /* XXX doesn't fit model */ void (*af_status_tunnel)(int); void (*af_settunnel)(int s, struct addrinfo *srcres, struct addrinfo *dstres); }; void af_register(struct afswtch *); +int af_exec_ioctl(struct io_handler *h, int action, void *data); struct ifconfig_args { bool all; /* Match everything */ bool downonly; /* Down-only items */ bool uponly; /* Up-only items */ bool namesonly; /* Output only names */ bool noload; /* Do not load relevant kernel modules */ bool supmedia; /* Supported media */ bool printkeys; /* Print security keys */ bool allfamilies; /* Print all families */ int verbose; /* verbosity level */ int argc; char **argv; const char *ifname; /* Requested interface name */ const char *matchgroup; /* Group name to match */ const char *nogroup; /* Group name to exclude */ const struct afswtch *afp; /* AF we're operating on */ }; struct option { const char *opt; const char *opt_usage; void (*cb)(const char *arg); struct option *next; }; void opt_register(struct option *); extern ifconfig_handle_t *lifh; extern struct ifreq ifr; extern char name[IFNAMSIZ]; /* name of interface */ extern int allmedia; extern int printkeys; extern int newaddr; extern int verbose; extern int printifname; extern int exit_code; extern struct ifconfig_args args; void setifcap(const char *, int value, int s, const struct afswtch *); void setifcapnv(const char *vname, const char *arg, int s, const struct afswtch *afp); void Perror(const char *cmd); void printb(const char *s, unsigned value, const char *bits); void ifmaybeload(struct ifconfig_args *args, const char *name); typedef int clone_match_func(const char *); typedef void clone_callback_func(int, struct ifreq *); void clone_setdefcallback_prefix(const char *, clone_callback_func *); void clone_setdefcallback_filter(clone_match_func *, clone_callback_func *); void sfp_status(int s, struct ifreq *ifr, int verbose); struct sockaddr_dl; bool match_ether(const struct sockaddr_dl *sdl); bool match_if_flags(struct ifconfig_args *args, int if_flags); -int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp); +int ifconfig(struct ifconfig_args *args, struct io_handler *h, + int iscreate, const struct afswtch *uafp); bool group_member(const char *ifname, const char *match, const char *nomatch); void print_ifcap(struct ifconfig_args *args, int s); void tunnel_status(int s); struct afswtch *af_getbyfamily(int af); void af_other_status(int s); void print_ifstatus(int s); void print_metric(int s); /* Netlink-related functions */ void list_interfaces_nl(struct ifconfig_args *args); +int ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate, + const struct afswtch *uafp); +uint32_t if_nametoindex_nl(struct snl_state *ss, const char *ifname); /* * XXX expose this so modules that neeed to know of any pending * operations on ifmedia can avoid cmd line ordering confusion. */ struct ifmediareq *ifmedia_getstate(void); void print_vhid(const struct ifaddrs *, const char *); void ioctl_ifcreate(int s, struct ifreq *); diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c index 26a42b5866c5..598444d0d029 100644 --- a/sbin/ifconfig/ifconfig_netlink.c +++ b/sbin/ifconfig/ifconfig_netlink.c @@ -1,427 +1,469 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2022 Alexander V. Chernikov * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" static const char *IFFBITS[] = { "UP", /* 00:0x1 IFF_UP*/ "BROADCAST", /* 01:0x2 IFF_BROADCAST*/ "DEBUG", /* 02:0x4 IFF_DEBUG*/ "LOOPBACK", /* 03:0x8 IFF_LOOPBACK*/ "POINTOPOINT", /* 04:0x10 IFF_POINTOPOINT*/ "NEEDSEPOCH", /* 05:0x20 IFF_NEEDSEPOCH*/ "RUNNING", /* 06:0x40 IFF_DRV_RUNNING*/ "NOARP", /* 07:0x80 IFF_NOARP*/ "PROMISC", /* 08:0x100 IFF_PROMISC*/ "ALLMULTI", /* 09:0x200 IFF_ALLMULTI*/ "DRV_OACTIVE", /* 10:0x400 IFF_DRV_OACTIVE*/ "SIMPLEX", /* 11:0x800 IFF_SIMPLEX*/ "LINK0", /* 12:0x1000 IFF_LINK0*/ "LINK1", /* 13:0x2000 IFF_LINK1*/ "LINK2", /* 14:0x4000 IFF_LINK2*/ "MULTICAST", /* 15:0x8000 IFF_MULTICAST*/ "CANTCONFIG", /* 16:0x10000 IFF_CANTCONFIG*/ "PPROMISC", /* 17:0x20000 IFF_PPROMISC*/ "MONITOR", /* 18:0x40000 IFF_MONITOR*/ "STATICARP", /* 19:0x80000 IFF_STATICARP*/ "STICKYARP", /* 20:0x100000 IFF_STICKYARP*/ "DYING", /* 21:0x200000 IFF_DYING*/ "RENAMING", /* 22:0x400000 IFF_RENAMING*/ "NOGROUP", /* 23:0x800000 IFF_NOGROUP*/ "LOWER_UP", /* 24:0x1000000 IFF_NETLINK_1*/ }; static void print_bits(const char *btype, uint32_t *v, const int v_count, const char **names, const int n_count) { int num = 0; for (int i = 0; i < v_count * 32; i++) { bool is_set = v[i / 32] & (1 << (i % 32)); if (i == 31) v++; if (is_set) { if (num++ == 0) printf("<"); if (num != 1) printf(","); if (i < n_count) printf("%s", names[i]); else printf("%s_%d", btype, i); } } if (num > 0) printf(">"); } static void nl_init_socket(struct snl_state *ss) { if (snl_init(ss, NETLINK_ROUTE)) return; if (modfind("netlink") == -1 && errno == ENOENT) { /* Try to load */ if (kldload("netlink") == -1) err(1, "netlink is not loaded and load attempt failed"); if (snl_init(ss, NETLINK_ROUTE)) return; } err(1, "unable to open netlink socket"); } +int +ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate, + const struct afswtch *uafp) +{ + struct snl_state ss = {}; + struct io_handler h = { .ss = &ss }; + + nl_init_socket(&ss); + + int error = ifconfig(args, &h, iscreate, uafp); + + snl_free(&ss); + + return (error); +} + struct ifa { struct ifa *next; uint32_t count; uint32_t idx; struct snl_parsed_addr addr; }; struct iface { struct snl_parsed_link link; struct ifa *ifa; uint32_t ifa_count; uint32_t idx; }; struct ifmap { uint32_t size; uint32_t count; struct iface **ifaces; }; /* * Returns ifmap ifindex->snl_parsed_link. * Memory is allocated using snl temporary buffers */ static struct ifmap * prepare_ifmap(struct snl_state *ss) { struct snl_writer nw = {}; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); hdr->nlmsg_flags |= NLM_F_DUMP; snl_reserve_msg_object(&nw, struct ifinfomsg); if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) return (NULL); uint32_t nlmsg_seq = hdr->nlmsg_seq; struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap)); struct snl_errmsg_data e = {}; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct iface *iface = snl_allocz(ss, sizeof(*iface)); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link)) continue; if (iface->link.ifi_index >= ifmap->size) { size_t new_size = MAX(ifmap->size, 32); while (new_size <= iface->link.ifi_index + 1) new_size *= 2; struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *)); memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *)); ifmap->ifaces = ifaces; ifmap->size = new_size; } ifmap->ifaces[iface->link.ifi_index] = iface; ifmap->count++; iface->idx = ifmap->count; } return (ifmap); } +uint32_t +if_nametoindex_nl(struct snl_state *ss, const char *ifname) +{ + struct snl_writer nw = {}; + struct snl_parsed_link_simple link = {}; + + snl_init_writer(ss, &nw); + struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); + snl_reserve_msg_object(&nw, struct ifinfomsg); + snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname); + + if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + return (0); + + hdr = snl_read_reply(ss, hdr->nlmsg_seq); + if (hdr->nlmsg_type != NL_RTM_NEWLINK) + return (0); + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link)) + return (0); + + return (link.ifi_index); +} + static void prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap) { struct snl_writer nw = {}; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR); hdr->nlmsg_flags |= NLM_F_DUMP; snl_reserve_msg_object(&nw, struct ifaddrmsg); if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) return; uint32_t nlmsg_seq = hdr->nlmsg_seq; struct snl_errmsg_data e = {}; uint32_t count = 0; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct ifa *ifa = snl_allocz(ss, sizeof(*ifa)); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr)) continue; const uint32_t ifindex = ifa->addr.ifa_index; if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL) continue; struct iface *iface = ifmap->ifaces[ifindex]; ifa->next = iface->ifa; ifa->count = ++count; iface->ifa = ifa; iface->ifa_count++; } } static bool match_iface(struct ifconfig_args *args, struct iface *iface) { if_link_t *link = &iface->link; if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname)) return (false); if (!match_if_flags(args, link->ifi_flags)) return (false); if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup)) return (false); if (args->afp == NULL) return (true); if (!strcmp(args->afp->af_name, "ether")) { if (link->ifla_address == NULL) return (false); struct sockaddr_dl sdl = { .sdl_len = sizeof(struct sockaddr_dl), .sdl_family = AF_LINK, .sdl_type = link->ifi_type, .sdl_alen = NLA_DATA_LEN(link->ifla_address), }; return (match_ether(&sdl)); } for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { if (args->afp->af_af == ifa->addr.ifa_family) return (true); } return (false); } /* Sort according to the kernel-provided order */ static int cmp_iface(const void *_a, const void *_b) { const struct iface *a = *((const void * const *)_a); const struct iface *b = *((const void * const *)_b); return ((a->idx > b->idx) * 2 - 1); } static int cmp_ifaddr(const void *_a, const void *_b) { const struct ifa *a = *((const void * const *)_a); const struct ifa *b = *((const void * const *)_b); if (a->addr.ifa_family != b->addr.ifa_family) return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1); return ((a->idx > b->idx) * 2 - 1); } static void sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface) { if (iface->ifa_count == 0) return; struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *)); struct ifa *ifa = iface->ifa; for (int i = 0; i < iface->ifa_count; i++) { struct ifa *ifa_next = ifa->next; sorted_ifaddrs[i] = ifa; ifa->next = NULL; ifa = ifa_next; } qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr); ifa = sorted_ifaddrs[0]; iface->ifa = ifa; for (int i = 1; i < iface->ifa_count; i++) { ifa->next = sorted_ifaddrs[i]; ifa = sorted_ifaddrs[i]; } } static void status_nl(struct ifconfig_args *args, struct io_handler *h, struct iface *iface) { if_link_t *link = &iface->link; printf("%s: ", link->ifla_ifname); printf("flags=%x", link->ifi_flags); print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS)); print_metric(h->s); printf(" mtu %d\n", link->ifla_mtu); if (link->ifla_ifalias != NULL) printf("\tdescription: %s\n", link->ifla_ifalias); /* TODO: convert to netlink */ strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name)); print_ifcap(args, h->s); tunnel_status(h->s); if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) { /* Start with link-level */ const struct afswtch *p = af_getbyfamily(AF_LINK); if (p != NULL && link->ifla_address != NULL) p->af_status_nl(args, h, link, NULL); } sort_iface_ifaddrs(h->ss, iface); for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { if (args->allfamilies) { const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family); if (p != NULL) p->af_status_nl(args, h, link, &ifa->addr); } else if (args->afp->af_af == ifa->addr.ifa_family) { const struct afswtch *p = args->afp; p->af_status_nl(args, h, link, &ifa->addr); } } /* TODO: convert to netlink */ if (args->allfamilies) af_other_status(h->s); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(h->s); print_ifstatus(h->s); if (args->verbose > 0) sfp_status(h->s, &ifr, args->verbose); } static int get_local_socket(void) { int s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s < 0) err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL); return (s); } static void set_global_ifname(if_link_t *link) { int iflen = strlcpy(name, link->ifla_ifname, sizeof(name)); if (iflen >= sizeof(name)) errx(1, "%s: cloning name too long", link->ifla_ifname); strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name)); } void list_interfaces_nl(struct ifconfig_args *args) { struct snl_state ss = {}; nl_init_socket(&ss); struct ifmap *ifmap = prepare_ifmap(&ss); struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *)); for (int i = 0, num = 0; i < ifmap->size; i++) { if (ifmap->ifaces[i] != NULL) { sorted_ifaces[num++] = ifmap->ifaces[i]; if (num == ifmap->count) break; } } qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface); prepare_ifaddrs(&ss, ifmap); struct io_handler h = { .s = get_local_socket(), .ss = &ss, }; for (int i = 0, num = 0; i < ifmap->count; i++) { struct iface *iface = sorted_ifaces[i]; if (!match_iface(args, iface)) continue; set_global_ifname(&iface->link); if (args->namesonly) { if (num++ != 0) printf(" "); fputs(iface->link.ifla_ifname, stdout); } else if (args->argc == 0) status_nl(args, &h, iface); - else - ifconfig(args->argc, args->argv, 0, args->afp); + else { + struct io_handler hh = { .s = -1, .ss = &ss }; + + ifconfig(args, &hh, 0, args->afp); + } } if (args->namesonly) printf("\n"); close(h.s); snl_free(&ss); }