diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index 7a771a885412..f1755bcb7a16 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -1,798 +1,794 @@ /*- * 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; #ifdef WITHOUT_NETLINK static int prefix(void *, int); #endif static char *sec2str(time_t); static int explicit_prefix = 0; extern char *f_inet6, *f_addr; extern void setnd6flags(if_ctx *, const char *, int); extern void setnd6defif(if_ctx *,const char *, int); extern void nd6_status(if_ctx *); static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ static void setifprefixlen(if_ctx *ctx __netlink_unused, const char *addr, int dummy __unused) { #ifdef WITHOUT_NETLINK const struct afswtch *afp = ctx->afp; 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(if_ctx *ctx, const char *dummyaddr __unused, int flag) { const struct afswtch *afp = ctx->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(if_ctx *ctx, const char *cmd, const char *val) { const struct afswtch *afp = ctx->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) { lifetime->ia6t_expire = now.tv_sec + newval; lifetime->ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { lifetime->ia6t_preferred = now.tv_sec + newval; lifetime->ia6t_pltime = newval; } } static void setip6pltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "pltime", seconds); } static void setip6vltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "vltime", seconds); } static void setip6eui64(if_ctx *ctx, const char *cmd, int dummy __unused) { const struct afswtch *afp = ctx->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, ctx->ifname) == 0) { sin6 = (const struct sockaddr_in6 *)satosin6(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(if_ctx *ctx __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 void in6_status_nl(if_ctx *ctx __unused, if_link_t *link __unused, 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_copyaddr(if_ctx *ctx __unused, int to, int from) { sin6tab_nl[to]->addr = sin6tab_nl[from]->addr; sin6tab_nl[to]->set = sin6tab_nl[from]->set; } 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(if_ctx *ctx, unsigned long action, void *data) { struct in6_pdata *pdata = (struct in6_pdata *)data; struct snl_writer nw = {}; snl_init_writer(ctx->io_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(ctx->io_ss, ctx->ifname); 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(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); return (e.error); } #endif #ifdef WITHOUT_NETLINK static struct sockaddr_in6 *sin6tab[] = { &in6_ridreq.ifr_addr, &in6_addreq.ifra_addr, &in6_addreq.ifra_prefixmask, &in6_addreq.ifra_dstaddr }; static void in6_copyaddr(if_ctx *ctx, int to, int from) { memcpy(sin6tab[to], sin6tab[from], sizeof(struct sockaddr_in6)); } 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(if_ctx *ctx, 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(ctx, "64", 0); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(if_ctx *ctx) { 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, ctx->ifname, sizeof(in6_ifr.ifr_name)); if (ioctl_ctx(ctx, 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_ctx(ctx, 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(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_addreq; memset(&in6_addreq, 0, sizeof(in6_addreq)); strlcpy(in6_addreq.ifra_name, ctx->ifname, 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_ctx(ctx, 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 = in6_status_nl, #endif .af_getaddr = in6_getaddr, .af_copyaddr = in6_copyaddr, #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 *arg __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/ifconfig.c b/sbin/ifconfig/ifconfig.c index 4f1d95c43a63..e0aae77a317e 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,2056 +1,2056 @@ /*- * 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 #ifdef JAIL #include #endif #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]; #ifdef WITHOUT_NETLINK static char *descr = NULL; static size_t descrlen = 64; #endif static int setaddr; static int setmask; static int doalias; static int clearaddr; -int newaddr = 1; +static int newaddr = 1; int exit_code = 0; static char ifname_to_print[IFNAMSIZ]; /* Helper for printifnamemaybe() */ /* Formatter Strings */ char *f_inet, *f_inet6, *f_ether, *f_addr; #ifdef WITHOUT_NETLINK static void list_interfaces_ioctl(if_ctx *ctx); static void status(if_ctx *ctx, const struct sockaddr_dl *sdl, struct ifaddrs *ifa); #endif 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); 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 [-j jail] [-f type:format] %sinterface address_family\n" " [address [dest_address]] [parameters]\n" " ifconfig [-j jail] interface create\n" " ifconfig [-j jail] -a %s[-d] [-m] [-u] [-v] [address_family]\n" " ifconfig [-j jail] -l [-d] [-u] [address_family]\n" " ifconfig [-j jail] %s[-d] [-m] [-u] [-v]\n", options, options, options); exit(1); } static void ifname_update(if_ctx *ctx, const char *name) { strlcpy(ctx->_ifname_storage_ioctl, name, sizeof(ctx->_ifname_storage_ioctl)); ctx->ifname = ctx->_ifname_storage_ioctl; strlcpy(ifname_to_print, name, sizeof(ifname_to_print)); } void ifcreate_ioctl(if_ctx *ctx, struct ifreq *ifr) { char ifname_orig[IFNAMSIZ]; strlcpy(ifname_orig, ifr->ifr_name, sizeof(ifname_orig)); if (ioctl(ctx->io_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); } } if (strncmp(ifname_orig, ifr->ifr_name, sizeof(ifname_orig)) != 0) ifname_update(ctx, ifr->ifr_name); } #ifdef WITHOUT_NETLINK 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); } #endif 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); } #ifdef WITHOUT_NETLINK 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); } #endif static void printifnamemaybe(void) { if (ifname_to_print[0] != '\0') printf("%s\n", ifname_to_print); } static void list_interfaces(if_ctx *ctx) { #ifdef WITHOUT_NETLINK list_interfaces_ioctl(ctx); #else list_interfaces_nl(ctx->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:j: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 'j': #ifdef JAIL if (optarg == NULL) usage(); args->jail_name = optarg; #else Perror("not built with jail support"); #endif 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; } static int ifconfig(if_ctx *ctx, int iscreate, const struct afswtch *uafp) { #ifdef WITHOUT_NETLINK return (ifconfig_ioctl(ctx, iscreate, uafp)); #else return (ifconfig_nl(ctx, 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); } static bool isnametoolong(const char *ifname) { return (strlen(ifname) >= IFNAMSIZ); } int main(int ac, char *av[]) { char *envformat; int flags; #ifdef JAIL int jid; #endif struct ifconfig_args _args = {}; struct ifconfig_args *args = &_args; struct ifconfig_context ctx = { .args = args, .io_s = -1, }; 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); #ifdef JAIL if (args->jail_name) { jid = jail_getid(args->jail_name); if (jid == -1) Perror("jail not found"); if (jail_attach(jid) != 0) Perror("cannot attach to jail"); } #endif if (!args->all && !args->namesonly) { /* not listing, need an argument */ args->ifname = args_pop(args); ctx.ifname = args->ifname; /* 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 (isargcreate(arg)) { if (isnametoolong(args->ifname)) errx(1, "%s: cloning name too long", args->ifname); ifconfig(&ctx, 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)) { if (isnametoolong(args->ifname)) errx(1, "%s: interface name too long", args->ifname); ifconfig(&ctx, 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 (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)) { if (isnametoolong(args->ifname)) warnx("%s: interface name too long, skipping", args->ifname); else { flags = getifflags(args->ifname, -1, false); if (!(((flags & IFF_CANTCONFIG) != 0) || (args->downonly && (flags & IFF_UP) != 0) || (args->uponly && (flags & IFF_UP) == 0))) ifconfig(&ctx, 0, args->afp); } goto done; } args->allfamilies = args->afp == NULL; list_interfaces(&ctx); 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); } } 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 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); } static void list_interfaces_ioctl(if_ctx *ctx) { struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); struct ifaddrs *ifap, *sifap, *ifa; struct ifa_order_elt *cur, *tmp; char *namecp = NULL; int ifindex; struct ifconfig_args *args = ctx->args; 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; if (isnametoolong(ifa->ifa_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(cp, stdout); continue; } ifindex++; if (args->argc > 0) ifconfig(ctx, 0, args->afp); else status(ctx, 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; unsigned 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(if_ctx *ctx) { 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(ctx); setbit(afmask, afp->af_af); } } static void af_all_tunnel_status(if_ctx *ctx) { 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(ctx); 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(if_ctx *ctx, const char *addr, int param); static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused); static const struct cmd setifdstaddr_cmd = DEF_CMD("ifdstaddr", 0, setifdstaddr); int af_exec_ioctl(if_ctx *ctx, unsigned long action, void *data) { struct ifreq *req = (struct ifreq *)data; strlcpy(req->ifr_name, ctx->ifname, sizeof(req->ifr_name)); if (ioctl_ctx(ctx, action, req) == 0) return (0); return (errno); } static void delifaddr(if_ctx *ctx, const struct afswtch *afp) { int error; if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", ctx->ifname, afp->af_name); clearaddr = 0; return; } error = afp->af_exec(ctx, afp->af_difaddr, afp->af_ridreq); if (error != 0) { if (error == EADDRNOTAVAIL && (doalias >= 0)) { /* means no previous address for interface */ } else Perrorc("ioctl (SIOCDIFADDR)", error); } } static void addifaddr(if_ctx *ctx, const struct afswtch *afp) { if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", ctx->ifname, afp->af_name); newaddr = 0; return; } if (setaddr || setmask) { int error = afp->af_exec(ctx, afp->af_aifaddr, afp->af_addreq); if (error != 0) Perrorc("ioctl (SIOCAIFADDR)", error); } } int ifconfig_ioctl(if_ctx *orig_ctx, int iscreate, const struct afswtch *uafp) { const struct afswtch *afp, *nafp; const struct cmd *p; struct callback *cb; int s; int argc = orig_ctx->args->argc; char *const *argv = orig_ctx->args->argv; struct ifconfig_context _ctx = { .args = orig_ctx->args, .io_ss = orig_ctx->io_ss, .ifname = orig_ctx->ifname, }; struct ifconfig_context *ctx = &_ctx; strlcpy(ifr.ifr_name, ctx->ifname, 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); ctx->io_s = s; ctx->afp = afp; 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(ctx, 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(ctx, argv[1], 0); argc--, argv++; } else if (p->c_parameter == OPTARG && p->c_u.c_func) { p->c_u.c_func(ctx, argv[1], 0); 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(ctx, argv[1], argv[2]); argc -= 2, argv += 2; } else if (p->c_parameter == SPARAM && p->c_u.c_func3) { p->c_u.c_func3(ctx, *argv, p->c_sparameter); } else if (p->c_u.c_func) p->c_u.c_func(ctx, *argv, p->c_parameter); argc--, argv++; } /* * Do any post argument processing required by the address family. */ if (afp->af_postproc != NULL) afp->af_postproc(ctx, newaddr, getifflags(ctx->ifname, s, true)); /* * Do deferred callbacks registered while processing * command-line arguments. */ for (cb = callbacks; cb != NULL; cb = cb->cb_next) cb->cb_func(ctx, cb->cb_arg); /* * Do deferred operations. */ if (clearaddr) delifaddr(ctx, afp); if (newaddr) addifaddr(ctx, afp); close(s); return(0); } static void setifaddr(if_ctx *ctx, const char *addr, int param) { const struct afswtch *afp = ctx->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(if_ctx *ctx, const char *src, const char *dst) { const struct afswtch *afp = ctx->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(ctx, srcres, dstres); freeaddrinfo(srcres); freeaddrinfo(dstres); } static void deletetunnel(if_ctx *ctx, const char *vname, int param) { if (ioctl(ctx->io_s, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } #ifdef JAIL static void setifvnet(if_ctx *ctx, const char *jname, int dummy __unused) { 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(ctx->io_s, SIOCSIFVNET, &my_ifr) < 0) err(1, "SIOCSIFVNET"); } static void setifrvnet(if_ctx *ctx, const char *jname, int dummy __unused) { 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(ctx->io_s, SIOCSIFRVNET, &my_ifr) < 0) err(1, "SIOCSIFRVNET(%d, %s)", my_ifr.ifr_jid, my_ifr.ifr_name); } #endif static void setifnetmask(if_ctx *ctx, const char *addr, int dummy __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) { setmask++; afp->af_getaddr(addr, MASK); } } static void setifbroadaddr(if_ctx *ctx, const char *addr, int dummy __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) afp->af_getaddr(addr, BRDADDR); } static void notealias(if_ctx *ctx, const char *addr, int param) { const struct afswtch *afp = ctx->afp; if (setaddr && doalias == 0 && param < 0) { if (afp->af_copyaddr != NULL) afp->af_copyaddr(ctx, RIDADDR, ADDR); } doalias = param; if (param < 0) { clearaddr = 1; newaddr = 0; } else clearaddr = 0; } static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused) { const struct afswtch *afp = ctx->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(if_ctx *ctx, const char *vname, int value) { struct ifreq my_ifr; int flags; flags = getifflags(ctx->ifname, ctx->io_s, false); if (value < 0) { value = -value; flags &= ~value; } else flags |= value; memset(&my_ifr, 0, sizeof(my_ifr)); strlcpy(my_ifr.ifr_name, ctx->ifname, sizeof(my_ifr.ifr_name)); my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(ctx->io_s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } void setifcap(if_ctx *ctx, const char *vname, int value) { int flags; if (ioctl(ctx->io_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(ctx->io_s, SIOCSIFCAP, (caddr_t)&ifr) < 0) Perror(vname); } void setifcapnv(if_ctx *ctx, const char *vname, const char *arg) { nvlist_t *nvcap; void *buf; char *marg, *mopt; size_t nvbuflen; bool neg; if (ioctl(ctx->io_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(ctx->io_s, SIOCSIFCAPNV, (caddr_t)&ifr) < 0) Perror(vname); free(buf); nvlist_destroy(nvcap); free(marg); } static void setifmetric(if_ctx *ctx, const char *val, int dummy __unused) { strlcpy(ifr.ifr_name, ctx->ifname, sizeof (ifr.ifr_name)); ifr.ifr_metric = atoi(val); if (ioctl(ctx->io_s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) err(1, "ioctl SIOCSIFMETRIC (set metric)"); } static void setifmtu(if_ctx *ctx, const char *val, int dummy __unused) { strlcpy(ifr.ifr_name, ctx->ifname, sizeof (ifr.ifr_name)); ifr.ifr_mtu = atoi(val); if (ioctl(ctx->io_s, SIOCSIFMTU, (caddr_t)&ifr) < 0) err(1, "ioctl SIOCSIFMTU (set mtu)"); } static void setifpcp(if_ctx *ctx, const char *val, int arg __unused) { 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(ctx->io_s, SIOCSLANPCP, (caddr_t)&ifr) == -1) err(1, "SIOCSLANPCP"); } static void disableifpcp(if_ctx *ctx, const char *val, int arg __unused) { ifr.ifr_lan_pcp = IFNET_PCP_NONE; if (ioctl(ctx->io_s, SIOCSLANPCP, (caddr_t)&ifr) == -1) err(1, "SIOCSLANPCP"); } static void setifname(if_ctx *ctx, const char *val, int dummy __unused) { char *newname; strlcpy(ifr.ifr_name, ctx->ifname, sizeof(ifr.ifr_name)); newname = strdup(val); if (newname == NULL) err(1, "no memory to set ifname"); ifr.ifr_data = newname; if (ioctl(ctx->io_s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { free(newname); err(1, "ioctl SIOCSIFNAME (set name)"); } ifname_update(ctx, newname); free(newname); } static void setifdescr(if_ctx *ctx, const char *val, int dummy __unused) { char *newdescr; strlcpy(ifr.ifr_name, ctx->ifname, 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(ctx->io_s, SIOCSIFDESCR, (caddr_t)&ifr) < 0) err(1, "ioctl SIOCSIFDESCR (set descr)"); free(newdescr); } static void unsetifdescr(if_ctx *ctx, const char *val, int value) { setifdescr(ctx, "", 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(if_ctx *ctx) { struct ifstat ifs; strlcpy(ifs.ifs_name, ctx->ifname, sizeof ifs.ifs_name); if (ioctl_ctx(ctx, 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(if_ctx *ctx, const struct sockaddr_dl *sdl, struct ifaddrs *ifa) { struct ifaddrs *ift; int s, old_s; struct ifconfig_args *args = ctx->args; bool allfamilies = args->afp == NULL; char *ifname = ifa->ifa_name; 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, ifname, 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); old_s = ctx->io_s; ctx->io_s = s; printf("%s: ", ifname); printb("flags", ifa->ifa_flags, IFFBITS); print_metric(s); print_mtu(s); putchar('\n'); print_description(s); print_ifcap(args, s); tunnel_status(ctx); 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(ctx, ift); } else if (args->afp->af_af == ift->ifa_addr->sa_family) args->afp->af_status(ctx, 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(ctx); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(ctx); print_ifstatus(ctx); if (args->verbose > 0) sfp_status(ctx); close(s); ctx->io_s = old_s; return; } #endif void tunnel_status(if_ctx *ctx) { af_all_tunnel_status(ctx); } 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: 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 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 (unsigned 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 a65969ab4c56..9747c125746c 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -1,317 +1,316 @@ /*- * 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)) #ifdef WITHOUT_NETLINK #define __netlink_used __unused #define __netlink_unused #else #define __netlink_used #define __netlink_unused __unused #endif struct afswtch; struct cmd; struct snl_state; struct ifconfig_args; struct ifconfig_context { struct ifconfig_args *args; const struct afswtch *afp; int io_s; /* fd to use for ioctl() */ struct snl_state *io_ss; /* NETLINK_ROUTE socket */ const char *ifname; /* Current interface name */ char _ifname_storage_ioctl[IFNAMSIZ]; }; typedef struct ifconfig_context if_ctx; typedef void c_func(if_ctx *ctx, const char *cmd, int arg); typedef void c_func2(if_ctx *ctx, const char *arg1, const char *arg2); typedef void c_func3(if_ctx *ctx, const char *cmd, const char *arg); 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(if_ctx *, void *); void callback_register(callback_func *, void *); /* * Macros for initializing command handlers. */ #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, \ } #define ioctl_ctx(ctx, _req, ...) ioctl((ctx)->io_s, _req, ## __VA_ARGS__) struct ifaddrs; struct addrinfo; enum { RIDADDR = 0, ADDR = 1, MASK = 2, DSTADDR = 3, #ifdef WITHOUT_NETLINK BRDADDR = 3, #else BRDADDR = 4, #endif }; struct snl_parsed_addr; struct snl_parsed_link; typedef struct snl_parsed_link if_link_t; typedef struct snl_parsed_addr if_addr_t; typedef void af_setvhid_f(int vhid); typedef void af_status_nl_f(if_ctx *ctx, if_link_t *link, if_addr_t *ifa); typedef void af_status_f(if_ctx *ctx, const struct ifaddrs *); typedef void af_other_status_f(if_ctx *ctx); typedef void af_postproc_f(if_ctx *ctx, int newaddr, int ifflags); typedef int af_exec_f(if_ctx *ctx, unsigned long action, void *data); typedef void af_copyaddr_f(if_ctx *ctx, int to, int from); typedef void af_status_tunnel_f(if_ctx *ctx); typedef void af_settunnel_f(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres); 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; #else af_status_f *af_status; #endif af_other_status_f *af_other_status; void (*af_getaddr)(const char *, int); af_copyaddr_f *af_copyaddr; /* Copy address between ADDR */ /* parse prefix method (IPv6) */ void (*af_getprefix)(const char *, int); af_postproc_f *af_postproc; 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 */ af_status_tunnel_f *af_status_tunnel; af_settunnel_f *af_settunnel; }; void af_register(struct afswtch *); int af_exec_ioctl(if_ctx *ctx, unsigned long 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 */ const char *jail_name; /* Jail name or jail id specified */ }; 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 int allmedia; -extern int newaddr; extern int exit_code; extern char *f_inet, *f_inet6, *f_ether, *f_addr; void setifcap(if_ctx *ctx, const char *, int value); void setifcapnv(if_ctx *ctx, const char *vname, const char *arg); 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(if_ctx *, struct ifreq *); void clone_setdefcallback_prefix(const char *, clone_callback_func *); void clone_setdefcallback_filter(clone_match_func *, clone_callback_func *); void sfp_status(if_ctx *ctx); struct sockaddr_dl; bool match_ether(const struct sockaddr_dl *sdl); bool match_if_flags(struct ifconfig_args *args, int if_flags); int ifconfig_ioctl(if_ctx *ctx, 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(if_ctx *ctx); struct afswtch *af_getbyfamily(int af); void af_other_status(if_ctx *ctx); void print_ifstatus(if_ctx *ctx); void print_metric(int s); /* Netlink-related functions */ void list_interfaces_nl(struct ifconfig_args *args); int ifconfig_nl(if_ctx *ctx, int iscreate, const struct afswtch *uafp); uint32_t if_nametoindex_nl(struct snl_state *ss, const char *ifname); /* * XXX expose this so modules that need to know of any pending * operations on ifmedia can avoid cmd line ordering confusion. */ struct ifmediareq *ifmedia_getstate(if_ctx *ctx); void print_vhid(const struct ifaddrs *, const char *); void ifcreate_ioctl(if_ctx *ctx, struct ifreq *ifr); /* Helpers */ struct sockaddr_in; struct sockaddr_in6; struct sockaddr; static inline struct sockaddr_in6 * satosin6(struct sockaddr *sa) { return ((struct sockaddr_in6 *)(void *)sa); } static inline struct sockaddr_in * satosin(struct sockaddr *sa) { return ((struct sockaddr_in *)(void *)sa); }