diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index 6d66677467c1..5d789795e636 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,2078 +1,2107 @@ /*- * 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 #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; #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; 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)); } static void ifr_set_name(struct ifreq *ifr, const char *name) { strlcpy(ifr->ifr_name, name, sizeof(ifr->ifr_name)); } int ioctl_ctx_ifr(if_ctx *ctx, unsigned long cmd, struct ifreq *ifr) { ifr_set_name(ifr, ctx->ifname); return (ioctl_ctx(ctx, cmd, ifr)); } 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 = true; 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 = satosdl_c(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; ctx->ifname = cp; /* * 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; struct ifreq ifr = {}; 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 __unused) { 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 __unused, int param __unused) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } #ifdef JAIL static void setifvnet(if_ctx *ctx, const char *jname, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_jid = jail_getid(jname); if (ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl_ctx_ifr(ctx, SIOCSIFVNET, &ifr) < 0) err(1, "SIOCSIFVNET"); } static void setifrvnet(if_ctx *ctx, const char *jname, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_jid = jail_getid(jname); if (ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl_ctx_ifr(ctx, SIOCSIFRVNET, &ifr) < 0) err(1, "SIOCSIFRVNET(%d, %s)", ifr.ifr_jid, 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 __unused, 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 +clearifflags(if_ctx *ctx, const char *vname, int value) +{ + struct ifreq my_ifr; + int flags; + + flags = getifflags(ctx->ifname, ctx->io_s, false); + 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); +} + 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; + 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 +clearifcap(if_ctx *ctx, const char *vname, int value) +{ + struct ifreq ifr = {}; + int flags; + + if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) { + Perror("ioctl (SIOCGIFCAP)"); + exit(1); + } + flags = ifr.ifr_curcap; + 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(ctx, SIOCSIFCAP, &ifr) < 0) + Perror(vname); +} + void setifcap(if_ctx *ctx, const char *vname, int value) { struct ifreq ifr = {}; int flags; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; - if (value < 0) { - value = -value; - flags &= ~value; - } else - flags |= value; + 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(ctx, SIOCSIFCAP, &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; struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &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(ctx, 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) { struct ifreq ifr = {}; ifr.ifr_metric = atoi(val); if (ioctl_ctx_ifr(ctx, SIOCSIFMETRIC, &ifr) < 0) err(1, "ioctl SIOCSIFMETRIC (set metric)"); } static void setifmtu(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_mtu = atoi(val); if (ioctl_ctx_ifr(ctx, SIOCSIFMTU, &ifr) < 0) err(1, "ioctl SIOCSIFMTU (set mtu)"); } static void setifpcp(if_ctx *ctx, const char *val, int arg __unused) { struct ifreq ifr = {}; 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_ifr(ctx, SIOCSLANPCP, &ifr) == -1) err(1, "SIOCSLANPCP"); } static void disableifpcp(if_ctx *ctx, const char *val __unused, int arg __unused) { struct ifreq ifr = {}; ifr.ifr_lan_pcp = IFNET_PCP_NONE; if (ioctl_ctx_ifr(ctx, SIOCSLANPCP, &ifr) == -1) err(1, "SIOCSLANPCP"); } static void setifname(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; char *newname; ifr_set_name(&ifr, ctx->ifname); newname = strdup(val); if (newname == NULL) err(1, "no memory to set ifname"); ifr.ifr_data = newname; if (ioctl_ctx(ctx, 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) { struct ifreq ifr = {}; char *newdescr; 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_ifr(ctx, SIOCSIFDESCR, &ifr) < 0) err(1, "ioctl SIOCSIFDESCR (set descr)"); free(newdescr); } static void unsetifdescr(if_ctx *ctx, const char *val __unused, int value __unused) { setifdescr(ctx, "", 0); } #ifdef WITHOUT_NETLINK #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(if_ctx *ctx) { struct ifreq ifr = {}; 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_ctx_ifr(ctx, SIOCGIFCAPNV, &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 (ctx->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_ctx(ctx, SIOCGIFCAP, (caddr_t)&ifr) != 0) Perror("ioctl (SIOCGIFCAP)"); } static void print_ifcap(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) != 0) return; if ((ifr.ifr_curcap & IFCAP_NV) != 0) print_ifcap_nv(ctx); else { printb("\toptions", ifr.ifr_curcap, IFCAPBITS); putchar('\n'); if (ctx->args->supmedia && ifr.ifr_reqcap != 0) { printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); putchar('\n'); } } } #endif 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(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFMETRIC, &ifr) != -1) printf(" metric %d", ifr.ifr_metric); } #ifdef WITHOUT_NETLINK static void print_mtu(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFMTU, &ifr) != -1) printf(" mtu %d", ifr.ifr_mtu); } static void print_description(if_ctx *ctx) { struct ifreq ifr = {}; ifr_set_name(&ifr, ctx->ifname); for (;;) { if ((descr = reallocf(descr, descrlen)) != NULL) { ifr.ifr_buffer.buffer = descr; ifr.ifr_buffer.length = descrlen; if (ioctl_ctx(ctx, 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 __unused, struct ifaddrs *ifa) { struct ifaddrs *ift; int s, old_s; struct ifconfig_args *args = ctx->args; bool allfamilies = args->afp == NULL; struct ifreq ifr = {}; 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; 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: ", ctx->ifname); printb("flags", ifa->ifa_flags, IFFBITS); print_metric(ctx); print_mtu(ctx); putchar('\n'); print_description(ctx); print_ifcap(ctx); 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) { 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("down", IFF_UP, clearifflags), + DEF_CMD("arp", IFF_NOARP, clearifflags), DEF_CMD("-arp", IFF_NOARP, setifflags), DEF_CMD("debug", IFF_DEBUG, setifflags), - DEF_CMD("-debug", -IFF_DEBUG, setifflags), + DEF_CMD("-debug", IFF_DEBUG, clearifflags), 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("-promisc", IFF_PPROMISC, clearifflags), 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), + DEF_CMD("-swabips", EN_SWABIPS, clearifflags), #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("-link0", IFF_LINK0, clearifflags), DEF_CMD("link1", IFF_LINK1, setifflags), - DEF_CMD("-link1", -IFF_LINK1, setifflags), + DEF_CMD("-link1", IFF_LINK1, clearifflags), DEF_CMD("link2", IFF_LINK2, setifflags), - DEF_CMD("-link2", -IFF_LINK2, setifflags), + DEF_CMD("-link2", IFF_LINK2, clearifflags), DEF_CMD("monitor", IFF_MONITOR, setifflags), - DEF_CMD("-monitor", -IFF_MONITOR, setifflags), + DEF_CMD("-monitor", IFF_MONITOR, clearifflags), DEF_CMD("mextpg", IFCAP_MEXTPG, setifcap), - DEF_CMD("-mextpg", -IFCAP_MEXTPG, setifcap), + DEF_CMD("-mextpg", IFCAP_MEXTPG, clearifcap), DEF_CMD("staticarp", IFF_STATICARP, setifflags), - DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), + DEF_CMD("-staticarp", IFF_STATICARP, clearifflags), DEF_CMD("stickyarp", IFF_STICKYARP, setifflags), - DEF_CMD("-stickyarp", -IFF_STICKYARP, setifflags), + DEF_CMD("-stickyarp", IFF_STICKYARP, clearifflags), DEF_CMD("rxcsum6", IFCAP_RXCSUM_IPV6, setifcap), - DEF_CMD("-rxcsum6", -IFCAP_RXCSUM_IPV6, setifcap), + DEF_CMD("-rxcsum6", IFCAP_RXCSUM_IPV6, clearifcap), DEF_CMD("txcsum6", IFCAP_TXCSUM_IPV6, setifcap), - DEF_CMD("-txcsum6", -IFCAP_TXCSUM_IPV6, setifcap), + DEF_CMD("-txcsum6", IFCAP_TXCSUM_IPV6, clearifcap), DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), - DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), + DEF_CMD("-rxcsum", IFCAP_RXCSUM, clearifcap), DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), - DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), + DEF_CMD("-txcsum", IFCAP_TXCSUM, clearifcap), DEF_CMD("netcons", IFCAP_NETCONS, setifcap), - DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), + DEF_CMD("-netcons", IFCAP_NETCONS, clearifcap), DEF_CMD_ARG("pcp", setifpcp), DEF_CMD("-pcp", 0, disableifpcp), DEF_CMD("polling", IFCAP_POLLING, setifcap), - DEF_CMD("-polling", -IFCAP_POLLING, setifcap), + DEF_CMD("-polling", IFCAP_POLLING, clearifcap), DEF_CMD("tso6", IFCAP_TSO6, setifcap), - DEF_CMD("-tso6", -IFCAP_TSO6, setifcap), + DEF_CMD("-tso6", IFCAP_TSO6, clearifcap), DEF_CMD("tso4", IFCAP_TSO4, setifcap), - DEF_CMD("-tso4", -IFCAP_TSO4, setifcap), + DEF_CMD("-tso4", IFCAP_TSO4, clearifcap), DEF_CMD("tso", IFCAP_TSO, setifcap), - DEF_CMD("-tso", -IFCAP_TSO, setifcap), + DEF_CMD("-tso", IFCAP_TSO, clearifcap), DEF_CMD("toe", IFCAP_TOE, setifcap), - DEF_CMD("-toe", -IFCAP_TOE, setifcap), + DEF_CMD("-toe", IFCAP_TOE, clearifcap), DEF_CMD("lro", IFCAP_LRO, setifcap), - DEF_CMD("-lro", -IFCAP_LRO, setifcap), + DEF_CMD("-lro", IFCAP_LRO, clearifcap), DEF_CMD("txtls", IFCAP_TXTLS, setifcap), - DEF_CMD("-txtls", -IFCAP_TXTLS, setifcap), + DEF_CMD("-txtls", IFCAP_TXTLS, clearifcap), 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", IFCAP_WOL, clearifcap), DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), - DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), + DEF_CMD("-wol_ucast", IFCAP_WOL_UCAST, clearifcap), DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), - DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), + DEF_CMD("-wol_mcast", IFCAP_WOL_MCAST, clearifcap), DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), - DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), + DEF_CMD("-wol_magic", IFCAP_WOL_MAGIC, clearifcap), DEF_CMD("txrtlmt", IFCAP_TXRTLMT, setifcap), - DEF_CMD("-txrtlmt", -IFCAP_TXRTLMT, setifcap), + DEF_CMD("-txrtlmt", IFCAP_TXRTLMT, clearifcap), DEF_CMD("txtlsrtlmt", IFCAP_TXTLS_RTLMT, setifcap), - DEF_CMD("-txtlsrtlmt", -IFCAP_TXTLS_RTLMT, setifcap), + DEF_CMD("-txtlsrtlmt", IFCAP_TXTLS_RTLMT, clearifcap), DEF_CMD("hwrxtstmp", IFCAP_HWRXTSTMP, setifcap), - DEF_CMD("-hwrxtstmp", -IFCAP_HWRXTSTMP, setifcap), - DEF_CMD("normal", -IFF_LINK0, setifflags), + DEF_CMD("-hwrxtstmp", IFCAP_HWRXTSTMP, clearifcap), + DEF_CMD("normal", IFF_LINK0, clearifflags), 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 e33a2c63aec1..84c1ac7eebce 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -1,327 +1,328 @@ /*- * 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__) int ioctl_ctx_ifr(if_ctx *ctx, unsigned long cmd, struct ifreq *ifr); 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 int allmedia; extern int exit_code; extern char *f_inet, *f_inet6, *f_ether, *f_addr; +void clearifcap(if_ctx *ctx, const char *, int value); 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 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(if_ctx *ctx); /* 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 *); 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); } static inline struct sockaddr_dl * satosdl(struct sockaddr *sa) { return ((struct sockaddr_dl *)(void *)sa); } static inline const struct sockaddr_dl * satosdl_c(const struct sockaddr *sa) { return ((const struct sockaddr_dl *)(const void *)sa); } diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c index b871e953c44a..adc3c4692f8b 100644 --- a/sbin/ifconfig/ifvlan.c +++ b/sbin/ifconfig/ifvlan.c @@ -1,316 +1,316 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1999 Bill Paul * Copyright (c) 2012 ADARA Networks, Inc. * All rights reserved. * * Portions of this software were developed by Robert N. M. Watson under * contract to ADARA Networks, Inc. * * 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 by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif #define NOTAG ((u_short) -1) static const char proto_8021Q[] = "802.1q"; static const char proto_8021ad[] = "802.1ad"; static const char proto_qinq[] = "qinq"; static struct vlanreq params = { .vlr_tag = NOTAG, .vlr_proto = ETHERTYPE_VLAN, }; static void vlan_status(if_ctx *ctx) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1) return; printf("\tvlan: %d", vreq.vlr_tag); printf(" vlanproto: "); switch (vreq.vlr_proto) { case ETHERTYPE_VLAN: printf(proto_8021Q); break; case ETHERTYPE_QINQ: printf(proto_8021ad); break; default: printf("0x%04x", vreq.vlr_proto); } if (ioctl_ctx_ifr(ctx, SIOCGVLANPCP, &ifr) != -1) printf(" vlanpcp: %u", ifr.ifr_vlan_pcp); printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ? "" : vreq.vlr_parent); printf("\n"); } static int vlan_match_ethervid(const char *name) { return (strchr(name, '.') != NULL); } static void vlan_parse_ethervid(const char *name) { char ifname[IFNAMSIZ]; char *cp; unsigned int vid; strlcpy(ifname, name, IFNAMSIZ); if ((cp = strrchr(ifname, '.')) == NULL) return; /* * Derive params from interface name: "parent.vid". */ *cp++ = '\0'; if ((*cp < '1') || (*cp > '9')) errx(1, "invalid vlan tag"); vid = *cp++ - '0'; while ((*cp >= '0') && (*cp <= '9')) { vid = (vid * 10) + (*cp++ - '0'); if (vid >= 0xFFF) errx(1, "invalid vlan tag"); } if (*cp != '\0') errx(1, "invalid vlan tag"); /* * allow "devX.Y vlandev devX vlan Y" syntax */ if (params.vlr_tag == NOTAG || params.vlr_tag == vid) params.vlr_tag = vid; else errx(1, "ambiguous vlan specification"); /* Restrict overriding interface name */ if (params.vlr_parent[0] == '\0' || !strcmp(params.vlr_parent, ifname)) strlcpy(params.vlr_parent, ifname, IFNAMSIZ); else errx(1, "ambiguous vlan specification"); } static void vlan_create(if_ctx *ctx, struct ifreq *ifr) { vlan_parse_ethervid(ifr->ifr_name); if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { /* * One or both parameters were specified, make sure both. */ if (params.vlr_tag == NOTAG) errx(1, "must specify a tag for vlan create"); if (params.vlr_parent[0] == '\0') errx(1, "must specify a parent device for vlan create"); ifr->ifr_data = (caddr_t) ¶ms; } ifcreate_ioctl(ctx, ifr); } static void vlan_cb(if_ctx *ctx __unused, void *arg __unused) { if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) errx(1, "both vlan and vlandev must be specified"); } static void vlan_set(int s, struct ifreq *ifr) { if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { ifr->ifr_data = (caddr_t) ¶ms; if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) err(1, "SIOCSETVLAN"); } } static void setvlantag(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; u_long ul; char *endp; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for vlan"); params.vlr_tag = ul; /* check if the value can be represented in vlr_tag */ if (params.vlr_tag != ul) errx(1, "value for vlan out of range"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { vreq.vlr_tag = params.vlr_tag; memcpy(¶ms, &vreq, sizeof(params)); vlan_set(ctx->io_s, &ifr); } } static void setvlandev(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) vlan_set(ctx->io_s, &ifr); } static void setvlanproto(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (strncasecmp(proto_8021Q, val, strlen(proto_8021Q)) == 0) { params.vlr_proto = ETHERTYPE_VLAN; } else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0) || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) { params.vlr_proto = ETHERTYPE_QINQ; } else errx(1, "invalid value for vlanproto"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { vreq.vlr_proto = params.vlr_proto; memcpy(¶ms, &vreq, sizeof(params)); vlan_set(ctx->io_s, &ifr); } } static void setvlanpcp(if_ctx *ctx, const char *val, int dummy __unused) { u_long ul; char *endp; struct ifreq ifr = {}; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for vlanpcp"); if (ul > 7) errx(1, "value for vlanpcp out of range"); ifr.ifr_vlan_pcp = ul; if (ioctl_ctx_ifr(ctx, SIOCSVLANPCP, &ifr) == -1) err(1, "SIOCSVLANPCP"); } static void unsetvlandev(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1) err(1, "SIOCGETVLAN"); bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); vreq.vlr_tag = 0; if (ioctl_ctx(ctx, SIOCSETVLAN, (caddr_t)&ifr) == -1) err(1, "SIOCSETVLAN"); } static struct cmd vlan_cmds[] = { DEF_CLONE_CMD_ARG("vlan", setvlantag), DEF_CLONE_CMD_ARG("vlandev", setvlandev), DEF_CLONE_CMD_ARG("vlanproto", setvlanproto), DEF_CMD_ARG("vlanpcp", setvlanpcp), /* NB: non-clone cmds */ DEF_CMD_ARG("vlan", setvlantag), DEF_CMD_ARG("vlandev", setvlandev), DEF_CMD_ARG("vlanproto", setvlanproto), /* XXX For compatibility. Should become DEF_CMD() some day. */ DEF_CMD_OPTARG("-vlandev", unsetvlandev), DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), - DEF_CMD("-vlanmtu", -IFCAP_VLAN_MTU, setifcap), + DEF_CMD("-vlanmtu", IFCAP_VLAN_MTU, clearifcap), DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), - DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap), + DEF_CMD("-vlanhwtag", IFCAP_VLAN_HWTAGGING, clearifcap), DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap), - DEF_CMD("-vlanhwfilter", -IFCAP_VLAN_HWFILTER, setifcap), - DEF_CMD("-vlanhwtso", -IFCAP_VLAN_HWTSO, setifcap), + DEF_CMD("-vlanhwfilter", IFCAP_VLAN_HWFILTER, clearifcap), + DEF_CMD("-vlanhwtso", IFCAP_VLAN_HWTSO, clearifcap), DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap), DEF_CMD("vlanhwcsum", IFCAP_VLAN_HWCSUM, setifcap), - DEF_CMD("-vlanhwcsum", -IFCAP_VLAN_HWCSUM, setifcap), + DEF_CMD("-vlanhwcsum", IFCAP_VLAN_HWCSUM, clearifcap), }; static struct afswtch af_vlan = { .af_name = "af_vlan", .af_af = AF_UNSPEC, .af_other_status = vlan_status, }; static __constructor void vlan_ctor(void) { size_t i; for (i = 0; i < nitems(vlan_cmds); i++) cmd_register(&vlan_cmds[i]); af_register(&af_vlan); callback_register(vlan_cb, NULL); clone_setdefcallback_prefix("vlan", vlan_create); clone_setdefcallback_filter(vlan_match_ethervid, vlan_create); } diff --git a/sbin/ifconfig/ifvxlan.c b/sbin/ifconfig/ifvxlan.c index 4f54bee88b41..72e2eac35c2d 100644 --- a/sbin/ifconfig/ifvxlan.c +++ b/sbin/ifconfig/ifvxlan.c @@ -1,636 +1,636 @@ /*- * Copyright (c) 2014, Bryan Venteicher * 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 unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static struct ifvxlanparam params = { .vxlp_vni = VXLAN_VNI_MAX, }; static int get_val(const char *cp, u_long *valp) { char *endptr; u_long val; errno = 0; val = strtoul(cp, &endptr, 0); if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) return (-1); *valp = val; return (0); } static int do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set) { struct ifdrv ifd = {}; strlcpy(ifd.ifd_name, ctx->ifname, sizeof(ifd.ifd_name)); ifd.ifd_cmd = op; ifd.ifd_len = argsize; ifd.ifd_data = arg; return (ioctl_ctx(ctx, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); } static int vxlan_exists(if_ctx *ctx) { struct ifvxlancfg cfg; bzero(&cfg, sizeof(cfg)); return (do_cmd(ctx, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1); } static void vxlan_status(if_ctx *ctx) { struct ifvxlancfg cfg; char src[NI_MAXHOST], dst[NI_MAXHOST]; char srcport[NI_MAXSERV], dstport[NI_MAXSERV]; struct sockaddr *lsa, *rsa; int vni, mc, ipv6; bzero(&cfg, sizeof(cfg)); if (do_cmd(ctx, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0) return; vni = cfg.vxlc_vni; lsa = &cfg.vxlc_local_sa.sa; rsa = &cfg.vxlc_remote_sa.sa; ipv6 = rsa->sa_family == AF_INET6; /* Just report nothing if the network identity isn't set yet. */ if (vni >= VXLAN_VNI_MAX) return; if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src), srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) src[0] = srcport[0] = '\0'; if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst), dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) dst[0] = dstport[0] = '\0'; if (!ipv6) { struct sockaddr_in *sin = satosin(rsa); mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr)); } else { struct sockaddr_in6 *sin6 = satosin6(rsa); mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr); } printf("\tvxlan vni %d", vni); printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "", srcport); printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "", dst, ipv6 ? "]" : "", dstport); if (ctx->args->verbose) { printf("\n\t\tconfig: "); printf("%slearning portrange %d-%d ttl %d", cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min, cfg.vxlc_port_max, cfg.vxlc_ttl); printf("\n\t\tftable: "); printf("cnt %d max %d timeout %d", cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max, cfg.vxlc_ftable_timeout); } putchar('\n'); } #define _LOCAL_ADDR46 \ (VXLAN_PARAM_WITH_LOCAL_ADDR4 | VXLAN_PARAM_WITH_LOCAL_ADDR6) #define _REMOTE_ADDR46 \ (VXLAN_PARAM_WITH_REMOTE_ADDR4 | VXLAN_PARAM_WITH_REMOTE_ADDR6) static void vxlan_check_params(void) { if ((params.vxlp_with & _LOCAL_ADDR46) == _LOCAL_ADDR46) errx(1, "cannot specify both local IPv4 and IPv6 addresses"); if ((params.vxlp_with & _REMOTE_ADDR46) == _REMOTE_ADDR46) errx(1, "cannot specify both remote IPv4 and IPv6 addresses"); if ((params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4 && params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) || (params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6 && params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4)) errx(1, "cannot mix IPv4 and IPv6 addresses"); } #undef _LOCAL_ADDR46 #undef _REMOTE_ADDR46 static void vxlan_create(if_ctx *ctx, struct ifreq *ifr) { vxlan_check_params(); ifr->ifr_data = (caddr_t) ¶ms; ifcreate_ioctl(ctx, ifr); } static void setvxlan_vni(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; u_long val; if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX) errx(1, "invalid network identifier: %s", arg); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_VNI; params.vxlp_vni = val; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_vni = val; if (do_cmd(ctx, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_VNI"); } static void setvxlan_local(if_ctx *ctx, const char *addr, int dummy __unused) { struct ifvxlancmd cmd; struct addrinfo *ai; struct sockaddr *sa; int error; bzero(&cmd, sizeof(cmd)); if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) errx(1, "error in parsing local address string: %s", gai_strerror(error)); sa = ai->ai_addr; switch (ai->ai_family) { #ifdef INET case AF_INET: { struct sockaddr_in *sin = satosin(sa); if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) errx(1, "local address cannot be multicast"); cmd.vxlcmd_sa.in4 = *sin; break; } #endif #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6 = satosin6(sa); if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) errx(1, "local address cannot be multicast"); cmd.vxlcmd_sa.in6 = *sin6; break; } #endif default: errx(1, "local address %s not supported", addr); } freeaddrinfo(ai); if (!vxlan_exists(ctx)) { if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR4; params.vxlp_local_sa.in4 = cmd.vxlcmd_sa.in4; } else { params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6; params.vxlp_local_sa.in6 = cmd.vxlcmd_sa.in6; } return; } if (do_cmd(ctx, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_LOCAL_ADDR"); } static void setvxlan_remote(if_ctx *ctx, const char *addr, int dummy __unused) { struct ifvxlancmd cmd; struct addrinfo *ai; struct sockaddr *sa; int error; bzero(&cmd, sizeof(cmd)); if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) errx(1, "error in parsing remote address string: %s", gai_strerror(error)); sa = ai->ai_addr; switch (ai->ai_family) { #ifdef INET case AF_INET: { struct sockaddr_in *sin = satosin(sa); if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) errx(1, "remote address cannot be multicast"); cmd.vxlcmd_sa.in4 = *sin; break; } #endif #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6 = satosin6(sa); if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) errx(1, "remote address cannot be multicast"); cmd.vxlcmd_sa.in6 = *sin6; break; } #endif default: errx(1, "remote address %s not supported", addr); } freeaddrinfo(ai); if (!vxlan_exists(ctx)) { if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4; params.vxlp_remote_sa.in4 = cmd.vxlcmd_sa.in4; } else { params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6; params.vxlp_remote_sa.in6 = cmd.vxlcmd_sa.in6; } return; } if (do_cmd(ctx, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_REMOTE_ADDR"); } static void setvxlan_group(if_ctx *ctx, const char *addr, int dummy __unused) { struct ifvxlancmd cmd; struct addrinfo *ai; struct sockaddr *sa; int error; bzero(&cmd, sizeof(cmd)); if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) errx(1, "error in parsing group address string: %s", gai_strerror(error)); sa = ai->ai_addr; switch (ai->ai_family) { #ifdef INET case AF_INET: { struct sockaddr_in *sin = satosin(sa); if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) errx(1, "group address must be multicast"); cmd.vxlcmd_sa.in4 = *sin; break; } #endif #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6 = satosin6(sa); if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) errx(1, "group address must be multicast"); cmd.vxlcmd_sa.in6 = *sin6; break; } #endif default: errx(1, "group address %s not supported", addr); } freeaddrinfo(ai); if (!vxlan_exists(ctx)) { if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4; params.vxlp_remote_sa.in4 = cmd.vxlcmd_sa.in4; } else { params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6; params.vxlp_remote_sa.in6 = cmd.vxlcmd_sa.in6; } return; } if (do_cmd(ctx, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_REMOTE_ADDR"); } static void setvxlan_local_port(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; u_long val; if (get_val(arg, &val) < 0 || val >= UINT16_MAX) errx(1, "invalid local port: %s", arg); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT; params.vxlp_local_port = val; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_port = val; if (do_cmd(ctx, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_LOCAL_PORT"); } static void setvxlan_remote_port(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; u_long val; if (get_val(arg, &val) < 0 || val >= UINT16_MAX) errx(1, "invalid remote port: %s", arg); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT; params.vxlp_remote_port = val; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_port = val; if (do_cmd(ctx, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_REMOTE_PORT"); } static void setvxlan_port_range(if_ctx *ctx, const char *arg1, const char *arg2) { struct ifvxlancmd cmd; u_long min, max; if (get_val(arg1, &min) < 0 || min >= UINT16_MAX) errx(1, "invalid port range minimum: %s", arg1); if (get_val(arg2, &max) < 0 || max >= UINT16_MAX) errx(1, "invalid port range maximum: %s", arg2); if (max < min) errx(1, "invalid port range"); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_PORT_RANGE; params.vxlp_min_port = min; params.vxlp_max_port = max; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_port_min = min; cmd.vxlcmd_port_max = max; if (do_cmd(ctx, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_PORT_RANGE"); } static void setvxlan_timeout(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0) errx(1, "invalid timeout value: %s", arg); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT; params.vxlp_ftable_timeout = val & 0xFFFFFFFF; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF; if (do_cmd(ctx, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT"); } static void setvxlan_maxaddr(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0) errx(1, "invalid maxaddr value: %s", arg); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX; params.vxlp_ftable_max = val & 0xFFFFFFFF; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF; if (do_cmd(ctx, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_FTABLE_MAX"); } static void setvxlan_dev(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_MULTICAST_IF; strlcpy(params.vxlp_mc_ifname, arg, sizeof(params.vxlp_mc_ifname)); return; } bzero(&cmd, sizeof(cmd)); strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname)); if (do_cmd(ctx, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_MULTICAST_IF"); } static void setvxlan_ttl(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifvxlancmd cmd; u_long val; if (get_val(arg, &val) < 0 || val > 256) errx(1, "invalid TTL value: %s", arg); if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_TTL; params.vxlp_ttl = val; return; } bzero(&cmd, sizeof(cmd)); cmd.vxlcmd_ttl = val; if (do_cmd(ctx, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_TTL"); } static void setvxlan_learn(if_ctx *ctx, const char *arg __unused, int d) { struct ifvxlancmd cmd; if (!vxlan_exists(ctx)) { params.vxlp_with |= VXLAN_PARAM_WITH_LEARN; params.vxlp_learn = d; return; } bzero(&cmd, sizeof(cmd)); if (d != 0) cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN; if (do_cmd(ctx, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_SET_LEARN"); } static void setvxlan_flush(if_ctx *ctx, const char *val __unused, int d) { struct ifvxlancmd cmd; bzero(&cmd, sizeof(cmd)); if (d != 0) cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL; if (do_cmd(ctx, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0) err(1, "VXLAN_CMD_FLUSH"); } static struct cmd vxlan_cmds[] = { DEF_CLONE_CMD_ARG("vni", setvxlan_vni), DEF_CLONE_CMD_ARG("vxlanid", setvxlan_vni), DEF_CLONE_CMD_ARG("vxlanlocal", setvxlan_local), DEF_CLONE_CMD_ARG("vxlanremote", setvxlan_remote), DEF_CLONE_CMD_ARG("vxlangroup", setvxlan_group), DEF_CLONE_CMD_ARG("vxlanlocalport", setvxlan_local_port), DEF_CLONE_CMD_ARG("vxlanremoteport", setvxlan_remote_port), DEF_CLONE_CMD_ARG2("vxlanportrange", setvxlan_port_range), DEF_CLONE_CMD_ARG("vxlantimeout", setvxlan_timeout), DEF_CLONE_CMD_ARG("vxlanmaxaddr", setvxlan_maxaddr), DEF_CLONE_CMD_ARG("vxlandev", setvxlan_dev), DEF_CLONE_CMD_ARG("vxlanttl", setvxlan_ttl), DEF_CLONE_CMD("vxlanlearn", 1, setvxlan_learn), DEF_CLONE_CMD("-vxlanlearn", 0, setvxlan_learn), DEF_CMD_ARG("vni", setvxlan_vni), DEF_CMD_ARG("vxlanid", setvxlan_vni), DEF_CMD_ARG("vxlanlocal", setvxlan_local), DEF_CMD_ARG("vxlanremote", setvxlan_remote), DEF_CMD_ARG("vxlangroup", setvxlan_group), DEF_CMD_ARG("vxlanlocalport", setvxlan_local_port), DEF_CMD_ARG("vxlanremoteport", setvxlan_remote_port), DEF_CMD_ARG2("vxlanportrange", setvxlan_port_range), DEF_CMD_ARG("vxlantimeout", setvxlan_timeout), DEF_CMD_ARG("vxlanmaxaddr", setvxlan_maxaddr), DEF_CMD_ARG("vxlandev", setvxlan_dev), DEF_CMD_ARG("vxlanttl", setvxlan_ttl), DEF_CMD("vxlanlearn", 1, setvxlan_learn), DEF_CMD("-vxlanlearn", 0, setvxlan_learn), DEF_CMD("vxlanflush", 0, setvxlan_flush), DEF_CMD("vxlanflushall", 1, setvxlan_flush), DEF_CMD("vxlanhwcsum", IFCAP_VXLAN_HWCSUM, setifcap), - DEF_CMD("-vxlanhwcsum", -IFCAP_VXLAN_HWCSUM, setifcap), + DEF_CMD("-vxlanhwcsum", IFCAP_VXLAN_HWCSUM, clearifcap), DEF_CMD("vxlanhwtso", IFCAP_VXLAN_HWTSO, setifcap), - DEF_CMD("-vxlanhwtso", -IFCAP_VXLAN_HWTSO, setifcap), + DEF_CMD("-vxlanhwtso", IFCAP_VXLAN_HWTSO, clearifcap), }; static struct afswtch af_vxlan = { .af_name = "af_vxlan", .af_af = AF_UNSPEC, .af_other_status = vxlan_status, }; static __constructor void vxlan_ctor(void) { size_t i; for (i = 0; i < nitems(vxlan_cmds); i++) cmd_register(&vxlan_cmds[i]); af_register(&af_vxlan); clone_setdefcallback_prefix("vxlan", vxlan_create); }