/* * Copyright (c) 2009-2019 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * 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. * 4. 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. */ #include #ifndef lint __unused static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #include #include #include #include #include #ifndef __APPLE__ #include #include #endif #include #include #include #include #include #include #include #include #include /* IP */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #ifdef __APPLE__ #include #endif /* * Since "struct ifreq" is composed of various union members, callers * should pay special attention to interprete the value. * (.e.g. little/big endian difference in the structure.) */ struct ifreq ifr; char name[IFNAMSIZ]; int setaddr; int setmask; int doalias; int clearaddr; int newaddr = 1; int noload; int all; int bond_details = 0; int supmedia = 0; #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) int verbose = 1; int showrtref = 1; #else /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ int verbose = 0; int showrtref = 0; #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ int printkeys = 0; /* Print keying material for interfaces. */ static int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp); static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, struct ifaddrs *ifa); static char *bytes_to_str(unsigned long long bytes); static char *bps_to_str(unsigned long long rate); static char *ns_to_str(unsigned long long nsec); static void tunnel_status(int s); static void clat46_addr(int s, char *name); static void nat64_status(int s, char *name); static void usage(void); static char *sched2str(unsigned int s); static char *tl2str(unsigned int s); static char *ift2str(unsigned int t, unsigned int f, unsigned int sf); static char *iffunct2str(u_int32_t functional_type); static struct afswtch *af_getbyname(const char *name); static struct afswtch *af_getbyfamily(int af); static void af_other_status(int); static struct option *opts = NULL; 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 %sinterface address_family [address [dest_address]]\n" " [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); } int main(int argc, char *argv[]) { int c, namesonly, downonly, uponly; const struct afswtch *afp = NULL; int ifindex; struct ifaddrs *ifap, *ifa; struct ifreq paifr; const struct sockaddr_dl *sdl; char options[1024], *cp; const char *ifname; struct option *p; size_t iflen; all = downonly = uponly = namesonly = noload = 0; /* Parse leading line options */ #ifndef __APPLE__ strlcpy(options, "adklmnuv", sizeof(options)); #else strlcpy(options, "abdlmruv", sizeof(options)); #endif 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 */ all++; break; case 'b': /* bond detailed output */ bond_details++; break; case 'd': /* restrict scan to "down" interfaces */ downonly++; break; #ifndef __APPLE__ case 'k': printkeys++; break; #endif case 'l': /* scan interface names only */ namesonly++; break; case 'm': /* show media choices in status */ supmedia = 1; break; #ifndef __APPLE__ case 'n': /* suppress module loading */ noload++; break; #endif case 'r': showrtref++; break; case 'u': /* restrict scan to "up" interfaces */ uponly++; break; case 'v': verbose++; break; 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 -q or -m or -b */ if (namesonly && (all || supmedia || bond_details)) usage(); /* nonsense.. */ if (uponly && downonly) usage(); /* no arguments is equivalent to '-a' */ if (!namesonly && argc < 1) all = 1; /* -a and -l allow an address family arg to limit the output */ if (all || namesonly) { if (argc > 1) usage(); ifname = NULL; if (argc == 1) { afp = af_getbyname(*argv); if (afp == NULL) usage(); if (afp->af_name != NULL) argc--, argv++; /* leave with afp non-zero */ } } else { /* not listing, need an argument */ if (argc < 1) usage(); ifname = *argv; argc--, argv++; #ifdef notdef /* check and maybe load support for this interface */ ifmaybeload(ifname); #endif ifindex = if_nametoindex(ifname); if (ifindex == 0) { /* * NOTE: We must special-case the `create' command * right here as we would otherwise fail when trying * to find the interface. */ if (argc > 0 && (strcmp(argv[0], "create") == 0 || strcmp(argv[0], "plumb") == 0)) { iflen = strlcpy(name, ifname, sizeof(name)); if (iflen >= sizeof(name)) errx(1, "%s: cloning name too long", ifname); ifconfig(argc, argv, 1, NULL); exit(0); } errx(1, "interface %s does not exist", ifname); } } /* Check for address family */ if (argc > 0) { afp = af_getbyname(*argv); if (afp != NULL) argc--, argv++; } if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); cp = NULL; ifindex = 0; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { memset(&paifr, 0, sizeof(paifr)); 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 (ifname != NULL && strcmp(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) 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 (downonly && (ifa->ifa_flags & IFF_UP) != 0) continue; if (uponly && (ifa->ifa_flags & IFF_UP) == 0) continue; ifindex++; /* * Are we just listing the interfaces? */ if (namesonly) { if (ifindex > 1) printf(" "); fputs(name, stdout); continue; } if (argc > 0) ifconfig(argc, argv, 0, afp); else status(afp, sdl, ifa); } if (namesonly) printf("\n"); freeifaddrs(ifap); exit(0); } 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; } static 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; } static 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) { #define N(a) (sizeof(a)/sizeof(a[0])) const struct cmd *p; for (p = cmds; p != NULL; p = p->c_next) if (strcmp(name, p->c_name) == 0) return p; return NULL; #undef N } 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); static int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp) { const struct afswtch *nafp; struct callback *cb; int ret, s; strlcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); top: if (afp == NULL) afp = af_getbyname("inet"); ifr.ifr_addr.sa_family = afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? AF_INET : afp->af_af; if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); while (argc > 0) { const struct cmd *p; p = cmd_lookup(*argv); 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_u.c_func || p->c_u.c_func2) { if (iscreate && !p->c_iscloneop) { /* * 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; } } } if (p->c_parameter == NEXTARG) { 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(argv[1], 0, s, afp); if (argv[1] != NULL) argc--, argv++; } else if (p->c_parameter == NEXTARG2) { 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 == VAARGS) { ret = p->c_u.c_funcv(argc - 1, argv + 1, s, afp); if (ret < 0) errx(1, "'%s' command error", p->c_name); argc -= ret, argv += ret; } else { 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); /* * 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. */ if (clearaddr) { if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { warnx("interface %s cannot change %s addresses!", name, afp->af_name); clearaddr = 0; } } if (clearaddr) { strlcpy(afp->af_ridreq, name, sizeof ifr.ifr_name); ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); if (ret < 0) { if (errno == EADDRNOTAVAIL && (doalias >= 0)) { /* means no previous address for interface */ } else Perror("ioctl (SIOCDIFADDR)"); } } if (newaddr) { if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { warnx("interface %s cannot change %s addresses!", name, afp->af_name); newaddr = 0; } } if (newaddr && (setaddr || setmask)) { strlcpy(afp->af_addreq, name, sizeof ifr.ifr_name); if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) Perror("ioctl (SIOCAIFADDR)"); } 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"); } 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); } static void setifipdst(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { const struct afswtch *inet; inet = af_getbyname("inet"); if (inet == NULL) return; inet->af_getaddr(addr, DSTADDR); clearaddr = 0; newaddr = 0; } 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); } /* * 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; bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq)); if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } strlcpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name)); flags = my_ifr.ifr_flags; if (value < 0) { value = -value; flags &= ~value; } else flags |= value; my_ifr.ifr_flags = flags & 0xffff; 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; ifr.ifr_reqcap = flags; if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) Perror(vname); } 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) warn("ioctl (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) warn("ioctl (set mtu)"); } #ifndef __APPLE__ static void setifname(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *newname; newname = strdup(val); if (newname == NULL) { warn("no memory to set ifname"); return; } ifr.ifr_data = newname; if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { warn("ioctl (set name)"); free(newname); return; } strlcpy(name, newname, sizeof(name)); free(newname); } #endif static void setrouter(const char *vname, int value, int s, const struct afswtch *afp) { if (afp->af_setrouter == NULL) { warn("address family %s does not support router mode", afp->af_name); return; } afp->af_setrouter(s, value); } static int routermode(int argc, char *const *argv, int s, const struct afswtch *afp) { return (*afp->af_routermode)(s, argc, argv); } static void setifdesc(const char *val, int dummy __unused, int s, const struct afswtch *afp) { struct if_descreq ifdr; bzero(&ifdr, sizeof (ifdr)); strlcpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); ifdr.ifdr_len = strlen(val); strlcpy((char *)ifdr.ifdr_desc, val, sizeof (ifdr.ifdr_desc)); if (ioctl(s, SIOCSIFDESC, (caddr_t)&ifdr) < 0) { warn("ioctl (set desc)"); } } static void settbr(const char *val, int dummy __unused, int s, const struct afswtch *afp) { struct if_linkparamsreq iflpr; long double bps; u_int64_t rate; u_int32_t percent = 0; char *cp; errno = 0; bzero(&iflpr, sizeof (iflpr)); strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); bps = strtold(val, &cp); if (val == cp || errno != 0) { warn("Invalid value '%s'", val); return; } rate = (u_int64_t)bps; if (cp != NULL) { if (!strcmp(cp, "b") || !strcmp(cp, "bps")) { ; /* nothing */ } else if (!strcmp(cp, "Kb") || !strcmp(cp, "Kbps")) { rate *= 1000; } else if (!strcmp(cp, "Mb") || !strcmp(cp, "Mbps")) { rate *= 1000 * 1000; } else if (!strcmp(cp, "Gb") || !strcmp(cp, "Gbps")) { rate *= 1000 * 1000 * 1000; } else if (!strcmp(cp, "%")) { percent = rate; if (percent == 0 || percent > 100) { printf("Value out of range '%s'", val); return; } } else if (*cp != '\0') { printf("Unknown unit '%s'", cp); return; } } iflpr.iflpr_output_tbr_rate = rate; iflpr.iflpr_output_tbr_percent = percent; if (ioctl(s, SIOCSIFLINKPARAMS, &iflpr) < 0 && errno != ENOENT && errno != ENXIO && errno != ENODEV) { warn("ioctl (set link params)"); } else if (errno == ENXIO) { printf("TBR cannot be set on %s\n", name); } else if (errno == 0 && rate == 0) { printf("%s: TBR is now disabled\n", name); } else if (errno == ENODEV) { printf("%s: requires absolute TBR rate\n", name); } else if (percent != 0) { printf("%s: TBR rate set to %u%% of effective link rate\n", name, percent); } else { printf("%s: TBR rate set to %s\n", name, bps_to_str(rate)); } } static int get_int64(uint64_t *i, char const *s) { char *cp; *i = strtol(s, &cp, 10); if (cp == s || errno != 0) { return (-1); } return (0); } static int get_int32(uint32_t *i, char const *s) { char *cp; *i = strtol(s, &cp, 10); if (cp == s || errno != 0) { return (-1); } return (0); } static int get_percent(double *d, const char *s) { char *cp; *d = strtod(s, &cp) / (double)100; if (*d == HUGE_VALF || *d == HUGE_VALL) { return (-1); } if (*d == 0.0 || (*cp != '\0' && strcmp(cp, "%") != 0)) { return (-1); } return (0); } static int get_percent_fixed_point(uint32_t *i, const char *s) { double p; if (get_percent(&p, s) != 0){ return (-1); } *i = p * IF_NETEM_PARAMS_PSCALE; return (0); } static int netem_parse_args(struct if_netem_params *p, int argc, char *const *argv) { int argc_saved = argc; uint64_t bandwitdh = 0; uint32_t latency = 0, jitter = 0; uint32_t corruption = 0; uint32_t duplication = 0; uint32_t loss_p_gr_gl = 0, loss_p_gr_bl = 0, loss_p_bl_br = 0, loss_p_bl_gr = 0, loss_p_br_bl = 0; uint32_t reordering = 0; bzero(p, sizeof (*p)); /* take out "input"/"output" */ argc--, argv++; for ( ; argc > 0; ) { if (strcmp(*argv, "bandwidth") == 0) { argc--, argv++; if (argc <= 0 || get_int64(&bandwitdh, *argv) != 0) { err(1, "Invalid value '%s'", *argv); } argc--, argv++; } else if (strcmp(*argv, "corruption") == 0) { argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&corruption, *argv) != 0) { err(1, "Invalid value '%s'", *argv); } argc--, argv++; } else if (strcmp(*argv, "delay") == 0) { argc--, argv++; if (argc <= 0 || get_int32(&latency, *argv) != 0) { err(1, "Invalid value '%s'", *argv); } argc--, argv++; if (argc > 0 && get_int32(&jitter, *argv) == 0) { argc--, argv++; } } else if (strcmp(*argv, "duplication") == 0) { argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&duplication, *argv) != 0) { err(1, "Invalid value '%s'", *argv); return (-1); } argc--, argv++; } else if (strcmp(*argv, "loss") == 0) { argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_gl, *argv) != 0) { err(1, "Invalid value '%s'", *argv); } /* we may have all 5 probs, use naive model if not */ argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_bl, *argv) != 0) { continue; } /* if more than p_gr_gl, then should have all probs */ argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_br, *argv) != 0) { err(1, "Invalid value '%s' for p_bl_br", *argv); } argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_gr, *argv) != 0) { err(1, "Invalid value '%s' for p_bl_gr", *argv); } argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&loss_p_br_bl, *argv) != 0) { err(1, "Invalid value '%s' for p_br_bl", *argv); } argc--, argv++; } else if (strcmp(*argv, "reordering") == 0) { argc--, argv++; if (argc <= 0 || get_percent_fixed_point(&reordering, *argv) != 0) { err(1, "Invalid value '%s'", *argv); } argc--, argv++; } else { return (-1); } } if (corruption > IF_NETEM_PARAMS_PSCALE) { err(1, "corruption percentage > 100%%"); } if (duplication > IF_NETEM_PARAMS_PSCALE) { err(1, "duplication percentage > 100%%"); } if (duplication > 0 && latency == 0) { /* we need to insert dup'ed packet with latency */ err(1, "duplication needs latency param"); } if (latency > 1000) { err(1, "latency %dms too big (> 1 sec)", latency); } if (jitter * 3 > latency) { err(1, "jitter %dms too big (latency %dms)", jitter, latency); } /* if gr_gl == 0 (no loss), other prob should all be zero */ if (loss_p_gr_gl == 0 && (loss_p_gr_bl != 0 || loss_p_bl_br != 0 || loss_p_bl_gr != 0 || loss_p_br_bl != 0)) { err(1, "loss params not all zero when gr_gl is zero"); } /* check state machine transition prob integrity */ if (loss_p_gr_gl > IF_NETEM_PARAMS_PSCALE || /* gr_gl = IF_NETEM_PARAMS_PSCALE for total loss */ loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE || loss_p_bl_br > IF_NETEM_PARAMS_PSCALE || loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE || loss_p_br_bl > IF_NETEM_PARAMS_PSCALE || loss_p_gr_gl + loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE || loss_p_bl_br + loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE) { err(1, "loss params too big"); } if (reordering > IF_NETEM_PARAMS_PSCALE) { err(1, "reordering percentage > 100%%"); } p->ifnetem_bandwidth_bps = bandwitdh; p->ifnetem_latency_ms = latency; p->ifnetem_jitter_ms = jitter; p->ifnetem_corruption_p = corruption; p->ifnetem_duplication_p = duplication; p->ifnetem_loss_p_gr_gl = loss_p_gr_gl; p->ifnetem_loss_p_gr_bl = loss_p_gr_bl; p->ifnetem_loss_p_bl_br = loss_p_bl_br; p->ifnetem_loss_p_bl_gr = loss_p_bl_gr; p->ifnetem_loss_p_br_bl = loss_p_br_bl; p->ifnetem_reordering_p = reordering; return (argc_saved - argc); } void print_netem_params(struct if_netem_params *p, const char *desc) { struct if_netem_params zero_params; double pscale = IF_NETEM_PARAMS_PSCALE / 100; bzero(&zero_params, sizeof (zero_params)); if (memcmp(p, &zero_params, sizeof (zero_params)) == 0) { printf("%s NetEm Disabled\n\n", desc); } else { printf( "%s NetEm Parameters\n" "\tbandwidth rate %llubps\n" "\tdelay latency %dms\n" "\t jitter %dms\n", desc, p->ifnetem_bandwidth_bps, p->ifnetem_latency_ms, p->ifnetem_jitter_ms); if (p->ifnetem_loss_p_gr_bl == 0 && p->ifnetem_loss_p_bl_br == 0 && p->ifnetem_loss_p_bl_gr == 0 && p->ifnetem_loss_p_br_bl == 0) { printf( "\tloss %.3f%%\n", (double) p->ifnetem_loss_p_gr_gl / pscale); } else { printf( "\tloss GAP_RECV -> GAP_LOSS %.3f%%\n" "\t GAP_RECV -> BURST_LOSS %.3f%%\n" "\t BURST_LOSS -> BURST_RECV %.3f%%\n" "\t BURST_LOSS -> GAP_RECV %.3f%%\n" "\t BURST_RECV -> BURST_LOSS %.3f%%\n", (double) p->ifnetem_loss_p_gr_gl / pscale, (double) p->ifnetem_loss_p_gr_bl / pscale, (double) p->ifnetem_loss_p_bl_br / pscale, (double) p->ifnetem_loss_p_bl_gr / pscale, (double) p->ifnetem_loss_p_br_bl / pscale); } printf( "\tcorruption %.3f%%\n" "\treordering %.3f%%\n\n", (double) p->ifnetem_corruption_p / pscale, (double) p->ifnetem_reordering_p / pscale); } } static int setnetem(int argc, char *const *argv, int s, const struct afswtch *afp) { struct if_linkparamsreq iflpr; struct if_netem_params input_params, output_params; int ret = 0, error = 0; bzero(&iflpr, sizeof (iflpr)); bzero(&input_params, sizeof (input_params)); bzero(&output_params, sizeof (output_params)); if (argc > 1) { if (strcmp(argv[0], "input") == 0) { ret = netem_parse_args(&input_params, argc, argv); } else if (strcmp(argv[0], "output") == 0) { ret = netem_parse_args(&output_params, argc, argv); } else if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) { goto bad_args; } else { fprintf(stderr, "uknown option %s\n", argv[0]); goto bad_args; } if (ret < 0) { goto bad_args; } } errno = 0; strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); error = ioctl(s, SIOCGIFLINKPARAMS, &iflpr); if (error < 0) { warn("ioctl (get link params)"); } if (argc == 0) { print_netem_params(&iflpr.iflpr_input_netem, "Input"); print_netem_params(&iflpr.iflpr_output_netem, "Output"); return (0); } else if (argc == 1) { if (strcmp(argv[0], "input") == 0) { bzero(&iflpr.iflpr_input_netem, sizeof (iflpr.iflpr_input_netem)); } else if (strcmp(argv[0], "output") == 0) { bzero(&iflpr.iflpr_output_netem, sizeof (iflpr.iflpr_output_netem)); } else { fprintf(stderr, "uknown option %s\n", argv[0]); goto bad_args; } printf("%s: netem is now disabled for %s\n", name, argv[0]); ret = 1; } else { if (strcmp(argv[0], "input") == 0) { iflpr.iflpr_input_netem = input_params; } else if (strcmp(argv[0], "output") == 0) { iflpr.iflpr_output_netem = output_params; } } error = ioctl(s, SIOCSIFLINKPARAMS, &iflpr); if (error < 0 && errno != ENOENT && errno != ENXIO && errno != ENODEV) { warn("ioctl (set link params)"); } else if (errno == ENXIO) { printf("netem cannot be set on %s\n", name); } else { printf("%s: netem configured\n", name); } return (ret); bad_args: fprintf(stderr, "Usage:\n" "\tTo enable/set netem params\n" "\t\tnetem \n" "\t\t [ bandwidth BIT_PER_SEC ]\n" "\t\t [ delay DELAY_MSEC [ JITTER_MSEC ] ]\n" "\t\t [ loss PERCENTAGE ]\n" "\t\t [ duplication PERCENTAGE ]\n" "\t\t [ reordering PERCENTAGE ]\n\n" "\tTo disable netem\n" "\t\tnetem \n\n" "\tTo show current settings\n" "\t\tnetem\n\n"); return (-1); } static void setthrottle(const char *val, int dummy __unused, int s, const struct afswtch *afp) { struct if_throttlereq iftr; char *cp; errno = 0; bzero(&iftr, sizeof (iftr)); strlcpy(iftr.ifthr_name, name, sizeof (iftr.ifthr_name)); iftr.ifthr_level = strtold(val, &cp); if (val == cp || errno != 0) { warn("Invalid value '%s'", val); return; } if (ioctl(s, SIOCSIFTHROTTLE, &iftr) < 0 && errno != ENXIO) { warn("ioctl (set throttling level)"); } else if (errno == ENXIO) { printf("throttling level cannot be set on %s\n", name); } else { printf("%s: throttling level set to %d\n", name, iftr.ifthr_level); } } static void setdisableoutput(const char *val, int dummy __unused, int s, const struct afswtch *afp) { struct ifreq ifr; char *cp; errno = 0; bzero(&ifr, sizeof (ifr)); strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_ifru.ifru_disable_output = strtold(val, &cp); if (val == cp || errno != 0) { warn("Invalid value '%s'", val); return; } if (ioctl(s, SIOCSIFDISABLEOUTPUT, &ifr) < 0 && errno != ENXIO) { warn("ioctl set disable output"); } else if (errno == ENXIO) { printf("output thread can not be disabled on %s\n", name); } else { printf("output %s on %s\n", ((ifr.ifr_ifru.ifru_disable_output == 0) ? "enabled" : "disabled"), name); } } static void setlog(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *cp; errno = 0; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_log.ifl_level = strtold(val, &cp); if (val == cp || errno != 0) { warn("Invalid value '%s'", val); return; } ifr.ifr_log.ifl_flags = (IFRLOGF_DLIL|IFRLOGF_FAMILY|IFRLOGF_DRIVER| IFRLOGF_FIRMWARE); if (ioctl(s, SIOCSIFLOG, &ifr) < 0) warn("ioctl (set logging parameters)"); } void setcl2k(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_ifru.ifru_2kcl = value; if (ioctl(s, SIOCSIF2KCL, (caddr_t)&ifr) < 0) Perror(vname); } void setexpensive(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_ifru.ifru_expensive = value; if (ioctl(s, SIOCSIFEXPENSIVE, (caddr_t)&ifr) < 0) Perror(vname); } #ifdef SIOCSIFCONSTRAINED void setconstrained(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_ifru.ifru_constrained = value; if (ioctl(s, SIOCSIFCONSTRAINED, (caddr_t)&ifr) < 0) Perror(vname); } #endif static void setifmpklog(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_ifru.ifru_mpk_log = value; if (ioctl(s, SIOCSIFMPKLOG, (caddr_t)&ifr) < 0) Perror(vname); } void settimestamp(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (value == 0) { if (ioctl(s, SIOCSIFTIMESTAMPDISABLE, (caddr_t)&ifr) < 0) Perror(vname); } else { if (ioctl(s, SIOCSIFTIMESTAMPENABLE, (caddr_t)&ifr) < 0) Perror(vname); } } void setecnmode(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *cp; if (strcmp(val, "default") == 0) ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DEFAULT; else if (strcmp(val, "enable") == 0) ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_ENABLE; else if (strcmp(val, "disable") == 0) ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DISABLE; else { ifr.ifr_ifru.ifru_ecn_mode = strtold(val, &cp); if (val == cp || errno != 0) { warn("Invalid ECN mode value '%s'", val); return; } } strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCSECNMODE, (caddr_t)&ifr) < 0) Perror("ioctl(SIOCSECNMODE)"); } void setprobeconnectivity(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_ifru.ifru_probe_connectivity = value; if (ioctl(s, SIOCSIFPROBECONNECTIVITY, (caddr_t)&ifr) < 0) Perror(vname); } #if defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) void setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) { u_long ioc; #if (DEBUG | DEVELOPMENT) printf("%s(%s, %s)\n", __func__, cmd, arg); #endif /* (DEBUG | DEVELOPMENT) */ strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (strcmp(cmd, "mode") == 0) { ioc = SIOCSQOSMARKINGMODE; if (strcmp(arg, "fastlane") == 0) ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_FASTLANE; else if (strcmp(arg, "rfc4594") == 0) ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_RFC4594; else if (strcasecmp(arg, "none") == 0 || strcasecmp(arg, "off") == 0) ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_MODE_NONE; else err(EX_USAGE, "bad value for qosmarking mode: %s", arg); } else if (strcmp(cmd, "enabled") == 0) { ioc = SIOCSQOSMARKINGENABLED; if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) ifr.ifr_qosmarking_enabled = 1; else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) ifr.ifr_qosmarking_enabled = 0; else err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); } else { err(EX_USAGE, "qosmarking takes mode or enabled"); } if (ioctl(s, ioc, (caddr_t)&ifr) < 0) err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); } void setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); warnx("### fastlane is obsolete, use qosmarking ###"); if (strcmp(cmd, "capable") == 0) { if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) setqosmarking("mode", "fastlane", s, afp); else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) setqosmarking("mode", "off", s, afp); else err(EX_USAGE, "bad value for fastlane %s", cmd); } else if (strcmp(cmd, "enable") == 0) { if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) setqosmarking("enabled", "1", s, afp); else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) setqosmarking("enabled", "0", s, afp); else err(EX_USAGE, "bad value for fastlane %s", cmd); } else { err(EX_USAGE, "fastlane takes capable or enable"); } } #else /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ void setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) { int value; u_long ioc; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (strcmp(cmd, "capable") == 0) ioc = SIOCSFASTLANECAPABLE; else if (strcmp(cmd, "enable") == 0) ioc = SIOCSFASTLEENABLED; else err(EX_USAGE, "fastlane takes capable or enabled"); if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) value = 1; else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) value = 0; else err(EX_USAGE, "bad value for fastlane %s", cmd); if (ioc == SIOCSFASTLANECAPABLE) ifr.ifr_fastlane_capable = value; else ifr.ifr_fastlane_enabled = value; if (ioctl(s, ioc, (caddr_t)&ifr) < 0) err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); } void setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) { if (strcmp(cmd, "mode") == 0) { if (strcmp(arg, "fastlane") == 0) setfastlane("capable", "on", s, afp); else if (strcmp(arg, "none") == 0) setfastlane("capable", "off", s, afp); else err(EX_USAGE, "bad value for qosmarking mode: %s", arg); } else if (strcmp(cmd, "enabled") == 0) { if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) setfastlane("enable", "on", s, afp); else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) setfastlane("enable", "off", s, afp); else err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); } else { err(EX_USAGE, "qosmarking takes mode or enabled"); } } #endif /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ void setlowpowermode(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_low_power_mode = !!value; if (ioctl(s, SIOCSIFLOWPOWER, (caddr_t)&ifr) < 0) Perror(vname); } struct str2num { const char *str; uint32_t num; }; static struct str2num subfamily_str2num[] = { { .str = "any", .num = IFRTYPE_SUBFAMILY_ANY }, { .str = "USB", .num = IFRTYPE_SUBFAMILY_USB }, { .str = "Bluetooth", .num = IFRTYPE_SUBFAMILY_BLUETOOTH }, { .str = "Wi-Fi", .num = IFRTYPE_SUBFAMILY_WIFI }, { .str = "wifi", .num = IFRTYPE_SUBFAMILY_WIFI }, { .str = "Thunderbolt", .num = IFRTYPE_SUBFAMILY_THUNDERBOLT }, { .str = "reserverd", .num = IFRTYPE_SUBFAMILY_RESERVED }, { .str = "intcoproc", .num = IFRTYPE_SUBFAMILY_INTCOPROC }, { .str = "QuickRelay", .num = IFRTYPE_SUBFAMILY_QUICKRELAY }, { .str = "Default", .num = IFRTYPE_SUBFAMILY_DEFAULT }, { .str = NULL, .num = 0 }, }; static uint32_t get_num_from_str(struct str2num* str2nums, const char *str) { struct str2num *str2num = str2nums; while (str2num != NULL && str2num->str != NULL) { if (strcasecmp(str2num->str, str) == 0) { return str2num->num; } str2num++; } return 0; } static void setifsubfamily(const char *val, int dummy __unused, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); char *endptr; uint32_t subfamily = strtoul(val, &endptr, 0); if (*endptr != 0) { subfamily = get_num_from_str(subfamily_str2num, val); if (subfamily == 0) { return; } } ifr.ifr_type.ift_subfamily = subfamily; if (ioctl(s, SIOCSIFSUBFAMILY, (caddr_t)&ifr) < 0) warn("ioctl(SIOCSIFSUBFAMILY)"); } void setifavailability(const char *vname, int value, int s, const struct afswtch *afp) { strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); ifr.ifr_interface_state.valid_bitmask = IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID; if (value == 0) { ifr.ifr_interface_state.interface_availability = IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE; } else { ifr.ifr_interface_state.interface_availability = IF_INTERFACE_STATE_INTERFACE_AVAILABLE; } if (ioctl(s, SIOCSIFINTERFACESTATE, (caddr_t)&ifr) < 0) warn("ioctl(SIOCSIFINTERFACESTATE)"); } static void show_routermode(int s) { struct afswtch *afp; afp = af_getbyname("inet"); if (afp != NULL) { (*afp->af_routermode)(s, 0, NULL); } } static void show_routermode6(void) { struct afswtch *afp; static int s = -1; afp = af_getbyname("inet6"); if (afp != NULL) { if (s < 0) { s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return; } } (*afp->af_routermode)(s, 0, NULL); } } #define IFFBITS \ "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ "\20MULTICAST" #define IFEFBITS \ "\020\1AUTOCONFIGURING\4PROBE_CONNECTIVITY\5FASTLN_CAP\6IPV6_DISABLED\7ACCEPT_RTADV\10TXSTART\11RXPOLL" \ "\12VLAN\13BOND\14ARPLL\15CLAT46\16NOAUTOIPV6LL\17EXPENSIVE\20ROUTER4" \ "\22LOCALNET_PRIVATE\23ND6ALT\24RESTRICTED_RECV\25AWDL\26NOACKPRI" \ "\27AWDL_RESTRICTED\30CL2K\31ECN_ENABLE\32ECN_DISABLE\33CHANNEL_DRV\34CA" \ "\35SENDLIST\36DIRECTLINK\37FASTLN_ON\40UPDOWNCHANGE" #define IFXFBITS \ "\020\1WOL\2TIMESTAMP\3NOAUTONX\4LEGACY\5TXLOWINET\6RXLOWINET\7ALLOCKPI" \ "\10LOWPOWER\11MPKLOG\12CONSTRAINED" #define IFCAPBITS \ "\020\1RXCSUM\2TXCSUM\3VLAN_MTU\4VLAN_HWTAGGING\5JUMBO_MTU" \ "\6TSO4\7TSO6\10LRO\11AV\12TXSTATUS\13CHANNEL_IO\14HW_TIMESTAMP\15SW_TIMESTAMP" \ "\16PARTIAL_CSUM\17ZEROINVERT_CSUM" #define IFRLOGF_BITS \ "\020\1DLIL\21FAMILY\31DRIVER\35FIRMWARE" /* * Print the status of the interface. If an address family was * specified, show only it; otherwise, show them all. */ static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, struct ifaddrs *ifa) { struct ifaddrs *ift; int allfamilies, s; struct ifstat ifs; struct if_descreq ifdr; struct if_linkparamsreq iflpr; int mib[6]; struct ifmibdata_supplemental ifmsupp; size_t miblen = sizeof(struct ifmibdata_supplemental); u_int64_t eflags = 0; u_int64_t xflags = 0; int curcap = 0; if (afp == NULL) { allfamilies = 1; afp = af_getbyname("inet"); } else allfamilies = 0; ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : 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); if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) if (ifr.ifr_metric) printf(" metric %d", ifr.ifr_metric); if (ioctl(s, SIOCGIFMTU, &ifr) != -1) printf(" mtu %d", ifr.ifr_mtu); if (showrtref && ioctl(s, SIOCGIFGETRTREFCNT, &ifr) != -1) printf(" rtref %d", ifr.ifr_route_refcnt); if (verbose) { unsigned int ifindex = if_nametoindex(ifa->ifa_name); if (ifindex != 0) printf(" index %u", ifindex); } #ifdef SIOCGIFCONSTRAINED // Constrained is stored in if_xflags which isn't exposed directly if (ioctl(s, SIOCGIFCONSTRAINED, (caddr_t)&ifr) == 0 && ifr.ifr_constrained != 0) { printf(" constrained"); } #endif putchar('\n'); if (verbose && ioctl(s, SIOCGIFEFLAGS, (caddr_t)&ifr) != -1 && (eflags = ifr.ifr_eflags) != 0) { printb("\teflags", eflags, IFEFBITS); putchar('\n'); } if (verbose && ioctl(s, SIOCGIFXFLAGS, (caddr_t)&ifr) != -1 && (xflags = ifr.ifr_xflags) != 0) { printb("\txflags", xflags, IFXFBITS); putchar('\n'); } if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { if (ifr.ifr_curcap != 0) { curcap = ifr.ifr_curcap; printb("\toptions", ifr.ifr_curcap, IFCAPBITS); putchar('\n'); } if (supmedia && ifr.ifr_reqcap != 0) { printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); putchar('\n'); } } 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 (afp->af_af == ift->ifa_addr->sa_family) afp->af_status(s, ift); } /* Print CLAT46 address */ clat46_addr(s, name); /* Print NAT64 prefix */ nat64_status(s, name); #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 (afp->af_other_status != NULL) afp->af_other_status(s); strlcpy(ifs.ifs_name, name, sizeof ifs.ifs_name); if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); /* The rest is for when verbose is set; if not set, we're done */ if (!verbose) goto done; if (ioctl(s, SIOCGIFTYPE, &ifr) != -1) { char *c = ift2str(ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily); if (c != NULL) printf("\ttype: %s\n", c); } if (verbose > 1) { if (ioctl(s, SIOCGIFFUNCTIONALTYPE, &ifr) != -1) { char *c = iffunct2str(ifr.ifr_functional_type); if (c != NULL) printf("\tfunctional type: %s\n", c); } } { struct if_agentidsreq ifar; memset(&ifar, 0, sizeof(ifar)); strlcpy(ifar.ifar_name, name, sizeof(ifar.ifar_name)); if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != -1) { if (ifar.ifar_count != 0) { ifar.ifar_uuids = calloc(ifar.ifar_count, sizeof(uuid_t)); if (ifar.ifar_uuids != NULL) { if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != 1) { for (int agent_i = 0; agent_i < ifar.ifar_count; agent_i++) { struct netagent_req nar; memset(&nar, 0, sizeof(nar)); uuid_copy(nar.netagent_uuid, ifar.ifar_uuids[agent_i]); if (ioctl(s, SIOCGIFAGENTDATA, &nar) != 1) { printf("\tagent domain:%s type:%s flags:0x%x desc:\"%s\"\n", nar.netagent_domain, nar.netagent_type, nar.netagent_flags, nar.netagent_desc); } } } free(ifar.ifar_uuids); } } } } if (ioctl(s, SIOCGIFLINKQUALITYMETRIC, &ifr) != -1) { int lqm = ifr.ifr_link_quality_metric; if (verbose > 1) { printf("\tlink quality: %d ", lqm); if (lqm == IFNET_LQM_THRESH_OFF) printf("(off)"); else if (lqm == IFNET_LQM_THRESH_UNKNOWN) printf("(unknown)"); else if (lqm > IFNET_LQM_THRESH_UNKNOWN && lqm <= IFNET_LQM_THRESH_BAD) printf("(bad)"); else if (lqm > IFNET_LQM_THRESH_UNKNOWN && lqm <= IFNET_LQM_THRESH_POOR) printf("(poor)"); else if (lqm > IFNET_LQM_THRESH_POOR && lqm <= IFNET_LQM_THRESH_GOOD) printf("(good)"); else printf("(?)"); printf("\n"); } else if (lqm > IFNET_LQM_THRESH_UNKNOWN) { printf("\tlink quality: %d ", lqm); if (lqm <= IFNET_LQM_THRESH_BAD) printf("(bad)"); else if (lqm <= IFNET_LQM_THRESH_POOR) printf("(poor)"); else if (lqm <= IFNET_LQM_THRESH_GOOD) printf("(good)"); else printf("(?)"); printf("\n"); } } { if (ioctl(s, SIOCGIFINTERFACESTATE, &ifr) != -1) { printf("\tstate"); if (ifr.ifr_interface_state.valid_bitmask & IF_INTERFACE_STATE_RRC_STATE_VALID) { uint8_t rrc_state = ifr.ifr_interface_state.rrc_state; printf(" rrc: %u ", rrc_state); if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_CONNECTED) printf("(connected)"); else if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_IDLE) printf("(idle)"); else printf("(?)"); } if (ifr.ifr_interface_state.valid_bitmask & IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) { uint8_t ifavail = ifr.ifr_interface_state.interface_availability; printf(" availability: %u ", ifavail); if (ifavail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE) printf("(true)"); else if (ifavail == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE) printf("(false)"); else printf("(?)"); } else { printf(" availability: (not valid)"); } if (verbose > 1 && ifr.ifr_interface_state.valid_bitmask & IF_INTERFACE_STATE_LQM_STATE_VALID) { int8_t lqm = ifr.ifr_interface_state.lqm_state; printf(" lqm: %d", lqm); if (lqm == IFNET_LQM_THRESH_OFF) printf("(off)"); else if (lqm == IFNET_LQM_THRESH_UNKNOWN) printf("(unknown)"); else if (lqm == IFNET_LQM_THRESH_BAD) printf("(bad)"); else if (lqm == IFNET_LQM_THRESH_POOR) printf("(poor)"); else if (lqm == IFNET_LQM_THRESH_GOOD) printf("(good)"); else printf("(?)"); } } printf("\n"); } bzero(&iflpr, sizeof (iflpr)); strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); if (ioctl(s, SIOCGIFLINKPARAMS, &iflpr) != -1) { u_int64_t ibw_max = iflpr.iflpr_input_bw.max_bw; u_int64_t ibw_eff = iflpr.iflpr_input_bw.eff_bw; u_int64_t obw_max = iflpr.iflpr_output_bw.max_bw; u_int64_t obw_eff = iflpr.iflpr_output_bw.eff_bw; u_int64_t obw_tbr = iflpr.iflpr_output_tbr_rate; u_int32_t obw_pct = iflpr.iflpr_output_tbr_percent; u_int64_t ilt_max = iflpr.iflpr_input_lt.max_lt; u_int64_t ilt_eff = iflpr.iflpr_input_lt.eff_lt; u_int64_t olt_max = iflpr.iflpr_output_lt.max_lt; u_int64_t olt_eff = iflpr.iflpr_output_lt.eff_lt; if (eflags & IFEF_TXSTART) { u_int32_t flags = iflpr.iflpr_flags; u_int32_t sched = iflpr.iflpr_output_sched; struct if_throttlereq iftr; printf("\tscheduler: %s%s ", (flags & IFLPRF_ALTQ) ? "ALTQ_" : "", sched2str(sched)); if (flags & IFLPRF_DRVMANAGED) printf("(driver managed)"); printf("\n"); bzero(&iftr, sizeof (iftr)); strlcpy(iftr.ifthr_name, name, sizeof (iftr.ifthr_name)); if (ioctl(s, SIOCGIFTHROTTLE, &iftr) != -1 && iftr.ifthr_level != IFNET_THROTTLE_OFF) printf("\tthrottling: level %d (%s)\n", iftr.ifthr_level, tl2str(iftr.ifthr_level)); } if (obw_tbr != 0 && obw_eff > obw_tbr) obw_eff = obw_tbr; if (ibw_max != 0 || obw_max != 0) { if (ibw_max == obw_max && ibw_eff == obw_eff && ibw_max == ibw_eff && obw_tbr == 0) { printf("\tlink rate: %s\n", bps_to_str(ibw_max)); } else { printf("\tuplink rate: %s [eff] / ", bps_to_str(obw_eff)); if (obw_tbr != 0) { if (obw_pct == 0) printf("%s [tbr] / ", bps_to_str(obw_tbr)); else printf("%s [tbr %u%%] / ", bps_to_str(obw_tbr), obw_pct); } printf("%s", bps_to_str(obw_max)); if (obw_tbr != 0) printf(" [max]"); printf("\n"); if (ibw_eff == ibw_max) { printf("\tdownlink rate: %s\n", bps_to_str(ibw_max)); } else { printf("\tdownlink rate: " "%s [eff] / ", bps_to_str(ibw_eff)); printf("%s [max]\n", bps_to_str(ibw_max)); } } } else if (obw_tbr != 0) { printf("\tuplink rate: %s [tbr]\n", bps_to_str(obw_tbr)); } if (ilt_max != 0 || olt_max != 0) { if (ilt_max == olt_max && ilt_eff == olt_eff && ilt_max == ilt_eff) { printf("\tlink latency: %s\n", ns_to_str(ilt_max)); } else { if (olt_max != 0 && olt_eff == olt_max) { printf("\tuplink latency: %s\n", ns_to_str(olt_max)); } else if (olt_max != 0) { printf("\tuplink latency: " "%s [eff] / ", ns_to_str(olt_eff)); printf("%s [max]\n", ns_to_str(olt_max)); } if (ilt_max != 0 && ilt_eff == ilt_max) { printf("\tdownlink latency: %s\n", ns_to_str(ilt_max)); } else if (ilt_max != 0) { printf("\tdownlink latency: " "%s [eff] / ", ns_to_str(ilt_eff)); printf("%s [max]\n", ns_to_str(ilt_max)); } } } } /* Common OID prefix */ mib[0] = CTL_NET; mib[1] = PF_LINK; mib[2] = NETLINK_GENERIC; mib[3] = IFMIB_IFDATA; mib[4] = if_nametoindex(name); mib[5] = IFDATA_SUPPLEMENTAL; if (sysctl(mib, 6, &ifmsupp, &miblen, (void *)0, 0) == -1) err(1, "sysctl IFDATA_SUPPLEMENTAL"); if (ifmsupp.ifmd_data_extended.ifi_alignerrs != 0) { printf("\tunaligned pkts: %llu\n", ifmsupp.ifmd_data_extended.ifi_alignerrs); } if (ifmsupp.ifmd_data_extended.ifi_dt_bytes != 0) { printf("\tdata milestone interval: %s\n", bytes_to_str(ifmsupp.ifmd_data_extended.ifi_dt_bytes)); } bzero(&ifdr, sizeof (ifdr)); strlcpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); if (ioctl(s, SIOCGIFDESC, &ifdr) != -1 && ifdr.ifdr_len) { printf("\tdesc: %s\n", ifdr.ifdr_desc); } if (ioctl(s, SIOCGIFLOG, &ifr) != -1 && ifr.ifr_log.ifl_level) { printf("\tlogging: level %d ", ifr.ifr_log.ifl_level); printb("facilities", ifr.ifr_log.ifl_flags, IFRLOGF_BITS); putchar('\n'); } if (ioctl(s, SIOCGIFDELEGATE, &ifr) != -1 && ifr.ifr_delegated) { char delegatedif[IFNAMSIZ+1]; if (if_indextoname(ifr.ifr_delegated, delegatedif) != NULL) printf("\teffective interface: %s\n", delegatedif); } if (ioctl(s, SIOCGSTARTDELAY, &ifr) != -1) { if (ifr.ifr_start_delay_qlen > 0 && ifr.ifr_start_delay_timeout > 0) { printf("\ttxstart qlen: %u packets " "timeout: %u microseconds\n", ifr.ifr_start_delay_qlen, ifr.ifr_start_delay_timeout/1000); } } #if defined(IFCAP_HW_TIMESTAMP) && defined(IFCAP_SW_TIMESTAMP) if ((curcap & (IFCAP_HW_TIMESTAMP | IFCAP_SW_TIMESTAMP)) && ioctl(s, SIOCGIFTIMESTAMPENABLED, &ifr) != -1) { printf("\ttimestamp: %s\n", (ifr.ifr_intval != 0) ? "enabled" : "disabled"); } #endif #if defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) if (ioctl(s, SIOCGQOSMARKINGENABLED, &ifr) != -1) { printf("\tqosmarking enabled: %s mode: ", ifr.ifr_qosmarking_enabled ? "yes" : "no"); if (ioctl(s, SIOCGQOSMARKINGMODE, &ifr) != -1) { switch (ifr.ifr_qosmarking_mode) { case IFRTYPE_QOSMARKING_FASTLANE: printf("fastlane\n"); break; case IFRTYPE_QOSMARKING_RFC4594: printf("RFC4594\n"); break; case IFRTYPE_QOSMARKING_MODE_NONE: printf("none\n"); break; default: printf("unknown (%u)\n", ifr.ifr_qosmarking_mode); break; } } } #endif /* defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) */ if (ioctl(s, SIOCGIFLOWPOWER, &ifr) != -1) { printf("\tlow power mode: %s\n", (ifr.ifr_low_power_mode != 0) ? "enabled" : "disabled"); } if (ioctl(s, SIOCGIFMPKLOG, &ifr) != -1) { printf("\tmulti layer packet logging (mpklog): %s\n", (ifr.ifr_mpk_log != 0) ? "enabled" : "disabled"); } show_routermode(s); show_routermode6(); done: close(s); return; } #define KILOBYTES 1024 #define MEGABYTES (KILOBYTES * KILOBYTES) #define GIGABYTES (KILOBYTES * KILOBYTES * KILOBYTES) static char * bytes_to_str(unsigned long long bytes) { static char buf[32]; const char *u; long double n = bytes, t; if (bytes >= GIGABYTES) { t = n / GIGABYTES; u = "GB"; } else if (n >= MEGABYTES) { t = n / MEGABYTES; u = "MB"; } else if (n >= KILOBYTES) { t = n / KILOBYTES; u = "KB"; } else { t = n; u = "bytes"; } snprintf(buf, sizeof (buf), "%-4.2Lf %s", t, u); return (buf); } #define GIGABIT_PER_SEC 1000000000 /* gigabit per second */ #define MEGABIT_PER_SEC 1000000 /* megabit per second */ #define KILOBIT_PER_SEC 1000 /* kilobit per second */ static char * bps_to_str(unsigned long long rate) { static char buf[32]; const char *u; long double n = rate, t; if (rate >= GIGABIT_PER_SEC) { t = n / GIGABIT_PER_SEC; u = "Gbps"; } else if (n >= MEGABIT_PER_SEC) { t = n / MEGABIT_PER_SEC; u = "Mbps"; } else if (n >= KILOBIT_PER_SEC) { t = n / KILOBIT_PER_SEC; u = "Kbps"; } else { t = n; u = "bps "; } snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); return (buf); } #define NSEC_PER_SEC 1000000000 /* nanosecond per second */ #define USEC_PER_SEC 1000000 /* microsecond per second */ #define MSEC_PER_SEC 1000 /* millisecond per second */ static char * ns_to_str(unsigned long long nsec) { static char buf[32]; const char *u; long double n = nsec, t; if (nsec >= NSEC_PER_SEC) { t = n / NSEC_PER_SEC; u = "sec "; } else if (n >= USEC_PER_SEC) { t = n / USEC_PER_SEC; u = "msec"; } else if (n >= MSEC_PER_SEC) { t = n / MSEC_PER_SEC; u = "usec"; } else { t = n; u = "nsec"; } snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); return (buf); } static void tunnel_status(int s) { af_all_tunnel_status(s); } static void clat46_addr(int s, char * if_name) { struct if_clat46req ifr; char buf[MAXHOSTNAMELEN]; bzero(&ifr, sizeof (ifr)); strlcpy(ifr.ifclat46_name, if_name, sizeof(ifr.ifclat46_name)); if (ioctl(s, SIOCGIFCLAT46ADDR, &ifr) < 0) { if (errno != ENOENT) syslog(LOG_WARNING, "ioctl (SIOCGIFCLAT46ADDR): %d", errno); return; } if (inet_ntop(AF_INET6, &ifr.ifclat46_addr.v6_address, buf, sizeof(buf)) != NULL) printf("\tinet6 %s prefixlen %d clat46\n", buf, ifr.ifclat46_addr.v6_prefixlen); } static void nat64_status(int s, char * if_name) { int i; struct if_nat64req ifr; char buf[MAXHOSTNAMELEN]; bzero(&ifr, sizeof(ifr)); strlcpy(ifr.ifnat64_name, if_name, sizeof(ifr.ifnat64_name)); if (ioctl(s, SIOCGIFNAT64PREFIX, &ifr) < 0) { if (errno != ENOENT) syslog(LOG_WARNING, "ioctl(SIOCGIFNAT64PREFIX): %d", errno); return; } for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) { if (ifr.ifnat64_prefixes[i].prefix_len > 0) { inet_ntop(AF_INET6, &ifr.ifnat64_prefixes[i].ipv6_prefix, buf, sizeof(buf)); printf("\tnat64 prefix %s prefixlen %d\n", buf, ifr.ifnat64_prefixes[i].prefix_len << 3); } } } void Perror(const char *cmd) { 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); } } /* * 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); bits++; if (bits) { putchar('<'); while ((i = *bits++) != '\0') { if (v & (1 << (i-1))) { if (any) putchar(','); any = 1; for (; (c = *bits) > 32; bits++) putchar(c); } else for (; *bits > 32; bits++) ; } putchar('>'); } } #ifndef __APPLE__ void ifmaybeload(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; /* loading suppressed by the user */ if (noload) return; /* trim the interface number off the end */ strlcpy(ifname, name, sizeof(ifname)); for (dp = ifname; *dp != 0; dp++) if (isdigit(*dp)) { *dp = 0; break; } /* turn interface and unit into module name */ strlcpy(ifkind, "if_", sizeof(ifkind)); strlcpy(ifkind + MOD_PREFIX_LEN, ifname, sizeof(ifkind) - MOD_PREFIX_LEN); /* 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; } /* already loaded? */ if (strncmp(ifname, cp, strlen(ifname) + 1) == 0 || strncmp(ifkind, cp, strlen(ifkind) + 1) == 0) return; } } /* not present, we should try to load it */ kldload(ifkind); } #endif 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), #ifdef IFF_PPROMISC DEF_CMD("promisc", IFF_PPROMISC, setifflags), DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), #endif /* IFF_PPROMISC */ 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_ARG("ipdst", setifipdst), DEF_CMD_ARG2("tunnel", settunnel), DEF_CMD("-tunnel", 0, deletetunnel), DEF_CMD("deletetunnel", 0, deletetunnel), 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), #ifdef IFF_MONITOR DEF_CMD("monitor", IFF_MONITOR:, setifflags), DEF_CMD("-monitor", -IFF_MONITOR, setifflags), #endif /* IFF_MONITOR */ DEF_CMD("mpklog", 1, setifmpklog), DEF_CMD("-mpklog", 0, setifmpklog), #ifdef IFF_STATICARP DEF_CMD("staticarp", IFF_STATICARP, setifflags), DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), #endif /* IFF_STATICARP */ #ifdef IFCAP_RXCSUM DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), #endif /* IFCAP_RXCSUM */ #ifdef IFCAP_TXCSUM DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), #endif /* IFCAP_TXCSUM */ #ifdef IFCAP_NETCONS DEF_CMD("netcons", IFCAP_NETCONS, setifcap), DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), #endif /* IFCAP_NETCONS */ #ifdef IFCAP_POLLING DEF_CMD("polling", IFCAP_POLLING, setifcap), DEF_CMD("-polling", -IFCAP_POLLING, setifcap), #endif /* IFCAP_POLLING */ #ifdef IFCAP_TSO DEF_CMD("tso", IFCAP_TSO, setifcap), DEF_CMD("-tso", -IFCAP_TSO, setifcap), #endif /* IFCAP_TSO */ #ifdef IFCAP_LRO DEF_CMD("lro", IFCAP_LRO, setifcap), DEF_CMD("-lro", -IFCAP_LRO, setifcap), #endif /* IFCAP_LRO */ #ifdef IFCAP_WOL DEF_CMD("wol", IFCAP_WOL, setifcap), DEF_CMD("-wol", -IFCAP_WOL, setifcap), #endif /* IFCAP_WOL */ #ifdef IFCAP_WOL_UCAST DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), #endif /* IFCAP_WOL_UCAST */ #ifdef IFCAP_WOL_MCAST DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), #endif /* IFCAP_WOL_MCAST */ #ifdef IFCAP_WOL_MAGIC DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), #endif /* IFCAP_WOL_MAGIC */ DEF_CMD("normal", -IFF_LINK0, setifflags), DEF_CMD("compress", IFF_LINK0, setifflags), DEF_CMD("noicmp", IFF_LINK1, setifflags), DEF_CMD_ARG("mtu", setifmtu), #ifdef notdef DEF_CMD_ARG("name", setifname), #endif /* notdef */ #ifdef IFCAP_AV DEF_CMD("av", IFCAP_AV, setifcap), DEF_CMD("-av", -IFCAP_AV, setifcap), #endif /* IFCAP_AV */ DEF_CMD("router", 1, setrouter), DEF_CMD("-router", 0, setrouter), DEF_CMD_VA("routermode", routermode), DEF_CMD_ARG("desc", setifdesc), DEF_CMD_ARG("tbr", settbr), DEF_CMD_VA("netem", setnetem), DEF_CMD_ARG("throttle", setthrottle), DEF_CMD_ARG("log", setlog), DEF_CMD("cl2k", 1, setcl2k), DEF_CMD("-cl2k", 0, setcl2k), DEF_CMD("expensive", 1, setexpensive), DEF_CMD("-expensive", 0, setexpensive), #ifdef SIOCSIFCONSTRAINED DEF_CMD("constrained", 1, setconstrained), DEF_CMD("-constrained", 0, setconstrained), #endif DEF_CMD("timestamp", 1, settimestamp), DEF_CMD("-timestamp", 0, settimestamp), DEF_CMD_ARG("ecn", setecnmode), DEF_CMD_ARG2("fastlane", setfastlane), DEF_CMD_ARG2("qosmarking", setqosmarking), DEF_CMD_ARG("disable_output", setdisableoutput), DEF_CMD("probe_connectivity", 1, setprobeconnectivity), DEF_CMD("-probe_connectivity", 0, setprobeconnectivity), DEF_CMD("lowpowermode", 1, setlowpowermode), DEF_CMD("-lowpowermode", 0, setlowpowermode), DEF_CMD_ARG("subfamily", setifsubfamily), DEF_CMD("available", 1, setifavailability), DEF_CMD("-available", 0, setifavailability), DEF_CMD("unavailable", 0, setifavailability), }; static __constructor void ifconfig_ctor(void) { #define N(a) (sizeof(a) / sizeof(a[0])) int i; for (i = 0; i < N(basic_cmds); i++) cmd_register(&basic_cmds[i]); #undef N } static char * sched2str(unsigned int s) { char *c; switch (s) { case PKTSCHEDT_NONE: c = "NONE"; break; case PKTSCHEDT_FQ_CODEL: c = "FQ_CODEL"; break; default: c = "UNKNOWN"; break; } return (c); } static char * tl2str(unsigned int s) { char *c; switch (s) { case IFNET_THROTTLE_OFF: c = "off"; break; case IFNET_THROTTLE_OPPORTUNISTIC: c = "opportunistic"; break; default: c = "unknown"; break; } return (c); } static char * ift2str(unsigned int t, unsigned int f, unsigned int sf) { static char buf[256]; char *c = NULL; switch (t) { case IFT_ETHER: switch (sf) { case IFRTYPE_SUBFAMILY_USB: c = "USB Ethernet"; break; case IFRTYPE_SUBFAMILY_BLUETOOTH: c = "Bluetooth PAN"; break; case IFRTYPE_SUBFAMILY_WIFI: c = "Wi-Fi"; break; case IFRTYPE_SUBFAMILY_THUNDERBOLT: c = "IP over Thunderbolt"; break; case IFRTYPE_SUBFAMILY_ANY: default: c = "Ethernet"; break; } break; case IFT_IEEE1394: c = "IP over FireWire"; break; case IFT_PKTAP: c = "Packet capture"; break; case IFT_CELLULAR: c = "Cellular"; break; case IFT_OTHER: if (ifr.ifr_type.ift_family == APPLE_IF_FAM_IPSEC) { if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_BLUETOOTH) { c = "Companion Link Bluetooth"; } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_QUICKRELAY) { c = "Companion Link QuickRelay"; } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_WIFI) { c = "Companion Link Wi-Fi"; } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_DEFAULT) { c = "Companion Link Default"; } } break; case IFT_BRIDGE: case IFT_PFLOG: case IFT_PFSYNC: case IFT_PPP: case IFT_LOOP: case IFT_GIF: case IFT_STF: case IFT_L2VLAN: case IFT_IEEE8023ADLAG: default: break; } if (verbose > 1) { if (c == NULL) { (void) snprintf(buf, sizeof (buf), "0x%x family: %u subfamily: %u", ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily); } else { (void) snprintf(buf, sizeof (buf), "%s (0x%x) family: %u subfamily: %u", c, ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily); } c = buf; } return (c); } static char * iffunct2str(u_int32_t functional_type) { char *str = NULL; switch (functional_type) { case IFRTYPE_FUNCTIONAL_UNKNOWN: break; case IFRTYPE_FUNCTIONAL_LOOPBACK: str = "loopback"; break; case IFRTYPE_FUNCTIONAL_WIRED: str = "wired"; break; case IFRTYPE_FUNCTIONAL_WIFI_INFRA: str = "wifi"; break; case IFRTYPE_FUNCTIONAL_WIFI_AWDL: str = "awdl"; break; case IFRTYPE_FUNCTIONAL_CELLULAR: str = "cellular"; break; case IFRTYPE_FUNCTIONAL_INTCOPROC: break; case IFRTYPE_FUNCTIONAL_COMPANIONLINK: str = "companionlink"; break; default: break; } return str; }