diff options
author | Jennifer Averett <jennifer.averett@oarcorp.com> | 2012-09-07 13:16:22 -0500 |
---|---|---|
committer | Jennifer Averett <jennifer.averett@oarcorp.com> | 2012-09-07 13:16:22 -0500 |
commit | ebbe3cc71f18159cf021192241a2a7a397fff4f8 (patch) | |
tree | 082e6ffb46b3526ca399ef8d761a188793ce21cc | |
parent | netstat: New command - Almost completely compiles (diff) | |
download | rtems-libbsd-ebbe3cc71f18159cf021192241a2a7a397fff4f8.tar.bz2 |
Added the ifconfig command.
26 files changed, 14689 insertions, 0 deletions
diff --git a/freebsd-userspace/Makefile b/freebsd-userspace/Makefile index 8e5cc2f9..8e8adee2 100644 --- a/freebsd-userspace/Makefile +++ b/freebsd-userspace/Makefile @@ -107,6 +107,35 @@ C_FILES += commands/sbin/dhclient/privsep.c C_FILES += commands/sbin/dhclient/tables.c C_FILES += commands/sbin/dhclient/tree.c +# ifconfig command sources +C_FILES += commands/sbin/ifconfig/af_atalk.c +C_FILES += commands/sbin/ifconfig/af_inet.c +C_FILES += commands/sbin/ifconfig/af_link.c +C_FILES += commands/sbin/ifconfig/ifbridge.c +C_FILES += commands/sbin/ifconfig/ifclone.c +C_FILES += commands/sbin/ifconfig/ifgif.c +C_FILES += commands/sbin/ifconfig/ifgroup.c +C_FILES += commands/sbin/ifconfig/iflagg.c +C_FILES += commands/sbin/ifconfig/ifmedia.c +C_FILES += commands/sbin/ifconfig/ifvlan.c +C_FILES += commands/sbin/ifconfig/af_inet6.c +C_FILES += commands/sbin/ifconfig/af_nd6.c +C_FILES += commands/sbin/ifconfig/ifcarp.c +C_FILES += commands/sbin/ifconfig/ifconfig.c +C_FILES += commands/sbin/ifconfig/ifgre.c +C_FILES += commands/sbin/ifconfig/ifieee80211.c +C_FILES += commands/sbin/ifconfig/ifmac.c +C_FILES += commands/sbin/ifconfig/ifpfsync.c + +# The following two files were left out to avoid +# porting issues. regdomain uses an xml parser +# that is not part of the standard release and +# af_ipx uses thread0 which we are trying to avoid +# pulling in. +# +# C_FILES += commands/sbin/ifconfig/regdomain.c +# C_FILES += commands/sbin/ifconfig/af_ipx.c + C_O_FILES = $(C_FILES:%.c=%.o) C_D_FILES = $(C_FILES:%.c=%.d) diff --git a/freebsd-userspace/commands/sbin/ifconfig/af_atalk.c b/freebsd-userspace/commands/sbin/ifconfig/af_atalk.c new file mode 100644 index 00000000..c50e0fd1 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/af_atalk.c @@ -0,0 +1,182 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <netatalk/at.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <arpa/inet.h> + +#include "ifconfig.h" + +static struct netrange at_nr; /* AppleTalk net range */ +static struct ifaliasreq at_addreq; + +/* XXX FIXME -- should use strtoul for better parsing. */ +static void +setatrange(const char *range, int dummy __unused, int s, + const struct afswtch *afp) +{ + u_int first = 123, last = 123; + + if (sscanf(range, "%u-%u", &first, &last) != 2 + || first == 0 || first > 0xffff + || last == 0 || last > 0xffff || first > last) + errx(1, "%s: illegal net range: %u-%u", range, first, last); + at_nr.nr_firstnet = htons(first); + at_nr.nr_lastnet = htons(last); +} + +static void +setatphase(const char *phase, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (!strcmp(phase, "1")) + at_nr.nr_phase = 1; + else if (!strcmp(phase, "2")) + at_nr.nr_phase = 2; + else + errx(1, "%s: illegal phase", phase); +} + +static void +at_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_at *sat, null_sat; + struct netrange *nr; + + memset(&null_sat, 0, sizeof(null_sat)); + + sat = (struct sockaddr_at *)ifa->ifa_addr; + if (sat == NULL) + return; + nr = &sat->sat_range.r_netrange; + printf("\tatalk %d.%d range %d-%d phase %d", + ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node, + ntohs(nr->nr_firstnet), ntohs(nr->nr_lastnet), nr->nr_phase); + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sat = (struct sockaddr_at *)ifa->ifa_dstaddr; + if (sat == NULL) + sat = &null_sat; + printf("--> %d.%d", + ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node); + } + if (ifa->ifa_flags & IFF_BROADCAST) { + sat = (struct sockaddr_at *)ifa->ifa_broadaddr; + if (sat != NULL) + printf(" broadcast %d.%d", + ntohs(sat->sat_addr.s_net), + sat->sat_addr.s_node); + } + + putchar('\n'); +} + +static void +at_getaddr(const char *addr, int which) +{ + struct sockaddr_at *sat = (struct sockaddr_at *) &at_addreq.ifra_addr; + u_int net, node; + + sat->sat_family = AF_APPLETALK; + sat->sat_len = sizeof(*sat); + if (which == MASK) + errx(1, "AppleTalk does not use netmasks"); + if (sscanf(addr, "%u.%u", &net, &node) != 2 + || net > 0xffff || node > 0xfe) + errx(1, "%s: illegal address", addr); + sat->sat_addr.s_net = htons(net); + sat->sat_addr.s_node = node; +} + +static void +at_postproc(int s, const struct afswtch *afp) +{ + struct sockaddr_at *sat = (struct sockaddr_at *) &at_addreq.ifra_addr; + + if (at_nr.nr_phase == 0) + at_nr.nr_phase = 2; /* Default phase 2 */ + if (at_nr.nr_firstnet == 0) + at_nr.nr_firstnet = /* Default range of one */ + at_nr.nr_lastnet = sat->sat_addr.s_net; + printf("\tatalk %d.%d range %d-%d phase %d\n", + ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node, + ntohs(at_nr.nr_firstnet), ntohs(at_nr.nr_lastnet), + at_nr.nr_phase); + if ((u_short) ntohs(at_nr.nr_firstnet) > + (u_short) ntohs(sat->sat_addr.s_net) + || (u_short) ntohs(at_nr.nr_lastnet) < + (u_short) ntohs(sat->sat_addr.s_net)) + errx(1, "AppleTalk address is not in range"); + sat->sat_range.r_netrange = at_nr; +} + +static struct cmd atalk_cmds[] = { + DEF_CMD_ARG("range", setatrange), + DEF_CMD_ARG("phase", setatphase), +}; + +static struct afswtch af_atalk = { + .af_name = "atalk", + .af_af = AF_APPLETALK, + .af_status = at_status, + .af_getaddr = at_getaddr, + .af_postproc = at_postproc, + .af_difaddr = SIOCDIFADDR, + .af_aifaddr = SIOCAIFADDR, + .af_ridreq = &at_addreq, + .af_addreq = &at_addreq, +}; + +static __constructor void +atalk_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(atalk_cmds); i++) + cmd_register(&atalk_cmds[i]); + af_register(&af_atalk); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/af_inet.c b/freebsd-userspace/commands/sbin/ifconfig/af_inet.c new file mode 100644 index 00000000..65488a35 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/af_inet.c @@ -0,0 +1,213 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <netinet/in.h> +#ifdef __rtems__ +#include <freebsd/net/if_var.h> /* for struct ifaddr */ +#include <freebsd/netinet/in_var.h> +#else +#include <net/if_var.h> /* for struct ifaddr */ +#include <netinet/in_var.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> + +#include "ifconfig.h" + +#ifdef __rtems__ +#include <freebsd/sys/sockio.h> +#endif + +static struct in_aliasreq in_addreq; +static struct ifreq in_ridreq; + +static void +in_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_in *sin, null_sin; + + memset(&null_sin, 0, sizeof(null_sin)); + + sin = (struct sockaddr_in *)ifa->ifa_addr; + if (sin == NULL) + return; + + printf("\tinet %s ", inet_ntoa(sin->sin_addr)); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sin = (struct sockaddr_in *)ifa->ifa_dstaddr; + if (sin == NULL) + sin = &null_sin; + printf("--> %s ", inet_ntoa(sin->sin_addr)); + } + + sin = (struct sockaddr_in *)ifa->ifa_netmask; + if (sin == NULL) + sin = &null_sin; + printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr)); + + if (ifa->ifa_flags & IFF_BROADCAST) { + sin = (struct sockaddr_in *)ifa->ifa_broadaddr; + if (sin != NULL && sin->sin_addr.s_addr != 0) + printf("broadcast %s", inet_ntoa(sin->sin_addr)); + } + putchar('\n'); +} + +#define SIN(x) ((struct sockaddr_in *) &(x)) +static struct sockaddr_in *sintab[] = { + SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), + SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) +}; + +static void +in_getaddr(const char *s, int which) +{ +#define MIN(a,b) ((a)<(b)?(a):(b)) + struct sockaddr_in *sin = sintab[which]; + struct hostent *hp; + struct netent *np; + + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + + if (which == ADDR) { + char *p = NULL; + + if((p = strrchr(s, '/')) != NULL) { + const char *errstr; + /* address is `name/masklen' */ + int masklen; + struct sockaddr_in *min = sintab[MASK]; + *p = '\0'; + if (!isdigit(*(p + 1))) + errstr = "invalid"; + else + masklen = (int)strtonum(p + 1, 0, 32, &errstr); + if (errstr != NULL) { + *p = '/'; + errx(1, "%s: bad value (width %s)", s, errstr); + } + min->sin_len = sizeof(*min); + min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & + 0xffffffff); + } + } + + if (inet_aton(s, &sin->sin_addr)) + return; + if ((hp = gethostbyname(s)) != 0) + bcopy(hp->h_addr, (char *)&sin->sin_addr, + MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); + else if ((np = getnetbyname(s)) != 0) + sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); + else + errx(1, "%s: bad value", s); +#undef MIN +} + +static void +in_status_tunnel(int s) +{ + char src[NI_MAXHOST]; + char dst[NI_MAXHOST]; + struct ifreq ifr; + const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, IFNAMSIZ); + + if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) + return; + if (sa->sa_family != AF_INET) + return; + if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) + src[0] = '\0'; + + if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) + return; + if (sa->sa_family != AF_INET) + return; + if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) + dst[0] = '\0'; + + printf("\ttunnel inet %s --> %s\n", src, dst); +} + +static void +in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) +{ + struct in_aliasreq addreq; + + memset(&addreq, 0, sizeof(addreq)); + strncpy(addreq.ifra_name, name, IFNAMSIZ); + memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); + memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); + + if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0) + warn("SIOCSIFPHYADDR"); +} + +static struct afswtch af_inet = { + .af_name = "inet", + .af_af = AF_INET, + .af_status = in_status, + .af_getaddr = in_getaddr, + .af_status_tunnel = in_status_tunnel, + .af_settunnel = in_set_tunnel, + .af_difaddr = SIOCDIFADDR, + .af_aifaddr = SIOCAIFADDR, + .af_ridreq = &in_ridreq, + .af_addreq = &in_addreq, +}; + +static __constructor void +inet_ctor(void) +{ + af_register(&af_inet); +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/af_inet6.c b/freebsd-userspace/commands/sbin/ifconfig/af_inet6.c new file mode 100644 index 00000000..c09c778c --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/af_inet6.c @@ -0,0 +1,556 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <arpa/inet.h> + +#include <netinet/in.h> +#ifdef __rtems__ +#include <freebsd/net/if_var.h> /* for struct ifaddr */ +#include <freebsd/netinet/in_var.h> +#else +#include <net/if_var.h> /* for struct ifaddr */ +#include <netinet/in_var.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> + +#ifdef __rtems__ +#include <freebsd/netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */ +#else +#include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */ +#endif + +#include "ifconfig.h" + +static struct in6_ifreq in6_ridreq; +static struct in6_aliasreq in6_addreq = + { .ifra_flags = 0, + .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; +static int ip6lifetime; + +static void in6_fillscopeid(struct sockaddr_in6 *sin6); +static int prefix(void *, int); +static char *sec2str(time_t); +static int explicit_prefix = 0; + +extern void setnd6flags(const char *, int, int, const struct afswtch *); +extern void setnd6defif(const char *, int, int, const struct afswtch *); + +static char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/ + +static void +setifprefixlen(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getprefix != NULL) + afp->af_getprefix(addr, MASK); + explicit_prefix = 1; +} + +static void +setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused, + const struct afswtch *afp) +{ + if (afp->af_af != AF_INET6) + err(1, "address flags can be set only for inet6 addresses"); + + if (flag < 0) + in6_addreq.ifra_flags &= ~(-flag); + else + in6_addreq.ifra_flags |= flag; +} + +static void +setip6lifetime(const char *cmd, const char *val, int s, + const struct afswtch *afp) +{ + time_t newval, t; + char *ep; + + t = time(NULL); + newval = (time_t)strtoul(val, &ep, 0); + if (val == ep) + errx(1, "invalid %s", cmd); + if (afp->af_af != AF_INET6) + errx(1, "%s not allowed for the AF", cmd); + if (strcmp(cmd, "vltime") == 0) { + in6_addreq.ifra_lifetime.ia6t_expire = t + newval; + in6_addreq.ifra_lifetime.ia6t_vltime = newval; + } else if (strcmp(cmd, "pltime") == 0) { + in6_addreq.ifra_lifetime.ia6t_preferred = t + newval; + in6_addreq.ifra_lifetime.ia6t_pltime = newval; + } +} + +static void +setip6pltime(const char *seconds, int dummy __unused, int s, + const struct afswtch *afp) +{ + setip6lifetime("pltime", seconds, s, afp); +} + +static void +setip6vltime(const char *seconds, int dummy __unused, int s, + const struct afswtch *afp) +{ + setip6lifetime("vltime", seconds, s, afp); +} + +static void +setip6eui64(const char *cmd, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifaddrs *ifap, *ifa; + const struct sockaddr_in6 *sin6 = NULL; + const struct in6_addr *lladdr = NULL; + struct in6_addr *in6; + + if (afp->af_af != AF_INET6) + errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); + in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; + if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) + errx(EXIT_FAILURE, "interface index is already filled"); + if (getifaddrs(&ifap) != 0) + err(EXIT_FAILURE, "getifaddrs"); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family == AF_INET6 && + strcmp(ifa->ifa_name, name) == 0) { + sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + lladdr = &sin6->sin6_addr; + break; + } + } + } + if (!lladdr) + errx(EXIT_FAILURE, "could not determine link local address"); + + memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); + + freeifaddrs(ifap); +} + +static void +in6_fillscopeid(struct sockaddr_in6 *sin6) +{ +#if defined(__KAME__) && defined(KAME_SCOPEID) + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; + } +#endif +} + +static void +in6_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_in6 *sin, null_sin; + struct in6_ifreq ifr6; + int s6; + u_int32_t flags6; + struct in6_addrlifetime lifetime; + time_t t = time(NULL); + int error; + u_int32_t scopeid; + + memset(&null_sin, 0, sizeof(null_sin)); + + sin = (struct sockaddr_in6 *)ifa->ifa_addr; + if (sin == NULL) + return; + + strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + warn("socket(AF_INET6,SOCK_DGRAM)"); + return; + } + ifr6.ifr_addr = *sin; + if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + warn("ioctl(SIOCGIFAFLAG_IN6)"); + close(s6); + return; + } + flags6 = ifr6.ifr_ifru.ifru_flags6; + memset(&lifetime, 0, sizeof(lifetime)); + ifr6.ifr_addr = *sin; + if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { + warn("ioctl(SIOCGIFALIFETIME_IN6)"); + close(s6); + return; + } + lifetime = ifr6.ifr_ifru.ifru_lifetime; + close(s6); + + /* XXX: embedded link local addr check */ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && + *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { + u_short index; + + index = *(u_short *)&sin->sin6_addr.s6_addr[2]; + *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = ntohs(index); + } + scopeid = sin->sin6_scope_id; + + error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, + sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); + if (error != 0) + inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, + sizeof(addr_buf)); + printf("\tinet6 %s ", addr_buf); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr; + /* + * some of the interfaces do not have valid destination + * address. + */ + if (sin != NULL && sin->sin6_family == AF_INET6) { + int error; + + /* XXX: embedded link local addr check */ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && + *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { + u_short index; + + index = *(u_short *)&sin->sin6_addr.s6_addr[2]; + *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = ntohs(index); + } + + error = getnameinfo((struct sockaddr *)sin, + sin->sin6_len, addr_buf, + sizeof(addr_buf), NULL, 0, + NI_NUMERICHOST); + if (error != 0) + inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, + sizeof(addr_buf)); + printf("--> %s ", addr_buf); + } + } + + sin = (struct sockaddr_in6 *)ifa->ifa_netmask; + if (sin == NULL) + sin = &null_sin; + printf("prefixlen %d ", prefix(&sin->sin6_addr, + sizeof(struct in6_addr))); + + if ((flags6 & IN6_IFF_ANYCAST) != 0) + printf("anycast "); + if ((flags6 & IN6_IFF_TENTATIVE) != 0) + printf("tentative "); + if ((flags6 & IN6_IFF_DUPLICATED) != 0) + printf("duplicated "); + if ((flags6 & IN6_IFF_DETACHED) != 0) + printf("detached "); + if ((flags6 & IN6_IFF_DEPRECATED) != 0) + printf("deprecated "); + if ((flags6 & IN6_IFF_AUTOCONF) != 0) + printf("autoconf "); + if ((flags6 & IN6_IFF_TEMPORARY) != 0) + printf("temporary "); + + if (scopeid) + printf("scopeid 0x%x ", scopeid); + + if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { + printf("pltime "); + if (lifetime.ia6t_preferred) { + printf("%s ", lifetime.ia6t_preferred < t + ? "0" : sec2str(lifetime.ia6t_preferred - t)); + } else + printf("infty "); + + printf("vltime "); + if (lifetime.ia6t_expire) { + printf("%s ", lifetime.ia6t_expire < t + ? "0" : sec2str(lifetime.ia6t_expire - t)); + } else + printf("infty "); + } + + putchar('\n'); +} + +#define SIN6(x) ((struct sockaddr_in6 *) &(x)) +static struct sockaddr_in6 *sin6tab[] = { + SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), + SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr) +}; + +static void +in6_getprefix(const char *plen, int which) +{ + struct sockaddr_in6 *sin = sin6tab[which]; + u_char *cp; + int len = atoi(plen); + + if ((len < 0) || (len > 128)) + errx(1, "%s: bad value", plen); + sin->sin6_len = sizeof(*sin); + if (which != MASK) + sin->sin6_family = AF_INET6; + if ((len == 0) || (len == 128)) { + memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); + return; + } + memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); + for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) + *cp++ = 0xff; + *cp = 0xff << (8 - len); +} + +static void +in6_getaddr(const char *s, int which) +{ + struct sockaddr_in6 *sin = sin6tab[which]; + struct addrinfo hints, *res; + int error = -1; + + newaddr &= 1; + + sin->sin6_len = sizeof(*sin); + if (which != MASK) + sin->sin6_family = AF_INET6; + + if (which == ADDR) { + char *p = NULL; + if((p = strrchr(s, '/')) != NULL) { + *p = '\0'; + in6_getprefix(p + 1, MASK); + explicit_prefix = 1; + } + } + + if (sin->sin6_family == AF_INET6) { + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + error = getaddrinfo(s, NULL, &hints, &res); + } + if (error != 0) { + if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) + errx(1, "%s: bad value", s); + } else + bcopy(res->ai_addr, sin, res->ai_addrlen); +} + +static int +prefix(void *val, int size) +{ + u_char *name = (u_char *)val; + int byte, bit, plen = 0; + + for (byte = 0; byte < size; byte++, plen += 8) + if (name[byte] != 0xff) + break; + if (byte == size) + return (plen); + for (bit = 7; bit != 0; bit--, plen++) + if (!(name[byte] & (1 << bit))) + break; + for (; bit != 0; bit--) + if (name[byte] & (1 << bit)) + return(0); + byte++; + for (; byte < size; byte++) + if (name[byte]) + return(0); + return (plen); +} + +static char * +sec2str(time_t total) +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + if (0) { + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += sprintf(p, "%dd", days); + } + if (!first || hours) { + first = 0; + p += sprintf(p, "%dh", hours); + } + if (!first || mins) { + first = 0; + p += sprintf(p, "%dm", mins); + } + sprintf(p, "%ds", secs); + } else + sprintf(result, "%lu", (unsigned long)total); + + return(result); +} + +static void +in6_postproc(int s, const struct afswtch *afp) +{ + if (explicit_prefix == 0) { + /* Aggregatable address architecture defines all prefixes + are 64. So, it is convenient to set prefixlen to 64 if + it is not specified. */ + setifprefixlen("64", 0, s, afp); + /* in6_getprefix("64", MASK) if MASK is available here... */ + } +} + +static void +in6_status_tunnel(int s) +{ + char src[NI_MAXHOST]; + char dst[NI_MAXHOST]; + struct in6_ifreq in6_ifr; + const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; + + memset(&in6_ifr, 0, sizeof(in6_ifr)); + strncpy(in6_ifr.ifr_name, name, IFNAMSIZ); + + if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) + return; + if (sa->sa_family != AF_INET6) + return; + in6_fillscopeid(&in6_ifr.ifr_addr); + if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, + NI_NUMERICHOST) != 0) + src[0] = '\0'; + + if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) + return; + if (sa->sa_family != AF_INET6) + return; + in6_fillscopeid(&in6_ifr.ifr_addr); + if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, + NI_NUMERICHOST) != 0) + dst[0] = '\0'; + + printf("\ttunnel inet6 %s --> %s\n", src, dst); +} + +static void +in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) +{ + struct in6_aliasreq in6_addreq; + + memset(&in6_addreq, 0, sizeof(in6_addreq)); + strncpy(in6_addreq.ifra_name, name, IFNAMSIZ); + memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); + memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, + dstres->ai_addr->sa_len); + + if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) + warn("SIOCSIFPHYADDR_IN6"); +} + +static struct cmd inet6_cmds[] = { + DEF_CMD_ARG("prefixlen", setifprefixlen), + DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), + DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), + DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), + DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), + DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), + DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), + DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), + DEF_CMD("defaultif", 1, setnd6defif), + DEF_CMD("-defaultif", -1, setnd6defif), + DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), + DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), + DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), + DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), + DEF_CMD("prefer_source",ND6_IFF_PREFER_SOURCE, setnd6flags), + DEF_CMD("-prefer_source",-ND6_IFF_PREFER_SOURCE,setnd6flags), + DEF_CMD_ARG("pltime", setip6pltime), + DEF_CMD_ARG("vltime", setip6vltime), + DEF_CMD("eui64", 0, setip6eui64), +}; + +static struct afswtch af_inet6 = { + .af_name = "inet6", + .af_af = AF_INET6, + .af_status = in6_status, + .af_getaddr = in6_getaddr, + .af_getprefix = in6_getprefix, + .af_postproc = in6_postproc, + .af_status_tunnel = in6_status_tunnel, + .af_settunnel = in6_set_tunnel, + .af_difaddr = SIOCDIFADDR_IN6, + .af_aifaddr = SIOCAIFADDR_IN6, + .af_ridreq = &in6_addreq, + .af_addreq = &in6_addreq, +}; + +static void +in6_Lopt_cb(const char *optarg __unused) +{ + ip6lifetime++; /* print IPv6 address lifetime */ +} +static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; + +static __constructor void +inet6_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(inet6_cmds); i++) + cmd_register(&inet6_cmds[i]); + af_register(&af_inet6); + opt_register(&in6_Lopt); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/af_ipx.c b/freebsd-userspace/commands/sbin/ifconfig/af_ipx.c new file mode 100644 index 00000000..c7b718aa --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/af_ipx.c @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ifaddrs.h> + +#ifdef __rtems__ +#include <freebsd/net/if_var.h> +#else +#include <net/if_var.h> +#endif +#define IPTUNNEL +#include <netipx/ipx.h> +#include <netipx/ipx_if.h> + +#include "ifconfig.h" + +static struct ifaliasreq ipx_addreq; +static struct ifreq ipx_ridreq; + +static void +ipx_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_ipx *sipx, null_sipx; + + sipx = (struct sockaddr_ipx *)ifa->ifa_addr; + if (sipx == NULL) + return; + + printf("\tipx %s ", ipx_ntoa(sipx->sipx_addr)); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sipx = (struct sockaddr_ipx *)ifa->ifa_dstaddr; + if (sipx == NULL) { + memset(&null_sipx, 0, sizeof(null_sipx)); + sipx = &null_sipx; + } + printf("--> %s ", ipx_ntoa(sipx->sipx_addr)); + } + putchar('\n'); +} + +#define SIPX(x) ((struct sockaddr_ipx *) &(x)) +struct sockaddr_ipx *sipxtab[] = { + SIPX(ipx_ridreq.ifr_addr), SIPX(ipx_addreq.ifra_addr), + SIPX(ipx_addreq.ifra_mask), SIPX(ipx_addreq.ifra_broadaddr) +}; + +static void +ipx_getaddr(const char *addr, int which) +{ + struct sockaddr_ipx *sipx = sipxtab[which]; + + sipx->sipx_family = AF_IPX; + sipx->sipx_len = sizeof(*sipx); + sipx->sipx_addr = ipx_addr(addr); + if (which == MASK) + printf("Attempt to set IPX netmask will be ineffectual\n"); +} + +static void +ipx_postproc(int s, const struct afswtch *afp) +{ + +} + +static struct afswtch af_ipx = { + .af_name = "ipx", + .af_af = AF_IPX, + .af_status = ipx_status, + .af_getaddr = ipx_getaddr, + .af_postproc = ipx_postproc, + .af_difaddr = SIOCDIFADDR, + .af_aifaddr = SIOCAIFADDR, + .af_ridreq = &ipx_ridreq, + .af_addreq = &ipx_addreq, +}; + +static __constructor void +ipx_ctor(void) +{ + af_register(&af_ipx); +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/af_link.c b/freebsd-userspace/commands/sbin/ifconfig/af_link.c new file mode 100644 index 00000000..2de85045 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/af_link.c @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ifaddrs.h> + +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/ethernet.h> + +#include "ifconfig.h" + +#ifdef __rtems__ +#include <freebsd/sys/sockio.h> +#endif + +static struct ifreq link_ridreq; + +static void +link_status(int s __unused, const struct ifaddrs *ifa) +{ + /* XXX no const 'cuz LLADDR is defined wrong */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr; + + if (sdl != NULL && sdl->sdl_alen > 0) { + if ((sdl->sdl_type == IFT_ETHER || + sdl->sdl_type == IFT_L2VLAN || + sdl->sdl_type == IFT_BRIDGE) && + sdl->sdl_alen == ETHER_ADDR_LEN) + printf("\tether %s\n", + ether_ntoa((struct ether_addr *)LLADDR(sdl))); + else { + int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; + + printf("\tlladdr %s\n", link_ntoa(sdl) + n); + } + } +} + +static void +link_getaddr(const char *addr, int which) +{ + char *temp; + struct sockaddr_dl sdl; + struct sockaddr *sa = &link_ridreq.ifr_addr; + + if (which != ADDR) + errx(1, "can't set link-level netmask or broadcast"); + if ((temp = malloc(strlen(addr) + 2)) == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strcpy(temp + 1, addr); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen > sizeof(sa->sa_data)) + errx(1, "malformed link-level address"); + sa->sa_family = AF_LINK; + sa->sa_len = sdl.sdl_alen; + bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); +} + +static struct afswtch af_link = { + .af_name = "link", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; +static struct afswtch af_ether = { + .af_name = "ether", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; +static struct afswtch af_lladdr = { + .af_name = "lladdr", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; + +static __constructor void +link_ctor(void) +{ + af_register(&af_link); + af_register(&af_ether); + af_register(&af_lladdr); +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/af_nd6.c b/freebsd-userspace/commands/sbin/ifconfig/af_nd6.c new file mode 100644 index 00000000..74820732 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/af_nd6.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2009 Hiroki Sato. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <net/if.h> +#include <net/route.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ifaddrs.h> + +#include <arpa/inet.h> + +#include <netinet/in.h> +#ifdef __rtems__ +#include <freebsd/net/if_var.h> +#include <freebsd/netinet/in_var.h> +#else +#include <net/if_var.h> +#include <netinet/in_var.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> + +#ifdef __rtems__ +#include <freebsd/netinet6/nd6.h> +#else +#include <netinet6/nd6.h> +#endif + +#include "ifconfig.h" + +#define MAX_SYSCTL_TRY 5 +#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ + "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ + "\020DEFAULTIF" + +static int isnd6defif(int); +void setnd6flags(const char *, int, int, const struct afswtch *); +void setnd6defif(const char *, int, int, const struct afswtch *); + +void +setnd6flags(const char *dummyaddr __unused, + int d, int s, + const struct afswtch *afp) +{ + struct in6_ndireq nd; + int error; + + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname)); + error = ioctl(s, SIOCGIFINFO_IN6, &nd); + if (error) { + warn("ioctl(SIOCGIFINFO_IN6)"); + return; + } + if (d < 0) + nd.ndi.flags &= ~(-d); + else + nd.ndi.flags |= d; + error = ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd); + if (error) + warn("ioctl(SIOCSIFINFO_IN6)"); +} + +void +setnd6defif(const char *dummyaddr __unused, + int d, int s, + const struct afswtch *afp) +{ + struct in6_ndifreq ndifreq; + int ifindex; + int error; + + memset(&ndifreq, 0, sizeof(ndifreq)); + strncpy(ndifreq.ifname, ifr.ifr_name, sizeof(ndifreq.ifname)); + + if (d < 0) { + if (isnd6defif(s)) { + /* ifindex = 0 means to remove default if */ + ifindex = 0; + } else + return; + } else if ((ifindex = if_nametoindex(ndifreq.ifname)) == 0) { + warn("if_nametoindex(%s)", ndifreq.ifname); + return; + } + + ndifreq.ifindex = ifindex; + error = ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq); + if (error) + warn("ioctl(SIOCSDEFIFACE_IN6)"); +} + +static int +isnd6defif(int s) +{ + struct in6_ndifreq ndifreq; + unsigned int ifindex; + int error; + + memset(&ndifreq, 0, sizeof(ndifreq)); + strncpy(ndifreq.ifname, ifr.ifr_name, sizeof(ndifreq.ifname)); + + ifindex = if_nametoindex(ndifreq.ifname); + error = ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq); + if (error) { + warn("ioctl(SIOCGDEFIFACE_IN6)"); + return (error); + } + return (ndifreq.ifindex == ifindex); +} + +static void +nd6_status(int s) +{ + struct in6_ndireq nd; + struct rt_msghdr *rtm; + size_t needed; + char *buf, *next; + int mib[6], ntry; + int s6; + int error; + int isinet6, isdefif; + + /* Check if the interface has at least one IPv6 address. */ + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + mib[4] = NET_RT_IFLIST; + mib[5] = if_nametoindex(ifr.ifr_name); + + /* Try to prevent a race between two sysctls. */ + ntry = 0; + do { + error = sysctl(mib, 6, NULL, &needed, NULL, 0); + if (error) { + warn("sysctl(NET_RT_IFLIST)/estimate"); + return; + } + buf = malloc(needed); + if (buf == NULL) { + warn("malloc for sysctl(NET_RT_IFLIST) failed"); + return; + } + if ((error = sysctl(mib, 6, buf, &needed, NULL, 0)) < 0) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + warn("sysctl(NET_RT_IFLIST)/get"); + free(buf); + return; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + isinet6 = 0; + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + + if (rtm->rtm_version != RTM_VERSION) + continue; + if (rtm->rtm_type == RTM_NEWADDR) { + isinet6 = 1; + break; + } + } + free(buf); + if (!isinet6) + return; + + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname)); + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + warn("socket(AF_INET6, SOCK_DGRAM)"); + return; + } + error = ioctl(s6, SIOCGIFINFO_IN6, &nd); + if (error) { + warn("ioctl(SIOCGIFINFO_IN6)"); + close(s6); + return; + } + isdefif = isnd6defif(s6); + close(s6); + if (nd.ndi.flags == 0 && !isdefif) + return; + printb("\tnd6 options", + (unsigned int)(nd.ndi.flags | (isdefif << 15)), ND6BITS); + putchar('\n'); +} + +static struct afswtch af_nd6 = { + .af_name = "nd6", + .af_af = AF_LOCAL, + .af_other_status= nd6_status, +}; + +static __constructor void +nd6_ctor(void) +{ + af_register(&af_nd6); +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifbridge.c b/freebsd-userspace/commands/sbin/ifconfig/ifbridge.c new file mode 100644 index 00000000..049a0e7c --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifbridge.c @@ -0,0 +1,767 @@ +/*- + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, 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 for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/net/if_bridgevar.h> +#else +#include <net/if_bridgevar.h> +#endif +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +#ifdef __rtems__ +#include <freebsd/sys/sockio.h> +#endif + +#define PV2ID(pv, epri, eaddr) do { \ + epri = pv >> 48; \ + eaddr[0] = pv >> 40; \ + eaddr[1] = pv >> 32; \ + eaddr[2] = pv >> 24; \ + eaddr[3] = pv >> 16; \ + eaddr[4] = pv >> 8; \ + eaddr[5] = pv >> 0; \ +} while (0) + +static const char *stpstates[] = { + "disabled", + "listening", + "learning", + "forwarding", + "blocking", + "discarding" +}; +static const char *stpproto[] = { + "stp", + "-", + "rstp" +}; +static const char *stproles[] = { + "disabled", + "root", + "designated", + "alternate", + "backup" +}; + +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(int sock, u_long op, void *arg, size_t argsize, int set) +{ + struct ifdrv ifd; + + memset(&ifd, 0, sizeof(ifd)); + + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = op; + ifd.ifd_len = argsize; + ifd.ifd_data = arg; + + return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); +} + +static void +do_bridgeflag(int sock, const char *ifs, int flag, int set) +{ + struct ifbreq req; + + strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); + + if (do_cmd(sock, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) + err(1, "unable to get bridge flags"); + + if (set) + req.ifbr_ifsflags |= flag; + else + req.ifbr_ifsflags &= ~flag; + + if (do_cmd(sock, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) + err(1, "unable to set bridge flags"); +} + +static void +bridge_interfaces(int s, const char *prefix) +{ + struct ifbifconf bifc; + struct ifbreq *req; + char *inbuf = NULL, *ninbuf; + char *p, *pad; + int i, len = 8192; + + pad = strdup(prefix); + if (pad == NULL) + err(1, "strdup"); + /* replace the prefix with whitespace */ + for (p = pad; *p != '\0'; p++) { + if(isprint(*p)) + *p = ' '; + } + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate interface buffer"); + bifc.ifbic_len = len; + bifc.ifbic_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGGIFS, &bifc, sizeof(bifc), 0) < 0) + err(1, "unable to get interface list"); + if ((bifc.ifbic_len + sizeof(*req)) < len) + break; + len *= 2; + } + + for (i = 0; i < bifc.ifbic_len / sizeof(*req); i++) { + req = bifc.ifbic_req + i; + printf("%s%s ", prefix, req->ifbr_ifsname); + printb("flags", req->ifbr_ifsflags, IFBIFBITS); + printf("\n"); + + printf("%s", pad); + printf("ifmaxaddr %u", req->ifbr_addrmax); + printf(" port %u priority %u", req->ifbr_portno, + req->ifbr_priority); + printf(" path cost %u", req->ifbr_path_cost); + + if (req->ifbr_ifsflags & IFBIF_STP) { + if (req->ifbr_proto < + sizeof(stpproto) / sizeof(stpproto[0])) + printf(" proto %s", stpproto[req->ifbr_proto]); + else + printf(" <unknown proto %d>", + req->ifbr_proto); + + printf("\n%s", pad); + if (req->ifbr_role < + sizeof(stproles) / sizeof(stproles[0])) + printf("role %s", stproles[req->ifbr_role]); + else + printf("<unknown role %d>", + req->ifbr_role); + if (req->ifbr_state < + sizeof(stpstates) / sizeof(stpstates[0])) + printf(" state %s", stpstates[req->ifbr_state]); + else + printf(" <unknown state %d>", + req->ifbr_state); + } + printf("\n"); + } + + free(inbuf); +} + +static void +bridge_addresses(int s, const char *prefix) +{ + struct ifbaconf ifbac; + struct ifbareq *ifba; + char *inbuf = NULL, *ninbuf; + int i, len = 8192; + struct ether_addr ea; + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate address buffer"); + ifbac.ifbac_len = len; + ifbac.ifbac_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) + err(1, "unable to get address cache"); + if ((ifbac.ifbac_len + sizeof(*ifba)) < len) + break; + len *= 2; + } + + for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { + ifba = ifbac.ifbac_req + i; + memcpy(ea.octet, ifba->ifba_dst, + sizeof(ea.octet)); + printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea), + ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire); + printb("flags", ifba->ifba_flags, IFBAFBITS); + printf("\n"); + } + + free(inbuf); +} + +static void +bridge_status(int s) +{ + struct ifbropreq ifbp; + struct ifbrparam param; + u_int16_t pri; + u_int8_t ht, fd, ma, hc, pro; + u_int8_t lladdr[ETHER_ADDR_LEN]; + u_int16_t bprio; + u_int32_t csize, ctime; + + if (do_cmd(s, BRDGGCACHE, ¶m, sizeof(param), 0) < 0) + return; + csize = param.ifbrp_csize; + if (do_cmd(s, BRDGGTO, ¶m, sizeof(param), 0) < 0) + return; + ctime = param.ifbrp_ctime; + if (do_cmd(s, BRDGPARAM, &ifbp, sizeof(ifbp), 0) < 0) + return; + pri = ifbp.ifbop_priority; + pro = ifbp.ifbop_protocol; + ht = ifbp.ifbop_hellotime; + fd = ifbp.ifbop_fwddelay; + hc = ifbp.ifbop_holdcount; + ma = ifbp.ifbop_maxage; + + PV2ID(ifbp.ifbop_bridgeid, bprio, lladdr); + printf("\tid %s priority %u hellotime %u fwddelay %u\n", + ether_ntoa((struct ether_addr *)lladdr), pri, ht, fd); + printf("\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n", + ma, hc, stpproto[pro], csize, ctime); + + PV2ID(ifbp.ifbop_designated_root, bprio, lladdr); + printf("\troot id %s priority %d ifcost %u port %u\n", + ether_ntoa((struct ether_addr *)lladdr), bprio, + ifbp.ifbop_root_path_cost, ifbp.ifbop_root_port & 0xfff); + + bridge_interfaces(s, "\tmember: "); + + return; + +} + +static void +setbridge_add(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADD, &req, sizeof(req), 1) < 0) + err(1, "BRDGADD %s", val); +} + +static void +setbridge_delete(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDEL, &req, sizeof(req), 1) < 0) + err(1, "BRDGDEL %s", val); +} + +static void +setbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 1); +} + +static void +unsetbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 0); +} + +static void +setbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 1); +} + +static void +unsetbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 0); +} + +static void +setbridge_sticky(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STICKY, 1); +} + +static void +unsetbridge_sticky(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STICKY, 0); +} + +static void +setbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADDS, &req, sizeof(req), 1) < 0) + err(1, "BRDGADDS %s", val); +} + +static void +unsetbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDELS, &req, sizeof(req), 1) < 0) + err(1, "BRDGDELS %s", val); +} + +static void +setbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 1); +} + +static void +unsetbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 0); +} + +static void +setbridge_edge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_EDGE, 1); +} + +static void +unsetbridge_edge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_EDGE, 0); +} + +static void +setbridge_autoedge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOEDGE, 1); +} + +static void +unsetbridge_autoedge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOEDGE, 0); +} + +static void +setbridge_ptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_PTP, 1); +} + +static void +unsetbridge_ptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_PTP, 0); +} + +static void +setbridge_autoptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOPTP, 1); +} + +static void +unsetbridge_autoptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOPTP, 0); +} + +static void +setbridge_flush(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHDYN; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_flushall(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHALL; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_static(const char *val, const char *mac, int s, + const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname)); + + ea = ether_aton(mac); + if (ea == NULL) + errx(1, "%s: invalid address: %s", val, mac); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + req.ifba_flags = IFBAF_STATIC; + req.ifba_vlan = 1; /* XXX allow user to specify */ + + if (do_cmd(s, BRDGSADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGSADDR %s", val); +} + +static void +setbridge_deladdr(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + + ea = ether_aton(val); + if (ea == NULL) + errx(1, "invalid address: %s", val); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + + if (do_cmd(s, BRDGDADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGDADDR %s", val); +} + +static void +setbridge_addr(const char *val, int d, int s, const struct afswtch *afp) +{ + + bridge_addresses(s, ""); +} + +static void +setbridge_maxaddr(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_csize = val & 0xffffffff; + + if (do_cmd(s, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSCACHE %s", arg); +} + +static void +setbridge_hellotime(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_hellotime = val & 0xff; + + if (do_cmd(s, BRDGSHT, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSHT %s", arg); +} + +static void +setbridge_fwddelay(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_fwddelay = val & 0xff; + + if (do_cmd(s, BRDGSFD, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSFD %s", arg); +} + +static void +setbridge_maxage(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_maxage = val & 0xff; + + if (do_cmd(s, BRDGSMA, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSMA %s", arg); +} + +static void +setbridge_priority(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_prio = val & 0xffff; + + if (do_cmd(s, BRDGSPRI, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPRI %s", arg); +} + +static void +setbridge_protocol(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + + if (strcasecmp(arg, "stp") == 0) { + param.ifbrp_proto = 0; + } else if (strcasecmp(arg, "rstp") == 0) { + param.ifbrp_proto = 2; + } else { + errx(1, "unknown stp protocol"); + } + + if (do_cmd(s, BRDGSPROTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPROTO %s", arg); +} + +static void +setbridge_holdcount(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_txhc = val & 0xff; + + if (do_cmd(s, BRDGSTXHC, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTXHC %s", arg); +} + +static void +setbridge_ifpriority(const char *ifn, const char *pri, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", pri); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_priority = val & 0xff; + + if (do_cmd(s, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFPRIO %s", pri); +} + +static void +setbridge_ifpathcost(const char *ifn, const char *cost, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(cost, &val) < 0) + errx(1, "invalid value: %s", cost); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_path_cost = val; + + if (do_cmd(s, BRDGSIFCOST, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFCOST %s", cost); +} + +static void +setbridge_ifmaxaddr(const char *ifn, const char *arg, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_addrmax = val & 0xffffffff; + + if (do_cmd(s, BRDGSIFAMAX, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFAMAX %s", arg); +} + +static void +setbridge_timeout(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_ctime = val & 0xffffffff; + + if (do_cmd(s, BRDGSTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTO %s", arg); +} + +static void +setbridge_private(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_PRIVATE, 1); +} + +static void +unsetbridge_private(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_PRIVATE, 0); +} + +static struct cmd bridge_cmds[] = { + DEF_CMD_ARG("addm", setbridge_add), + DEF_CMD_ARG("deletem", setbridge_delete), + DEF_CMD_ARG("discover", setbridge_discover), + DEF_CMD_ARG("-discover", unsetbridge_discover), + DEF_CMD_ARG("learn", setbridge_learn), + DEF_CMD_ARG("-learn", unsetbridge_learn), + DEF_CMD_ARG("sticky", setbridge_sticky), + DEF_CMD_ARG("-sticky", unsetbridge_sticky), + DEF_CMD_ARG("span", setbridge_span), + DEF_CMD_ARG("-span", unsetbridge_span), + DEF_CMD_ARG("stp", setbridge_stp), + DEF_CMD_ARG("-stp", unsetbridge_stp), + DEF_CMD_ARG("edge", setbridge_edge), + DEF_CMD_ARG("-edge", unsetbridge_edge), + DEF_CMD_ARG("autoedge", setbridge_autoedge), + DEF_CMD_ARG("-autoedge", unsetbridge_autoedge), + DEF_CMD_ARG("ptp", setbridge_ptp), + DEF_CMD_ARG("-ptp", unsetbridge_ptp), + DEF_CMD_ARG("autoptp", setbridge_autoptp), + DEF_CMD_ARG("-autoptp", unsetbridge_autoptp), + DEF_CMD("flush", 0, setbridge_flush), + DEF_CMD("flushall", 0, setbridge_flushall), + DEF_CMD_ARG2("static", setbridge_static), + DEF_CMD_ARG("deladdr", setbridge_deladdr), + DEF_CMD("addr", 1, setbridge_addr), + DEF_CMD_ARG("maxaddr", setbridge_maxaddr), + DEF_CMD_ARG("hellotime", setbridge_hellotime), + DEF_CMD_ARG("fwddelay", setbridge_fwddelay), + DEF_CMD_ARG("maxage", setbridge_maxage), + DEF_CMD_ARG("priority", setbridge_priority), + DEF_CMD_ARG("proto", setbridge_protocol), + DEF_CMD_ARG("holdcnt", setbridge_holdcount), + DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), + DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), + DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr), + DEF_CMD_ARG("timeout", setbridge_timeout), + DEF_CMD_ARG("private", setbridge_private), + DEF_CMD_ARG("-private", unsetbridge_private), +}; +static struct afswtch af_bridge = { + .af_name = "af_bridge", + .af_af = AF_UNSPEC, + .af_other_status = bridge_status, +}; + +static __constructor void +bridge_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(bridge_cmds); i++) + cmd_register(&bridge_cmds[i]); + af_register(&af_bridge); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifcarp.c b/freebsd-userspace/commands/sbin/ifconfig/ifcarp.c new file mode 100644 index 00000000..6c8c5a1b --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifcarp.c @@ -0,0 +1,204 @@ +/* $FreeBSD$ */ +/* from $OpenBSD: ifconfig.c,v 1.82 2003/10/19 05:43:35 mcbride Exp $ */ + +/* + * Copyright (c) 2002 Michael Shalayeff. All rights reserved. + * Copyright (c) 2003 Ryan McBride. 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. + * + * 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 OR HIS RELATIVES 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 MIND, 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 <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/netinet/ip_carp.h> +#else +#include <netinet/ip_carp.h> +#endif +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +static const char *carp_states[] = { CARP_STATES }; + +void carp_status(int s); +void setcarp_advbase(const char *,int, int, const struct afswtch *rafp); +void setcarp_advskew(const char *, int, int, const struct afswtch *rafp); +void setcarp_passwd(const char *, int, int, const struct afswtch *rafp); +void setcarp_vhid(const char *, int, int, const struct afswtch *rafp); + +void +carp_status(int s) +{ + const char *state; + struct carpreq carpr; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + return; + + if (carpr.carpr_vhid > 0) { + if (carpr.carpr_state > CARP_MAXSTATE) + state = "<UNKNOWN>"; + else + state = carp_states[carpr.carpr_state]; + + printf("\tcarp: %s vhid %d advbase %d advskew %d\n", + state, carpr.carpr_vhid, carpr.carpr_advbase, + carpr.carpr_advskew); + } + + return; + +} + +void +setcarp_passwd(const char *val, int d, int s, const struct afswtch *afp) +{ + struct carpreq carpr; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(1, "SIOCGVH"); + + memset(carpr.carpr_key, 0, sizeof(carpr.carpr_key)); + /* XXX Should hash the password into the key here, perhaps? */ + strlcpy(carpr.carpr_key, val, CARP_KEY_LEN); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); + + return; +} + +void +setcarp_vhid(const char *val, int d, int s, const struct afswtch *afp) +{ + int vhid; + struct carpreq carpr; + + vhid = atoi(val); + + if (vhid <= 0) + errx(1, "vhid must be greater than 0"); + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(1, "SIOCGVH"); + + carpr.carpr_vhid = vhid; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); + + return; +} + +void +setcarp_advskew(const char *val, int d, int s, const struct afswtch *afp) +{ + int advskew; + struct carpreq carpr; + + advskew = atoi(val); + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(1, "SIOCGVH"); + + carpr.carpr_advskew = advskew; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); + + return; +} + +void +setcarp_advbase(const char *val, int d, int s, const struct afswtch *afp) +{ + int advbase; + struct carpreq carpr; + + advbase = atoi(val); + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(1, "SIOCGVH"); + + carpr.carpr_advbase = advbase; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); + + return; +} + +static struct cmd carp_cmds[] = { + DEF_CMD_ARG("advbase", setcarp_advbase), + DEF_CMD_ARG("advskew", setcarp_advskew), + DEF_CMD_ARG("pass", setcarp_passwd), + DEF_CMD_ARG("vhid", setcarp_vhid), +}; +static struct afswtch af_carp = { + .af_name = "af_carp", + .af_af = AF_UNSPEC, + .af_other_status = carp_status, +}; + +static __constructor void +carp_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(carp_cmds); i++) + cmd_register(&carp_cmds[i]); + af_register(&af_carp); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifclone.c b/freebsd-userspace/commands/sbin/ifconfig/ifclone.c new file mode 100644 index 00000000..3dc5a37f --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifclone.c @@ -0,0 +1,198 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +#ifdef __rtems__ +#include <freebsd/sys/sockio.h> +#endif + +static void +list_cloners(void) +{ + struct if_clonereq ifcr; + char *cp, *buf; + int idx; + int s; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); + + memset(&ifcr, 0, sizeof(ifcr)); + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for count"); + + buf = malloc(ifcr.ifcr_total * IFNAMSIZ); + if (buf == NULL) + err(1, "unable to allocate cloner name buffer"); + + ifcr.ifcr_count = ifcr.ifcr_total; + ifcr.ifcr_buffer = buf; + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for names"); + + /* + * In case some disappeared in the mean time, clamp it down. + */ + if (ifcr.ifcr_count > ifcr.ifcr_total) + ifcr.ifcr_count = ifcr.ifcr_total; + + for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) { + if (idx > 0) + putchar(' '); + printf("%s", cp); + } + + putchar('\n'); + free(buf); +} + +struct clone_defcb { + char ifprefix[IFNAMSIZ]; + clone_callback_func *clone_cb; + SLIST_ENTRY(clone_defcb) next; +}; + +static SLIST_HEAD(, clone_defcb) clone_defcbh = + SLIST_HEAD_INITIALIZER(clone_defcbh); + +void +clone_setdefcallback(const char *ifprefix, clone_callback_func *p) +{ + struct clone_defcb *dcp; + + dcp = malloc(sizeof(*dcp)); + strlcpy(dcp->ifprefix, ifprefix, IFNAMSIZ-1); + dcp->clone_cb = p; + SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); +} + +/* + * Do the actual clone operation. Any parameters must have been + * setup by now. If a callback has been setup to do the work + * then defer to it; otherwise do a simple create operation with + * no parameters. + */ +static void +ifclonecreate(int s, void *arg) +{ + struct ifreq ifr; + struct clone_defcb *dcp; + clone_callback_func *clone_cb = NULL; + + memset(&ifr, 0, sizeof(ifr)); + (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (clone_cb == NULL) { + /* Try to find a default callback */ + SLIST_FOREACH(dcp, &clone_defcbh, next) { + if (strncmp(dcp->ifprefix, ifr.ifr_name, + strlen(dcp->ifprefix)) == 0) { + clone_cb = dcp->clone_cb; + break; + } + } + } + if (clone_cb == NULL) { + /* NB: no parameters */ + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + err(1, "SIOCIFCREATE2"); + } else { + clone_cb(s, &ifr); + } + + /* + * If we get a different name back than we put in, print it. + */ + if (strncmp(name, ifr.ifr_name, sizeof(name)) != 0) { + strlcpy(name, ifr.ifr_name, sizeof(name)); + printf("%s\n", name); + } +} + +static +DECL_CMD_FUNC(clone_create, arg, d) +{ + callback_register(ifclonecreate, NULL); +} + +static +DECL_CMD_FUNC(clone_destroy, arg, d) +{ + (void) strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + err(1, "SIOCIFDESTROY"); +} + +static struct cmd clone_cmds[] = { + DEF_CLONE_CMD("create", 0, clone_create), + DEF_CMD("destroy", 0, clone_destroy), + DEF_CLONE_CMD("plumb", 0, clone_create), + DEF_CMD("unplumb", 0, clone_destroy), +}; + +static void +clone_Copt_cb(const char *optarg __unused) +{ + list_cloners(); + exit(0); +} +static struct option clone_Copt = { .opt = "C", .opt_usage = "[-C]", .cb = clone_Copt_cb }; + +static __constructor void +clone_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(clone_cmds); i++) + cmd_register(&clone_cmds[i]); + opt_register(&clone_Copt); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifconfig.8 b/freebsd-userspace/commands/sbin/ifconfig/ifconfig.8 new file mode 100644 index 00000000..eaab8fa4 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifconfig.8 @@ -0,0 +1,2555 @@ +.\" Copyright (c) 1983, 1991, 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. +.\" +.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 +.\" $FreeBSD$ +.\" +.Dd May 14, 2010 +.Dt IFCONFIG 8 +.Os +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm +.Op Fl L +.Op Fl k +.Op Fl m +.Op Fl n +.Ar interface +.Op Cm create +.Op Ar address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm +.Ar interface +.Cm destroy +.Nm +.Fl a +.Op Fl L +.Op Fl d +.Op Fl m +.Op Fl u +.Op Fl v +.Op Ar address_family +.Nm +.Fl l +.Op Fl d +.Op Fl u +.Op Ar address_family +.Nm +.Op Fl L +.Op Fl d +.Op Fl k +.Op Fl m +.Op Fl u +.Op Fl v +.Op Fl C +.Nm +.Op Fl g Ar groupname +.Sh DESCRIPTION +The +.Nm +utility is used to assign an address +to a network interface and/or configure +network interface parameters. +The +.Nm +utility must be used at boot time to define the network address +of each interface present on a machine; it may also be used at +a later time to redefine an interface's address +or other operating parameters. +.Pp +The following options are available: +.Bl -tag -width indent +.It Ar address +For the +.Tn DARPA Ns -Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +.Pp +It is also possible to use the CIDR notation (also known as the +slash notation) to include the netmask. +That is, one can specify an address like +.Li 192.168.0.1/16 . +.Pp +For the +.Dq inet6 +family, it is also possible to specify the prefix length using the slash +notation, like +.Li ::1/128 . +See the +.Cm prefixlen +parameter below for more information. +.\" For the Xerox Network Systems(tm) family, +.\" addresses are +.\" .Ar net:a.b.c.d.e.f , +.\" where +.\" .Ar net +.\" is the assigned network number (in decimal), +.\" and each of the six bytes of the host number, +.\" .Ar a +.\" through +.\" .Ar f , +.\" are specified in hexadecimal. +.\" The host number may be omitted on IEEE 802 protocol +.\" (Ethernet, FDDI, and Token Ring) interfaces, +.\" which use the hardware physical address, +.\" and on interfaces other than the first. +.\" For the +.\" .Tn ISO +.\" family, addresses are specified as a long hexadecimal string, +.\" as in the Xerox family. +.\" However, two consecutive dots imply a zero +.\" byte, and the dots are optional, if the user wishes to (carefully) +.\" count out long strings of digits in network byte order. +.Pp +The link-level +.Pq Dq link +address +is specified as a series of colon-separated hex digits. +This can be used to +e.g.\& set a new MAC address on an ethernet interface, though the +mechanism used is not ethernet-specific. +If the interface is already +up when this option is used, it will be briefly brought down and +then brought back up again in order to ensure that the receive +filter in the underlying ethernet hardware is properly reprogrammed. +.It Ar address_family +Specify the +address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommended. +The address or protocol families currently +supported are +.Dq inet , +.Dq inet6 , +.Dq atalk , +.Dq ipx , +.\" .Dq iso , +and +.Dq link . +.\" and +.\" .Dq ns . +The default is +.Dq inet . +.Dq ether +and +.Dq lladdr +are synonyms for +.Dq link . +.It Ar dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Ar interface +This +parameter is a string of the form +.Dq name unit , +for example, +.Dq Li ed0 . +.It Ar groupname +List the interfaces in the given group. +.El +.Pp +The following parameters may be set with +.Nm : +.Bl -tag -width indent +.It Cm add +Another name for the +.Cm alias +parameter. +Introduced for compatibility +with +.Bsx . +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +If the address is on the same subnet as the first network address +for this interface, a non-conflicting netmask must be given. +Usually +.Li 0xffffffff +is most appropriate. +.It Fl alias +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm anycast +(Inet6 only.) +Specify that the address configured is an anycast address. +Based on the current specification, +only routers may configure anycast addresses. +Anycast address will not be used as source address of any of outgoing +IPv6 packets. +.It Cm arp +Enable the use of the Address Resolution Protocol +.Pq Xr arp 4 +in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and +.Tn IEEE +802 48-bit MAC addresses (Ethernet, FDDI, and Token Ring addresses). +.It Fl arp +Disable the use of the Address Resolution Protocol +.Pq Xr arp 4 . +.It Cm staticarp +If the Address Resolution Protocol is enabled, +the host will only reply to requests for its addresses, +and will never send any requests. +.It Fl staticarp +If the Address Resolution Protocol is enabled, +the host will perform normally, +sending out requests and listening for replies. +.It Cm broadcast +(Inet only.) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.It Cm promisc +Put interface into permanently promiscuous mode. +.It Fl promisc +Disable permanently promiscuous mode. +.It Cm delete +Another name for the +.Fl alias +parameter. +.It Cm description Ar value , Cm descr Ar value +Specify a description of the interface. +This can be used to label interfaces in situations where they may +otherwise be difficult to distinguish. +.It Cm -description , Cm -descr +Clear the interface description. +.It Cm down +Mark an interface +.Dq down . +When an interface is marked +.Dq down , +the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +This action does not automatically disable routes using the interface. +.It Cm group Ar group-name +Assign the interface to a +.Dq group . +Any interface can be in multiple groups. +.Pp +Cloned interfaces are members of their interface family group by default. +For example, a PPP interface such as +.Em ppp0 +is a member of the PPP interface family group, +.Em ppp . +.\" The interface(s) the default route(s) point to are members of the +.\" .Em egress +.\" interface group. +.It Cm -group Ar group-name +Remove the interface from the given +.Dq group . +.It Cm eui64 +(Inet6 only.) +Fill interface index +(lowermost 64bit of an IPv6 address) +automatically. +.It Cm ipdst +This is used to specify an Internet host who is willing to receive +IP packets encapsulating IPX packets bound for a remote network. +An apparent point to point link is constructed, and +the address specified will be taken as the IPX address and network +of the destination. +.It Cm maclabel Ar label +If Mandatory Access Control support is enabled in the kernel, +set the MAC label to +.Ar label . +.\" (see +.\" .Xr maclabel 7 ) . +.It Cm media Ar type +If the driver supports the media selection system, set the media type +of the interface to +.Ar type . +Some interfaces support the mutually exclusive use of one of several +different physical media connectors. +For example, a 10Mbit/s Ethernet +interface might support the use of either +.Tn AUI +or twisted pair connectors. +Setting the media type to +.Cm 10base5/AUI +would change the currently active connector to the AUI port. +Setting it to +.Cm 10baseT/UTP +would activate twisted pair. +Refer to the interfaces' driver +specific documentation or man page for a complete list of the +available types. +.It Cm mediaopt Ar opts +If the driver supports the media selection system, set the specified +media options on the interface. +The +.Ar opts +argument +is a comma delimited list of options to apply to the interface. +Refer to the interfaces' driver specific man page for a complete +list of available options. +.It Fl mediaopt Ar opts +If the driver supports the media selection system, disable the +specified media options on the interface. +.It Cm mode Ar mode +If the driver supports the media selection system, set the specified +operating mode on the interface to +.Ar mode . +For IEEE 802.11 wireless interfaces that support multiple operating modes +this directive is used to select between 802.11a +.Pq Cm 11a , +802.11b +.Pq Cm 11b , +and 802.11g +.Pq Cm 11g +operating modes. +.It Cm inst Ar minst , Cm instance Ar minst +Set the media instance to +.Ar minst . +This is useful for devices which have multiple physical layer interfaces +.Pq PHYs . +.It Cm name Ar name +Set the interface name to +.Ar name . +.It Cm rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +enable receive (or transmit) checksum offloading on the interface. +Some drivers may not be able to enable these flags independently +of each other, so setting one may also set the other. +The driver will offload as much checksum work as it can reliably +support, the exact level of offloading varies between drivers. +.It Fl rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +disable receive (or transmit) checksum offloading on the interface. +These settings may not always be independent of each other. +.It Cm tso +If the driver supports +.Xr tcp 4 +segmentation offloading, enable TSO on the interface. +Some drivers may not be able to support TSO for +.Xr ip 4 +and +.Xr ip6 4 +packets, so they may enable only one of them. +.It Fl tso +If the driver supports +.Xr tcp 4 +segmentation offloading, disable TSO on the interface. +It will always disable TSO for +.Xr ip 4 +and +.Xr ip6 4 . +.It Cm lro +If the driver supports +.Xr tcp 4 +large receive offloading, enable LRO on the interface. +.It Fl lro +If the driver supports +.Xr tcp 4 +large receive offloading, disable LRO on the interface. +.It Cm wol , wol_ucast , wol_mcast , wol_magic +Enable Wake On Lan (WOL) support, if available. +WOL is a facility whereby a machine in a low power state may be woken +in response to a received packet. +There are three types of packets that may wake a system: +ucast (directed solely to the machine's mac address), +mcast (directed to a broadcast or multicast address), +or +magic (unicast or multicast frames with a ``magic contents''). +Not all devices support WOL, those that do indicate the mechanisms +they support in their capabilities. +.Cm wol +is a synonym for enabling all available WOL mechanisms. +To disable WOL use +.Fl wol . +.It Cm vlanmtu , vlanhwtag, vlanhwfilter, vlanhwtso +If the driver offers user-configurable VLAN support, enable +reception of extended frames, tag processing in hardware, +frame filtering in hardware, or TSO on VLAN, +respectively. +Note that this must be issued on a physical interface associated with +.Xr vlan 4 , +not on a +.Xr vlan 4 +interface itself. +.It Fl vlanmtu , vlanhwtag, vlanhwfilter, vlanhwtso +If the driver offers user-configurable VLAN support, disable +reception of extended frames, tag processing in hardware, +frame filtering in hardware, or TSO on VLAN, +respectively. +.It Cm vnet Ar jail +Move the interface to the +.Xr jail 8 , +specified by name or JID. +If the jail has a virtual network stack, the interface will disappear +from the current environment and become visible to the jail. +.It Fl vnet Ar jail +Reclaim the interface from the +.Xr jail 8 , +specified by name or JID. +If the jail has a virtual network stack, the interface will disappear +from the jail, and become visible to the current network environment. +.It Cm polling +Turn on +.Xr polling 4 +feature and disable interrupts on the interface, if driver supports +this mode. +.It Fl polling +Turn off +.Xr polling 4 +feature and enable interrupt mode on the interface. +.It Cm create +Create the specified network pseudo-device. +If the interface is given without a unit number, try to create a new +device with an arbitrary unit number. +If creation of an arbitrary device is successful, the new device name is +printed to standard output unless the interface is renamed or destroyed +in the same +.Nm +invocation. +.It Cm destroy +Destroy the specified network pseudo-device. +.It Cm plumb +Another name for the +.Cm create +parameter. +Included for +.Tn Solaris +compatibility. +.It Cm unplumb +Another name for the +.Cm destroy +parameter. +Included for +.Tn Solaris +compatibility. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as additional hops +to the destination network or host. +.It Cm mtu Ar n +Set the maximum transmission unit of the interface to +.Ar n , +default is interface specific. +The MTU is used to limit the size of packets that are transmitted on an +interface. +Not all interfaces support setting the MTU, and some interfaces have +range restrictions. +.It Cm netmask Ar mask +.\" (Inet and ISO.) +(Inet only.) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading +.Ql 0x , +with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.Pp +The netmask can also be specified in CIDR notation after the address. +See the +.Ar address +option above for more information. +.It Cm prefixlen Ar len +(Inet6 only.) +Specify that +.Ar len +bits are reserved for subdividing networks into sub-networks. +The +.Ar len +must be integer, and for syntactical reason it must be between 0 to 128. +It is almost always 64 under the current IPv6 assignment rule. +If the parameter is omitted, 64 is used. +.Pp +The prefix can also be specified using the slash notation after the address. +See the +.Ar address +option above for more information. +.\" see +.\" Xr eon 5 . +.\" .It Cm nsellength Ar n +.\" .Pf ( Tn ISO +.\" only) +.\" This specifies a trailing number of bytes for a received +.\" .Tn NSAP +.\" used for local identification, the remaining leading part of which is +.\" taken to be the +.\" .Tn NET +.\" (Network Entity Title). +.\" The default value is 1, which is conformant to US +.\" .Tn GOSIP . +.\" When an ISO address is set in an ifconfig command, +.\" it is really the +.\" .Tn NSAP +.\" which is being specified. +.\" For example, in +.\" .Tn US GOSIP , +.\" 20 hex digits should be +.\" specified in the +.\" .Tn ISO NSAP +.\" to be assigned to the interface. +.\" There is some evidence that a number different from 1 may be useful +.\" for +.\" .Tn AFI +.\" 37 type addresses. +.It Cm range Ar netrange +Under appletalk, set the interface to respond to a +.Ar netrange +of the form +.Ar startnet Ns - Ns Ar endnet . +Appletalk uses this scheme instead of +netmasks though +.Fx +implements it internally as a set of netmasks. +.It Cm remove +Another name for the +.Fl alias +parameter. +Introduced for compatibility +with +.Bsx . +.It Cm phase +The argument following this specifies the version (phase) of the +Appletalk network attached to the interface. +Values of 1 or 2 are permitted. +.Sm off +.It Cm link Op Cm 0 No - Cm 2 +.Sm on +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. +An example +of this is to enable SLIP compression, or to select the connector type +for some Ethernet cards. +Refer to the man page for the specific driver +for more information. +.Sm off +.It Fl link Op Cm 0 No - Cm 2 +.Sm on +Disable special processing at the link level with the specified interface. +.It Cm monitor +Put the interface in monitor mode. +No packets are transmitted, and received packets are discarded after +.Xr bpf 4 +processing. +.It Fl monitor +Take the interface out of monitor mode. +.It Cm up +Mark an interface +.Dq up . +This may be used to enable an interface after an +.Dq Nm Cm down . +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +The following parameters are for ICMPv6 Neightbor Discovery Protocol: +.Bl -tag -width indent +.It Cm accept_rtadv +Set a flag to enable accepting ICMPv6 Router Advertisement messages. +.It Cm -accept_rtadv +Clear a flag +.Cm accept_rtadv . +.It Cm defaultif +Set the specified interface as the default route when there is no +default router. +.It Cm -defaultif +Clear a flag +.Cm defaultif . +.It Cm ifdisabled +Set a flag to disable all of IPv6 network communications on the +specified interface. +.It Cm -ifdisabled +Clear a flag +.Cm ifdisabled . +.It Cm nud +Set a flag to enable Neighbor Unreachability Detection. +.It Cm -nud +Clear a flag +.Cm nud . +.It Cm prefer_source +Set a flag to prefer addesses on the interface as candidates of the +source address for outgoing packets. +.It Cm -prefer_source +Clear a flag +.Cm prefer_source . +.El +.Pp +The following parameters are specific to cloning +IEEE 802.11 wireless interfaces with the +.Cm create +request: +.Bl -tag -width indent +.It Cm wlandev Ar device +Use +.Ar device +as the parent for the cloned device. +.It Cm wlanmode Ar mode +Specify the operating mode for this cloned device. +.Ar mode +is one of +.Cm sta , +.Cm ahdemo +(or +.Cm adhoc-demo ), +.Cm ibss , +(or +.Cm adhoc ), +.Cm ap , +(or +.Cm hostap ), +.Cm wds , +.Cm tdma , +.Cm mesh , +and +.Cm monitor . +The operating mode of a cloned interface cannot be changed. +The +.Cm tdma +mode is actually implemented as an +.Cm adhoc-demo +interface with special properties. +.It Cm wlanbssid Ar bssid +The 802.11 mac address to use for the bssid. +This must be specified at create time for a legacy +.Cm wds +device. +.It Cm wlanaddr Ar address +The local mac address. +If this is not specified then a mac address will automatically be assigned +to the cloned device. +Typically this address is the same as the address of the parent device +but if the +.Cm bssid +parameter is specified then the driver will craft a unique address for +the device (if supported). +.It Cm wdslegacy +Mark a +.Cm wds +device as operating in ``legacy mode''. +Legacy +.Cm wds +devices have a fixed peer relationship and do not, for example, roam +if their peer stops communicating. +For completeness a Dynamic WDS (DWDS) interface may marked as +.Fl wdslegacy . +.It Cm bssid +Request a unique local mac address for the cloned device. +This is only possible if the device supports multiple mac addresses. +To force use of the parent's mac address use +.Fl bssid . +.It Cm beacons +Mark the cloned interface as depending on hardware support to +track received beacons. +To have beacons tracked in software use +.Fl beacons . +For +.Cm hostap +mode +.Fl beacons +can also be used to indicate no beacons should +be transmitted; this can be useful when creating a WDS configuration but +.Cm wds +interfaces can only be created as companions to an access point. +.El +.Pp +The following parameters are specific to IEEE 802.11 wireless interfaces +cloned with a +.Cm create +operation: +.Bl -tag -width indent +.It Cm ampdu +Enable sending and receiving AMPDU frames when using 802.11n (default). +The 802.11n specification states a compliant station must be capable +of receiving AMPDU frames but transmision is optional. +Use +.Fl ampdu +to disable all use of AMPDU with 802.11n. +For testing and/or to work around interoperability problems one can use +.Cm ampdutx +and +.Cm ampdurx +to control use of AMPDU in one direction. +.It Cm ampdudensity Ar density +Set the AMPDU density parameter used when operating with 802.11n. +This parameter controls the inter-packet gap for AMPDU frames. +The sending device normally controls this setting but a receiving station +may request wider gaps. +Legal values for +.Ar density +are 0, .25, .5, 1, 2, 4, 8, and 16 (microseconds). +A value of +.Cm - +is treated the same as 0. +.It Cm ampdulimit Ar limit +Set the limit on packet size for receiving AMPDU frames when operating +with 802.11n. +Legal values for +.Ar limit +are 8192, 16384, 32768, and 65536 but one can also specify +just the unique prefix: 8, 16, 32, 64. +Note the sender may limit the size of AMPDU frames to be less +than the maximum specified by the receiving station. +.It Cm amsdu +Enable sending and receiving AMSDU frames when using 802.11n. +By default AMSDU is received but not transmitted. +Use +.Fl amsdu +to disable all use of AMSDU with 802.11n. +For testing and/or to work around interoperability problems one can use +.Cm amsdutx +and +.Cm amsdurx +to control use of AMSDU in one direction. +.It Cm amsdulimit Ar limit +Set the limit on packet size for sending and receiving AMSDU frames +when operating with 802.11n. +Legal values for +.Ar limit +are 7935 and 3839 (bytes). +Note the sender may limit the size of AMSDU frames to be less +than the maximum specified by the receiving station. +Note also that devices are not required to support the 7935 limit, +only 3839 is required by the specification and the larger value +may require more memory to be dedicated to support functionality +that is rarely used. +.It Cm apbridge +When operating as an access point, pass packets between +wireless clients directly (default). +To instead let them pass up through the +system and be forwarded using some other mechanism, use +.Fl apbridge . +Disabling the internal bridging +is useful when traffic is to be processed with +packet filtering. +.It Cm authmode Ar mode +Set the desired authentication mode in infrastructure mode. +Not all adapters support all modes. +The set of +valid modes is +.Cm none , open , shared +(shared key), +.Cm 8021x +(IEEE 802.1x), +and +.Cm wpa +(IEEE WPA/WPA2/802.11i). +The +.Cm 8021x +and +.Cm wpa +modes are only useful when using an authentication service +(a supplicant for client operation or an authenticator when +operating as an access point). +Modes are case insensitive. +.It Cm bgscan +Enable background scanning when operating as a station. +Background scanning is a technique whereby a station associated to +an access point will temporarily leave the channel to scan for +neighboring stations. +This allows a station to maintain a cache of nearby access points +so that roaming between access points can be done without +a lengthy scan operation. +Background scanning is done only when a station is not busy and +any outbound traffic will cancel a scan operation. +Background scanning should never cause packets to be lost though +there may be some small latency if outbound traffic interrupts a +scan operation. +By default background scanning is enabled if the device is capable. +To disable background scanning, use +.Fl bgscan . +Background scanning is controlled by the +.Cm bgscanidle +and +.Cm bgscanintvl +parameters. +Background scanning must be enabled for roaming; this is an artifact +of the current implementation and may not be required in the future. +.It Cm bgscanidle Ar idletime +Set the minimum time a station must be idle (not transmitting or +receiving frames) before a background scan is initiated. +The +.Ar idletime +parameter is specified in milliseconds. +By default a station must be idle at least 250 milliseconds before +a background scan is initiated. +The idle time may not be set to less than 100 milliseconds. +.It Cm bgscanintvl Ar interval +Set the interval at which background scanning is attempted. +The +.Ar interval +parameter is specified in seconds. +By default a background scan is considered every 300 seconds (5 minutes). +The +.Ar interval +may not be set to less than 15 seconds. +.It Cm bintval Ar interval +Set the interval at which beacon frames are sent when operating in +ad-hoc or ap mode. +The +.Ar interval +parameter is specified in TU's (1024 usecs). +By default beacon frames are transmitted every 100 TU's. +.It Cm bmissthreshold Ar count +Set the number of consecutive missed beacons at which the station +will attempt to roam (i.e., search for a new access point). +The +.Ar count +parameter must be in the range 1 to 255; though the +upper bound may be reduced according to device capabilities. +The default threshold is 7 consecutive missed beacons; but +this may be overridden by the device driver. +Another name for the +.Cm bmissthreshold +parameter is +.Cm bmiss . +.It Cm bssid Ar address +Specify the MAC address of the access point to use when operating +as a station in a BSS network. +This overrides any automatic selection done by the system. +To disable a previously selected access point, supply +.Cm any , none , +or +.Cm - +for the address. +This option is useful when more than one access point uses the same SSID. +Another name for the +.Cm bssid +parameter is +.Cm ap . +.It Cm burst +Enable packet bursting. +Packet bursting is a transmission technique whereby the wireless +medium is acquired once to send multiple frames and the interframe +spacing is reduced. +This technique can significantly increase throughput by reducing +transmission overhead. +Packet bursting is supported by the 802.11e QoS specification +and some devices that do not support QoS may still be capable. +By default packet bursting is enabled if a device is capable +of doing it. +To disable packet bursting, use +.Fl burst . +.It Cm chanlist Ar channels +Set the desired channels to use when scanning for access +points, neighbors in an IBSS network, or looking for unoccupied +channels when operating as an access point. +The set of channels is specified as a comma-separated list with +each element in the list representing either a single channel number or a range +of the form +.Dq Li a-b . +Channel numbers must be in the range 1 to 255 and be permissible +according to the operating characteristics of the device. +.It Cm channel Ar number +Set a single desired channel. +Channels range from 1 to 255, but the exact selection available +depends on the region your adaptor was manufactured for. +Setting +the channel to +.Li any , +or +.Cm - +will clear any desired channel and, if the device is marked up, +force a scan for a channel to operate on. +Alternatively the frequency, in megahertz, may be specified +instead of the channel number. +.Pp +When there are several ways to use a channel the channel +number/frequency may be appended with attributes to clarify. +For example, if a device is capable of operating on channel 6 +with 802.11n and 802.11g then one can specify that g-only use +should be used by specifying ``6:g''. +Similarly the channel width can be specified by appending it +with ``/''; e.g. ``6/40'' specifies a 40MHz wide channel, +These attributes can be combined as in: ``6:ht/40''. +The full set of flags specified following a `:'' are: +.Cm a +(802.11a), +.Cm b +(802.11b), +.Cm d +(Atheros Dynamic Turbo mode), +.Cm g +(802.11g), +.Cm h +or +.Cm n +(802.11n aka HT), +.Cm s +(Atheros Static Turbo mode), +and +.Cm t +(Atheros Dynamic Turbo mode, or appended to ``st'' and ``dt''). +The full set of channel widths following a '/' are: +.Cm 5 +(5MHz aka quarter-rate channel), +.Cm 10 +(10MHz aka half-rate channel), +.Cm 20 +(20MHz mostly for use in specifying ht20), +and +.Cm 40 +(40MHz mostly for use in specifying ht40), +In addition, +a 40MHz HT channel specification may include the location +of the extension channel by appending ``+'' or ``-'' for above and below, +respectively; e.g. ``2437:ht/40+'' specifies 40MHz wide HT operation +with the center channel at frequency 2437 and the extension channel above. +.It Cm country Ar name +Set the country code to use in calculating the regulatory constraints +for operation. +In particular the set of available channels, how the wireless device +will operation on the channels, and the maximum transmit power that +can be used on a channel are defined by this setting. +Country/Region codes are specified as a 2-character abbreviation +defined by ISO 3166 or using a longer, but possibly ambiguous, spelling; +e.g. "ES" and "Spain". +The set of country codes are taken from /etc/regdomain.xml and can also +be viewed with the ``list countries'' request. +Note that not all devices support changing the country code from a default +setting; typically stored in EEPROM. +See also +.Cm regdomain , +.Cm indoor , +.Cm outdoor , +and +.Cm anywhere . +.It Cm dfs +Enable Dynamic Frequency Selection (DFS) as specified in 802.11h. +DFS embodies several facilities including detection of overlapping +radar signals, dynamic transmit power control, and channel selection +according to a least-congested criteria. +DFS support is mandatory for some 5Ghz frequencies in certain +locales (e.g. ETSI). +By default DFS is enabled according to the regulatory definitions +specified in /etc/regdomain.xml and the curent country code, regdomain, +and channel. +Note the underlying device (and driver) must support radar detection +for full DFS support to work. +To be fully compliant with the local regulatory agency frequencies that +require DFS should not be used unless it is fully supported. +Use +.Fl dfs +to disable this functionality for testing. +.It Cm dotd +Enable support for the 802.11d specification (default). +When this support is enabled in station mode, beacon frames that advertise +a country code different than the currently configured country code will +cause an event to be dispatched to user applications. +This event can be used by the station to adopt that country code and +operate according to the associated regulatory constraints. +When operating as an access point with 802.11d enabled the beacon and +probe response frames transmitted will advertise the current regulatory +domain settings. +To disable 802.11d use +.Fl dotd . +.It Cm doth +Enable 802.11h support including spectrum management. +When 802.11h is enabled beacon and probe response frames will have +the SpectrumMgt bit set in the capabilities field and +country and power constraint information elements will be present. +802.11h support also includes handling Channel Switch Announcements (CSA) +which are a mechanism to coordinate channel changes by an access point. +By default 802.11h is enabled if the device is capable. +To disable 802.11h use +.Fl doth . +.It Cm deftxkey Ar index +Set the default key to use for transmission. +Typically this is only set when using WEP encryption. +Note that you must set a default transmit key +for the system to know which key to use in encrypting outbound traffic. +The +.Cm weptxkey +is an alias for this request; it is provided for backwards compatibility. +.It Cm dtimperiod Ar period +Set the +DTIM +period for transmitting buffered multicast data frames when +operating in ap mode. +The +.Ar period +specifies the number of beacon intervals between DTIM +and must be in the range 1 to 15. +By default DTIM is 1 (i.e., DTIM occurs at each beacon). +.It Cm dturbo +Enable the use of Atheros Dynamic Turbo mode when communicating with +another Dynamic Turbo-capable station. +Dynamic Turbo mode is an Atheros-specific mechanism by which +stations switch between normal 802.11 operation and a ``boosted'' +mode in which a 40MHz wide channel is used for communication. +Stations using Dynamic Turbo mode operate boosted only when the +channel is free of non-dturbo stations; when a non-dturbo station +is identified on the channel all stations will automatically drop +back to normal operation. +By default, Dynamic Turbo mode is not enabled, even if the device is capable. +Note that turbo mode (dynamic or static) is only allowed on some +channels depending on the regulatory constraints; use the +.Cm list chan +command to identify the channels where turbo mode may be used. +To disable Dynamic Turbo mode use +.Fl dturbo . +.It Cm dwds +Enable Dynamic WDS (DWDS) support. +DWDS is a facility by which 4-address traffic can be carried between +stations operating in infrastructure mode. +A station first associates to an access point and authenticates using +normal procedures (e.g. WPA). +Then 4-address frames are passed to carry traffic for stations +operating on either side of the wireless link. +DWDS extends the normal WDS mechanism by leveraging existing security +protocols and eliminating static binding. +.Pp +When DWDS is enabled on an access point 4-address frames received from +an authorized station will generate a ``DWDS discovery'' event to user +applications. +This event should be used to create a WDS interface that is bound +to the remote station (and usually plumbed into a bridge). +Once the WDS interface is up and running 4-address traffic then logically +flows through that interface. +.Pp +When DWDS is enabled on a station, traffic with a destination address +different from the peer station are encapsulated in a 4-address frame +and transmitted to the peer. +All 4-address traffic uses the security information of the stations +(e.g. cryptographic keys). +A station is associated using 802.11n facilities may transport +4-address traffic using these same mechanisms; this depends on available +resources and capabilities of the device. +The DWDS implementation guards against layer 2 routing loops of +multicast traffic. +.It Cm ff +Enable the use of Atheros Fast Frames when communicating with +another Fast Frames-capable station. +Fast Frames are an encapsulation technique by which two 802.3 +frames are transmitted in a single 802.11 frame. +This can noticeably improve throughput but requires that the +receiving station understand how to decapsulate the frame. +Fast frame use is negotiated using the Atheros 802.11 vendor-specific +protocol extension so enabling use is safe when communicating with +non-Atheros devices. +By default, use of fast frames is enabled if the device is capable. +To explicitly disable fast frames, use +.Fl ff . +.It Cm fragthreshold Ar length +Set the threshold for which transmitted frames are broken into fragments. +The +.Ar length +argument is the frame size in bytes and must be in the range 256 to 2346. +Setting +.Ar length +to +.Li 2346 , +.Cm any , +or +.Cm - +disables transmit fragmentation. +Not all adapters honor the fragmentation threshold. +.It Cm hidessid +When operating as an access point, do not broadcast the SSID +in beacon frames or respond to probe request frames unless +they are directed to the ap (i.e., they include the ap's SSID). +By default, the SSID is included in beacon frames and +undirected probe request frames are answered. +To re-enable the broadcast of the SSID etc., use +.Fl hidessid . +.It Cm ht +Enable use of High Throughput (HT) when using 802.11n (default). +The 802.11n specification includes mechanisms for operation +on 20MHz and 40MHz wide channels using different signalling mechanisms +than specified in 802.11b, 802.11g, and 802.11a. +Stations negotiate use of these facilities, termed HT20 and HT40, +when they associate. +To disable all use of 802.11n use +.Fl ht . +To disable use of HT20 (e.g. to force only HT40 use) use +.Fl ht20 . +To disable use of HT40 use +.Fl ht40 . +.Pp +HT configuration is used to ``auto promote'' operation +when several choices are available. +For example, if a station associates to an 11n-capable access point +it controls whether the station uses legacy operation, HT20, or HT40. +When an 11n-capable device is setup as an access point and +Auto Channel Selection is used to locate a channel to operate on, +HT configuration controls whether legacy, HT20, or HT40 operation is setup +on the selected channel. +If a fixed channel is specified for a station then HT configuration can +be given as part of the channel specification; e.g. 6:ht/20 to setup +HT20 operation on channel 6. +.It Cm htcompat +Enable use of compatibility support for pre-802.11n devices (default). +The 802.11n protocol specification went through several incompatible iterations. +Some vendors implemented 11n support to older specifications that +will not interoperate with a purely 11n-compliant station. +In particular the information elements included in management frames +for old devices are different. +When compatibility support is enabled both standard and compatible data +will be provided. +Stations that associate using the compatiblity mechanisms are flagged +in ``list sta''. +To disable compatiblity support use +.Fl htcompat . +.It Cm htprotmode Ar technique +For interfaces operating in 802.11n, use the specified +.Ar technique +for protecting HT frames in a mixed legacy/HT network. +The set of valid techniques is +.Cm off , +and +.Cm rts +(RTS/CTS, default). +Technique names are case insensitive. +.It Cm inact +Enable inactivity processing for stations associated to an +access point (default). +When operating as an access point the 802.11 layer monitors +the activity of each associated station. +When a station is inactive for 5 minutes it will send several +``probe frames'' to see if the station is still present. +If no response is received then the station is deauthenticated. +Applications that prefer to handle this work can disable this +facility by using +.Fl inact . +.It Cm indoor +Set the location to use in calculating regulatory constraints. +The location is also advertised in beacon and probe response frames +when 802.11d is enabled with +.Cm dotd . +See also +.Cm outdoor , +.Cm anywhere , +.Cm country , +and +.Cm regdomain . +.It Cm list active +Display the list of channels available for use taking into account +any restrictions set with the +.Cm chanlist +directive. +See the description of +.Cm list chan +for more information. +.It Cm list caps +Display the adaptor's capabilities, including the operating +modes supported. +.It Cm list chan +Display the list of channels available for use. +Channels are shown with their IEEE channel number, equivalent +frequency, and usage modes. +Channels identified as +.Ql 11g +are also usable in +.Ql 11b +mode. +Channels identified as +.Ql 11a Turbo +may be used only for Atheros' Static Turbo mode +(specified with +. Cm mediaopt turbo ) . +Channels marked with a +.Ql * +have a regulatory constraint that they be passively scanned. +This means a station is not permitted to transmit on the channel until +it identifies the channel is being used for 802.11 communication; +typically by hearing a beacon frame from an access point operating +on the channel. +.Cm list freq +is another way of requesting this information. +By default a compacted list of channels is displayed; if the +.Fl v +option is specified then all channels are shown. +.It Cm list countries +Display the set of country codes and regulatory domains that can be +used in regulatory configuration. +.It Cm list mac +Display the current MAC Access Control List state. +Each address is prefixed with a character that indicates the +current policy applied to it: +.Ql + +indicates the address is allowed access, +.Ql - +indicates the address is denied access, +.Ql * +indicates the address is present but the current policy open +(so the ACL is not consulted). +.It Cm list mesh +Displays the mesh routing table, used for forwarding packets on a mesh +network. +.It Cm list regdomain +Display the current regulatory settings including the available channels +and transmit power caps. +.It Cm list roam +Display the parameters that govern roaming operation. +.It Cm list txparam +Display the parameters that govern transmit operation. +.It Cm list txpower +Display the transmit power caps for each channel. +.It Cm list scan +Display the access points and/or ad-hoc neighbors +located in the vicinity. +This information may be updated automatically by the adapter +with a +.Cm scan +request or through background scanning. +Depending on the capabilities of the stations the following +flags can be included in the output: +.Bl -tag -width 3n +.It Li A +Authorized. +Indicates that the station is permitted to send/receive data frames. +.It Li E +Extended Rate Phy (ERP). +Indicates that the station is operating in an 802.11g network +using extended transmit rates. +.It Li H +High Throughput (HT). +Indicates that the station is using HT transmit rates. +If a `+' follows immediately after then the station associated +using deprecated mechanisms supported only when +.Cm htcompat +is enabled. +.It Li P +Power Save. +Indicates that the station is operating in power save mode. +.It Li Q +Quality of Service (QoS). +Indicates that the station is using QoS encapsulation for +data frame. +QoS encapsulation is enabled only when WME mode is enabled. +.It Li S +Short Preamble. +Indicates that the station is doing short preamble to optionally +improve throughput performance with 802.11g and 802.11b. +.It Li T +Transitional Security Network (TSN). +Indicates that the station associated using TSN; see also +.Cm tsn +below. +.It Li W +Wi-Fi Protected Setup (WPS). +Indicates that the station associated using WPS. +.El +.Pp +By default interesting information elements captured from the neighboring +stations are displayed at the end of each row. +Possible elements include: +.Cm WME +(station supports WME), +.Cm WPA +(station supports WPA), +.Cm WPS +(station supports WPS), +.Cm RSN +(station supports 802.11i/RSN), +.Cm HTCAP +(station supports 802.11n/HT communication), +.Cm ATH +(station supports Atheros protocol extensions), +.Cm VEN +(station supports unknown vendor-specific extensions). +If the +.Fl v +flag is used all the information elements and their +contents will be shown. +Specifying the +.Fl v +flag also enables display of long SSIDs. +The +.Cm list ap +command is another way of requesting this information. +.It Cm list sta +When operating as an access point display the stations that are +currently associated. +When operating in ad-hoc mode display stations identified as +neighbors in the IBSS. +When operating in mesh mode display stations identified as +neighbors in the MBSS. +When operating in station mode display the access point. +Capabilities advertised by the stations are described under +the +.Cm scan +request. +Depending on the capabilities of the stations the following +flags can be included in the output: +.Bl -tag -width 3n +.It Li A +Authorized. +Indicates that the station is permitted to send/receive data frames. +.It Li E +Extended Rate Phy (ERP). +Indicates that the station is operating in an 802.11g network +using extended transmit rates. +.It Li H +High Throughput (HT). +Indicates that the station is using HT transmit rates. +If a `+' follows immediately after then the station associated +using deprecated mechanisms supported only when +.Cm htcompat +is enabled. +.It Li P +Power Save. +Indicates that the station is operating in power save mode. +.It Li Q +Quality of Service (QoS). +Indicates that the station is using QoS encapsulation for +data frame. +QoS encapsulation is enabled only when WME mode is enabled. +.It Li S +Short Preamble. +Indicates that the station is doing short preamble to optionally +improve throughput performance with 802.11g and 802.11b. +.It Li T +Transitional Security Network (TSN). +Indicates that the station associated using TSN; see also +.Cm tsn +below. +.It Li W +Wi-Fi Protected Setup (WPS). +Indicates that the station associated using WPS. +.El +.Pp +By default information elements received from associated stations +are displayed in a short form; the +.Fl v +flag causes this information to be displayed symbolically. +.It Cm list wme +Display the current channel parameters to use when operating in WME mode. +If the +.Fl v +option is specified then both channel and BSS parameters are displayed +for each AC (first channel, then BSS). +When WME mode is enabled for an adaptor this information will be +displayed with the regular status; this command is mostly useful +for examining parameters when WME mode is disabled. +See the description of the +.Cm wme +directive for information on the various parameters. +.It Cm maxretry Ar count +Set the maximum number of tries to use in sending unicast frames. +The default setting is 6 but drivers may override this with a value +they choose. +.It Cm mcastrate Ar rate +Set the rate for transmitting multicast/broadcast frames. +Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s. +This rate should be valid for the current operating conditions; +if an invalid rate is specified drivers are free to chose an +appropriate rate. +.It Cm mgtrate Ar rate +Set the rate for transmitting management and/or control frames. +Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s. +.It Cm outdoor +Set the location to use in calculating regulatory constraints. +The location is also advertised in beacon and probe response frames +when 802.11d is enabled with +.Cm dotd . +See also +.Cm anywhere , +.Cm country , +.Cm indoor , +and +.Cm regdomain . +.It Cm powersave +Enable powersave operation. +When operating as a client, the station will conserve power by +periodically turning off the radio and listening for +messages from the access point telling it there are packets waiting. +The station must then retrieve the packets. +Not all devices support power save operation as a client. +The 802.11 specification requires that all access points support +power save but some drivers do not. +Use +.Fl powersave +to disable powersave operation when operating as a client. +.It Cm powersavesleep Ar sleep +Set the desired max powersave sleep time in TU's (1024 usecs). +By default the max powersave sleep time is 100 TU's. +.It Cm protmode Ar technique +For interfaces operating in 802.11g, use the specified +.Ar technique +for protecting OFDM frames in a mixed 11b/11g network. +The set of valid techniques is +.Cm off , cts +(CTS to self), +and +.Cm rtscts +(RTS/CTS). +Technique names are case insensitive. +Not all devices support +.Cm cts +as a protection technique. +.It Cm pureg +When operating as an access point in 802.11g mode allow only +11g-capable stations to associate (11b-only stations are not +permitted to associate). +To allow both 11g and 11b-only stations to associate, use +.Fl pureg . +.It Cm puren +When operating as an access point in 802.11n mode allow only +HT-capable stations to associate (legacy stations are not +permitted to associate). +To allow both HT and legacy stations to associate, use +.Fl puren . +.It Cm regdomain Ar sku +Set the regulatory domain to use in calculating the regulatory constraints +for operation. +In particular the set of available channels, how the wireless device +will operation on the channels, and the maximum transmit power that +can be used on a channel are defined by this setting. +Regdomain codes (SKU's) are taken from /etc/regdomain.xml and can also +be viewed with the ``list countries'' request. +Note that not all devices support changing the regdomain from a default +setting; typically stored in EEPROM. +See also +.Cm country , +.Cm indoor , +.Cm outdoor , +and +.Cm anywhere . +.It Cm rifs +Enable use of Reduced InterFrame Spacing (RIFS) when operating in 802.11n +on an HT channel. +Note that RIFS must be supported by both the station and access point +for it to be used. +To disable RIFS use +.Fl rifs . +.It Cm roam:rate Ar rate +Set the threshold for controlling roaming when operating in a BSS. +The +.Ar rate +parameter specifies the transmit rate in megabits +at which roaming should be considered. +If the current transmit rate drops below this setting and background scanning +is enabled, then the system will check if a more desirable access point is +available and switch over to it. +The current scan cache contents are used if they are considered +valid according to the +.Cm scanvalid +parameter; otherwise a background scan operation is triggered before +any selection occurs. +Each channel type has a separate rate threshold; the default values are: +12 Mb/s (11a), 2 Mb/s (11b), 2 Mb/s (11g), MCS 1 (11na, 11ng). +.It Cm roam:rssi Ar rssi +Set the threshold for controlling roaming when operating in a BSS. +The +.Ar rssi +parameter specifies the receive signal strength in dBm units +at which roaming should be considered. +If the current rssi drops below this setting and background scanning +is enabled, then the system will check if a more desirable access point is +available and switch over to it. +The current scan cache contents are used if they are considered +valid according to the +.Cm scanvalid +parameter; otherwise a background scan operation is triggered before +any selection occurs. +Each channel type has a separate rssi threshold; the default values are +all 7 dBm. +.It Cm roaming Ar mode +When operating as a station, control how the system will +behave when communication with the current access point +is broken. +The +.Ar mode +argument may be one of +.Cm device +(leave it to the hardware device to decide), +.Cm auto +(handle either in the device or the operating system\[em]as appropriate), +.Cm manual +(do nothing until explicitly instructed). +By default, the device is left to handle this if it is +capable; otherwise, the operating system will automatically +attempt to reestablish communication. +Manual mode is used by applications such as +.Xr wpa_supplicant 8 +that want to control the selection of an access point. +.It Cm rtsthreshold Ar length +Set the threshold for which +transmitted frames are preceded by transmission of an +RTS +control frame. +The +.Ar length +argument +is the frame size in bytes and must be in the range 1 to 2346. +Setting +.Ar length +to +.Li 2346 , +.Cm any , +or +.Cm - +disables transmission of RTS frames. +Not all adapters support setting the RTS threshold. +.It Cm scan +Initiate a scan of neighboring stations, wait for it to complete, and +display all stations found. +Only the super-user can initiate a scan. +See +.Cm list scan +for information on the display. +By default a background scan is done; otherwise a foreground +scan is done and the station may roam to a different access point. +The +.Cm list scan +request can be used to show recent scan results without +initiating a new scan. +.It Cm scanvalid Ar threshold +Set the maximum time the scan cache contents are considered valid; +i.e. will be used without first triggering a scan operation to +refresh the data. +The +.Ar threshold +parameter is specified in seconds and defaults to 60 seconds. +The minimum setting for +.Ar threshold +is 10 seconds. +One should take care setting this threshold; if it is set too low +then attempts to roam to another access point may trigger unnecessary +background scan operations. +.It Cm shortgi +Enable use of Short Guard Interval when operating in 802.11n +on an HT channel. +NB: this currently enables Short GI on both HT40 and HT20 channels. +To disable Short GI use +.Fl shortgi . +.It Cm smps +Enable use of Static Spatial Multiplexing Power Save (SMPS) +when operating in 802.11n. +A station operating with Static SMPS maintains only a single +receive chain active (this can significantly reduce power consumption). +To disable SMPS use +.Fl smps . +.It Cm smpsdyn +Enable use of Dynamic Spatial Multiplexing Power Save (SMPS) +when operating in 802.11n. +A station operating with Dynamic SMPS maintains only a single +receive chain active but switches to multiple receive chains when it +receives an RTS frame (this can significantly reduce power consumption). +Note that stations cannot distinguish between RTS/CTS intended to +enable multiple receive chains and those used for other purposes. +To disable SMPS use +.Fl smps . +.It Cm ssid Ar ssid +Set the desired Service Set Identifier (aka network name). +The SSID is a string up to 32 characters +in length and may be specified as either a normal string or in +hexadecimal when preceded by +.Ql 0x . +Additionally, the SSID may be cleared by setting it to +.Ql - . +.It Cm tdmaslot Ar slot +When operating with TDMA, use the specified +.Ar slot +configuration. +The +.Ar slot +is a number between 0 and the maximum number of slots in the BSS. +Note that a station configured as slot 0 is a master and +will broadcast beacon frames advertising the BSS; +stations configured to use other slots will always +scan to locate a master before they ever transmit. +By default +.Cm tdmaslot +is set to 1. +.It Cm tdmaslotcnt Ar cnt +When operating with TDMA, setup a BSS with +.Ar cnt +slots. +The slot count may be at most 8. +The current implementation is only tested with two stations +(i.e. point to point applications). +This setting is only meaningful when a station is configured as slot 0; +other stations adopt this setting from the BSS they join. +By default +.Cm tdmaslotcnt +is set to 2. +.It Cm tdmaslotlen Ar len +When operating with TDMA, setup a BSS such that each station has a slot +.Ar len +microseconds long. +The slot length must be at least 150 microseconds (1/8 TU) +and no more than 65 milliseconds. +Note that setting too small a slot length may result in poor channel +bandwidth utilization due to factors such as timer granularity and +guard time. +This setting is only meaningful when a station is configured as slot 0; +other stations adopt this setting from the BSS they join. +By default +.Cm tdmaslotlen +is set to 10 milliseconds. +.It Cm tdmabintval Ar intval +When operating with TDMA, setup a BSS such that beacons are transmitted every +.Ar intval +superframes to synchronize the TDMA slot timing. +A superframe is defined as the number of slots times the slot length; e.g. +a BSS with two slots of 10 milliseconds has a 20 millisecond superframe. +The beacon interval may not be zero. +A lower setting of +.Cm tdmabintval +causes the timers to be resynchronized more often; this can be help if +significant timer drift is observed. +By default +.Cm tdmabintval +is set to 5. +.It Cm tsn +When operating as an access point with WPA/802.11i allow legacy +stations to associate using static key WEP and open authentication. +To disallow legacy station use of WEP, use +.Fl tsn . +.It Cm txpower Ar power +Set the power used to transmit frames. +The +.Ar power +argument is specified in .5 dBm units. +Out of range values are truncated. +Typically only a few discreet power settings are available and +the driver will use the setting closest to the specified value. +Not all adapters support changing the transmit power. +.It Cm ucastrate Ar rate +Set a fixed rate for transmitting unicast frames. +Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s. +This rate should be valid for the current operating conditions; +if an invalid rate is specified drivers are free to chose an +appropriate rate. +.It Cm wepmode Ar mode +Set the desired WEP mode. +Not all adapters support all modes. +The set of valid modes is +.Cm off , on , +and +.Cm mixed . +The +.Cm mixed +mode explicitly tells the adaptor to allow association with access +points which allow both encrypted and unencrypted traffic. +On these adapters, +.Cm on +means that the access point must only allow encrypted connections. +On other adapters, +.Cm on +is generally another name for +.Cm mixed . +Modes are case insensitive. +.It Cm weptxkey Ar index +Set the WEP key to be used for transmission. +This is the same as setting the default transmission key with +.Cm deftxkey . +.It Cm wepkey Ar key Ns | Ns Ar index : Ns Ar key +Set the selected WEP key. +If an +.Ar index +is not given, key 1 is set. +A WEP key will be either 5 or 13 +characters (40 or 104 bits) depending of the local network and the +capabilities of the adaptor. +It may be specified either as a plain +string or as a string of hexadecimal digits preceded by +.Ql 0x . +For maximum portability, hex keys are recommended; +the mapping of text keys to WEP encryption is usually driver-specific. +In particular, the +.Tn Windows +drivers do this mapping differently to +.Fx . +A key may be cleared by setting it to +.Ql - . +If WEP is supported then there are at least four keys. +Some adapters support more than four keys. +If that is the case, then the first four keys +(1-4) will be the standard temporary keys and any others will be adaptor +specific keys such as permanent keys stored in NVRAM. +.Pp +Note that you must set a default transmit key with +.Cm deftxkey +for the system to know which key to use in encrypting outbound traffic. +.It Cm wme +Enable Wireless Multimedia Extensions (WME) support, if available, +for the specified interface. +WME is a subset of the IEEE 802.11e standard to support the +efficient communication of realtime and multimedia data. +To disable WME support, use +.Fl wme . +Another name for this parameter is +.Cm wmm . +.Pp +The following parameters are meaningful only when WME support is in use. +Parameters are specified per-AC (Access Category) and +split into those that are used by a station when acting +as an access point and those for client stations in the BSS. +The latter are received from the access point and may not be changed +(at the station). +The following Access Categories are recognized: +.Pp +.Bl -tag -width ".Cm AC_BK" -compact +.It Cm AC_BE +(or +.Cm BE ) +best effort delivery, +.It Cm AC_BK +(or +.Cm BK ) +background traffic, +.It Cm AC_VI +(or +.Cm VI ) +video traffic, +.It Cm AC_VO +(or +.Cm VO ) +voice traffic. +.El +.Pp +AC parameters are case-insensitive. +Traffic classification is done in the operating system using the +vlan priority associated with data frames or the +ToS (Type of Service) indication in IP-encapsulated frames. +If neither information is present, traffic is assigned to the +Best Effort (BE) category. +.Bl -tag -width indent +.It Cm ack Ar ac +Set the ACK policy for QoS transmissions by the local station; +this controls whether or not data frames transmitted by a station +require an ACK response from the receiving station. +To disable waiting for an ACK use +.Fl ack . +This parameter is applied only to the local station. +.It Cm acm Ar ac +Enable the Admission Control Mandatory (ACM) mechanism +for transmissions by the local station. +To disable the ACM use +.Fl acm . +On stations in a BSS this parameter is read-only and indicates +the setting received from the access point. +NB: ACM is not supported right now. +.It Cm aifs Ar ac Ar count +Set the Arbitration Inter Frame Spacing (AIFS) +channel access parameter to use for transmissions +by the local station. +On stations in a BSS this parameter is read-only and indicates +the setting received from the access point. +.It Cm cwmin Ar ac Ar count +Set the CWmin channel access parameter to use for transmissions +by the local station. +On stations in a BSS this parameter is read-only and indicates +the setting received from the access point. +.It Cm cwmax Ar ac Ar count +Set the CWmax channel access parameter to use for transmissions +by the local station. +On stations in a BSS this parameter is read-only and indicates +the setting received from the access point. +.It Cm txoplimit Ar ac Ar limit +Set the Transmission Opportunity Limit channel access parameter +to use for transmissions by the local station. +This parameter defines an interval of time when a WME station +has the right to initiate transmissions onto the wireless medium. +On stations in a BSS this parameter is read-only and indicates +the setting received from the access point. +.It Cm bss:aifs Ar ac Ar count +Set the AIFS channel access parameter to send to stations in a BSS. +This parameter is meaningful only when operating in ap mode. +.It Cm bss:cwmin Ar ac Ar count +Set the CWmin channel access parameter to send to stations in a BSS. +This parameter is meaningful only when operating in ap mode. +.It Cm bss:cwmax Ar ac Ar count +Set the CWmax channel access parameter to send to stations in a BSS. +This parameter is meaningful only when operating in ap mode. +.It Cm bss:txoplimit Ar ac Ar limit +Set the TxOpLimit channel access parameter to send to stations in a BSS. +This parameter is meaningful only when operating in ap mode. +.El +.It Cm wps +Enable Wireless Privacy Subscriber support. +Note that WPS support requires a WPS-capable supplicant. +To disable this function use +.Fl wps . +.El +.Pp +The following parameters support an optional access control list +feature available with some adapters when operating in ap mode; see +.Xr wlan_acl 4 . +This facility allows an access point to accept/deny association +requests based on the MAC address of the station. +Note that this feature does not significantly enhance security +as MAC address spoofing is easy to do. +.Bl -tag -width indent +.It Cm mac:add Ar address +Add the specified MAC address to the database. +Depending on the policy setting association requests from the +specified station will be allowed or denied. +.It Cm mac:allow +Set the ACL policy to permit association only by +stations registered in the database. +.It Cm mac:del Ar address +Delete the specified MAC address from the database. +.It Cm mac:deny +Set the ACL policy to deny association only by +stations registered in the database. +.It Cm mac:kick Ar address +Force the specified station to be deauthenticated. +This typically is done to block a station after updating the +address database. +.It Cm mac:open +Set the ACL policy to allow all stations to associate. +.It Cm mac:flush +Delete all entries in the database. +.It Cm mac:radius +Set the ACL policy to permit association only by +stations approved by a RADIUS server. +Note that this feature requires the +.Xr hostapd 8 +program be configured to do the right thing +as it handles the RADIUS processing +(and marks stations as authorized). +.El +.Pp +The following parameters are related to a wireless interface operating in mesh +mode: +.Bl -tag -width indent +.It Cm meshid Ar meshid +Set the desired Mesh Identifier. +The Mesh ID is a string up to 32 characters in length. +A mesh interface must have a Mesh Identifier specified +to reach an operational state. +.It Cm meshttl Ar ttl +Set the desired ``time to live'' for mesh forwarded packets; +this is the number of hops a packet may be forwarded before +it is discarded. +The default setting for +.Cm meshttl +is 31. +.It Cm meshpeering +Enable or disable peering with neighbor mesh stations. +Stations must peer before any data packets can be exchanged. +By default +.Cm meshpeering +is enabled. +.It Cm meshforward +Enable or disable forwarding packets by a mesh interface. +By default +.Cm meshforward +is enabled. +.It Cm meshmetric Ar protocol +Set the specified +.Ar protocol +as the link metric protocol used on a mesh network. +The default protocol is called +.Ar AIRTIME . +The mesh interface will restart after changing this setting. +.It Cm meshpath Ar protocol +Set the specified +.Ar protocol +as the path selection protocol used on a mesh network. +The only available protocol at the moment is called +.Ar HWMP +(Hybrid Wireless Mesh Protocol). +The mesh interface will restart after changing this setting. +.It Cm hwmprootmode Ar mode +Stations on a mesh network can operate as ``root nodes.'' +Root nodes try to find paths to all mesh nodes and advertise themselves +regularly. +When there is a root mesh node on a network, other mesh nodes can setup +paths between themselves faster because they can use the root node +to find the destination. +This path may not be the best, but on-demand +routing will eventually find the best path. +The following modes are recognized: +.Pp +.Bl -tag -width ".Cm PROACTIVE" -compact +.It Cm DISABLED +Disable root mode. +.It Cm NORMAL +Send broadcast path requests every two seconds. +Nodes on the mesh without a path to this root mesh station with try to +discover a path to us. +.It Cm PROACTIVE +Send broadcast path requests every two seconds and every node must reply with +with a path reply even if it already has a path to this root mesh station, +.It Cm RANN +Send broadcast root annoucement (RANN) frames. +Nodes on the mesh without a path to this root mesh station with try to +discover a path to us. +.El +By default +.Cm hwmprootmode +is set to +.Ar DISABLED . +.It Cm hwmpmaxhops Ar cnt +Set the maximum number of hops allowed in an HMWP path to +.Ar cnt . +The default setting for +.Cm hwmpmaxhops +is 31. +.El +.Pp +The following parameters are for compatibility with other systems: +.Bl -tag -width indent +.It Cm nwid Ar ssid +Another name for the +.Cm ssid +parameter. +Included for +.Nx +compatibility. +.It Cm stationname Ar name +Set the name of this station. +The station name is not part of the IEEE 802.11 +protocol though some interfaces support it. +As such it only +seems to be meaningful to identical or virtually identical equipment. +Setting the station name is identical in syntax to setting the SSID. +One can also use +.Cm station +for +.Bsx +compatibility. +.It Cm wep +Another way of saying +.Cm wepmode on . +Included for +.Bsx +compatibility. +.It Fl wep +Another way of saying +.Cm wepmode off . +Included for +.Bsx +compatibility. +.It Cm nwkey key +Another way of saying: +.Dq Li "wepmode on weptxkey 1 wepkey 1:key wepkey 2:- wepkey 3:- wepkey 4:-" . +Included for +.Nx +compatibility. +.It Cm nwkey Xo +.Sm off +.Ar n : k1 , k2 , k3 , k4 +.Sm on +.Xc +Another way of saying +.Dq Li "wepmode on weptxkey n wepkey 1:k1 wepkey 2:k2 wepkey 3:k3 wepkey 4:k4" . +Included for +.Nx +compatibility. +.It Fl nwkey +Another way of saying +.Cm wepmode off . +Included for +.Nx +compatibility. +.El +.Pp +The following parameters are specific to bridge interfaces: +.Bl -tag -width indent +.It Cm addm Ar interface +Add the interface named by +.Ar interface +as a member of the bridge. +The interface is put into promiscuous mode +so that it can receive every packet sent on the network. +.It Cm deletem Ar interface +Remove the interface named by +.Ar interface +from the bridge. +Promiscuous mode is disabled on the interface when +it is removed from the bridge. +.It Cm maxaddr Ar size +Set the size of the bridge address cache to +.Ar size . +The default is 100 entries. +.It Cm timeout Ar seconds +Set the timeout of address cache entries to +.Ar seconds +seconds. +If +.Ar seconds +is zero, then address cache entries will not be expired. +The default is 240 seconds. +.It Cm addr +Display the addresses that have been learned by the bridge. +.It Cm static Ar interface-name Ar address +Add a static entry into the address cache pointing to +.Ar interface-name . +Static entries are never aged out of the cache or re-placed, even if the +address is seen on a different interface. +.It Cm deladdr Ar address +Delete +.Ar address +from the address cache. +.It Cm flush +Delete all dynamically-learned addresses from the address cache. +.It Cm flushall +Delete all addresses, including static addresses, from the address cache. +.It Cm discover Ar interface +Mark an interface as a +.Dq discovering +interface. +When the bridge has no address cache entry +(either dynamic or static) +for the destination address of a packet, +the bridge will forward the packet to all +member interfaces marked as +.Dq discovering . +This is the default for all interfaces added to a bridge. +.It Cm -discover Ar interface +Clear the +.Dq discovering +attribute on a member interface. +For packets without the +.Dq discovering +attribute, the only packets forwarded on the interface are broadcast +or multicast packets and packets for which the destination address +is known to be on the interface's segment. +.It Cm learn Ar interface +Mark an interface as a +.Dq learning +interface. +When a packet arrives on such an interface, the source +address of the packet is entered into the address cache as being a +destination address on the interface's segment. +This is the default for all interfaces added to a bridge. +.It Cm -learn Ar interface +Clear the +.Dq learning +attribute on a member interface. +.It Cm sticky Ar interface +Mark an interface as a +.Dq sticky +interface. +Dynamically learned address entries are treated at static once entered into +the cache. +Sticky entries are never aged out of the cache or replaced, even if the +address is seen on a different interface. +.It Cm -sticky Ar interface +Clear the +.Dq sticky +attribute on a member interface. +.It Cm private Ar interface +Mark an interface as a +.Dq private +interface. +A private interface does not forward any traffic to any other port that is also +a private interface. +.It Cm -private Ar interface +Clear the +.Dq private +attribute on a member interface. +.It Cm span Ar interface +Add the interface named by +.Ar interface +as a span port on the bridge. +Span ports transmit a copy of every frame received by the bridge. +This is most useful for snooping a bridged network passively on +another host connected to one of the span ports of the bridge. +.It Cm -span Ar interface +Delete the interface named by +.Ar interface +from the list of span ports of the bridge. +.It Cm stp Ar interface +Enable Spanning Tree protocol on +.Ar interface . +The +.Xr if_bridge 4 +driver has support for the IEEE 802.1D Spanning Tree protocol (STP). +Spanning Tree is used to detect and remove loops in a network topology. +.It Cm -stp Ar interface +Disable Spanning Tree protocol on +.Ar interface . +This is the default for all interfaces added to a bridge. +.It Cm edge Ar interface +Set +.Ar interface +as an edge port. +An edge port connects directly to end stations cannot create bridging +loops in the network, this allows it to transition straight to forwarding. +.It Cm -edge Ar interface +Disable edge status on +.Ar interface . +.It Cm autoedge Ar interface +Allow +.Ar interface +to automatically detect edge status. +This is the default for all interfaces added to a bridge. +.It Cm -autoedge Ar interface +Disable automatic edge status on +.Ar interface . +.It Cm ptp Ar interface +Set the +.Ar interface +as a point to point link. +This is required for straight transitions to forwarding and +should be enabled on a direct link to another RSTP capable switch. +.It Cm -ptp Ar interface +Disable point to point link status on +.Ar interface . +This should be disabled for a half duplex link and for an interface +connected to a shared network segment, +like a hub or a wireless network. +.It Cm autoptp Ar interface +Automatically detect the point to point status on +.Ar interface +by checking the full duplex link status. +This is the default for interfaces added to the bridge. +.It Cm -autoptp Ar interface +Disable automatic point to point link detection on +.Ar interface . +.It Cm maxage Ar seconds +Set the time that a Spanning Tree protocol configuration is valid. +The default is 20 seconds. +The minimum is 6 seconds and the maximum is 40 seconds. +.It Cm fwddelay Ar seconds +Set the time that must pass before an interface begins forwarding +packets when Spanning Tree is enabled. +The default is 15 seconds. +The minimum is 4 seconds and the maximum is 30 seconds. +.It Cm hellotime Ar seconds +Set the time between broadcasting of Spanning Tree protocol +configuration messages. +The hello time may only be changed when operating in legacy stp mode. +The default is 2 seconds. +The minimum is 1 second and the maximum is 2 seconds. +.It Cm priority Ar value +Set the bridge priority for Spanning Tree. +The default is 32768. +The minimum is 0 and the maximum is 61440. +.It Cm proto Ar value +Set the Spanning Tree protocol. +The default is rstp. +The available options are stp and rstp. +.It Cm holdcnt Ar value +Set the transmit hold count for Spanning Tree. +This is the number of packets transmitted before being rate limited. +The default is 6. +The minimum is 1 and the maximum is 10. +.It Cm ifpriority Ar interface Ar value +Set the Spanning Tree priority of +.Ar interface +to +.Ar value . +The default is 128. +The minimum is 0 and the maximum is 240. +.It Cm ifpathcost Ar interface Ar value +Set the Spanning Tree path cost of +.Ar interface +to +.Ar value . +The default is calculated from the link speed. +To change a previously selected path cost back to automatic, set the +cost to 0. +The minimum is 1 and the maximum is 200000000. +.It Cm ifmaxaddr Ar interface Ar size +Set the maximum number of hosts allowed from an interface, packets with unknown +source addresses are dropped until an existing host cache entry expires or is +removed. +Set to 0 to disable. +.El +.Pp +The following parameters are specific to lagg interfaces: +.Bl -tag -width indent +.It Cm laggport Ar interface +Add the interface named by +.Ar interface +as a port of the aggregation interface. +.It Cm -laggport Ar interface +Remove the interface named by +.Ar interface +from the aggregation interface. +.It Cm laggproto Ar proto +Set the aggregation protocol. +The default is failover. +The available options are failover, fec, lacp, loadbalance, roundrobin and +none. +.El +.Pp +The following parameters are specific to IP tunnel interfaces, +.Xr gif 4 : +.Bl -tag -width indent +.It Cm tunnel Ar src_addr dest_addr +Configure the physical source and destination address for IP tunnel +interfaces. +The arguments +.Ar src_addr +and +.Ar dest_addr +are interpreted as the outer source/destination for the encapsulating +IPv4/IPv6 header. +.It Fl tunnel +Unconfigure the physical source and destination address for IP tunnel +interfaces previously configured with +.Cm tunnel . +.It Cm deletetunnel +Another name for the +.Fl tunnel +parameter. +.It Cm accept_rev_ethip_ver +Set a flag to acccept both correct EtherIP packets and ones +with reversed version field. Enabled by default. +This is for backward compatibility with +.Fx 6.1 , +6.2, 6.3, 7.0, and 7.1. +.It Cm -accept_rev_ethip_ver +Clear a flag +.Cm accept_rev_ethip_ver . +.It Cm send_rev_ethip_ver +Set a flag to send EtherIP packets with reversed version +field intentionally. Disabled by default. +This is for backward compatibility with +.Fx 6.1 , +6.2, 6.3, 7.0, and 7.1. +.It Cm -send_rev_ethip_ver +Clear a flag +.Cm send_rev_ethip_ver . +.El +.Pp +The following parameters are specific to GRE tunnel interfaces, +.Xr gre 4 : +.Bl -tag -width indent +.It Cm grekey Ar key +Configure the GRE key to be used for outgoing packets. +Note that +.Xr gre 4 will always accept GRE packets with invalid or absent keys. +This command will result in a four byte MTU reduction on the interface. +.El +.Pp +The following parameters are specific to +.Xr pfsync 4 +interfaces: +.Bl -tag -width indent +.It Cm maxupd Ar n +Set the maximum number of updates for a single state which +can be collapsed into one. +This is an 8-bit number; the default value is 128. +.El +.Pp +The following parameters are specific to +.Xr vlan 4 +interfaces: +.Bl -tag -width indent +.It Cm vlan Ar vlan_tag +Set the VLAN tag value to +.Ar vlan_tag . +This value is a 16-bit number which is used to create an 802.1Q +VLAN header for packets sent from the +.Xr vlan 4 +interface. +Note that +.Cm vlan +and +.Cm vlandev +must both be set at the same time. +.It Cm vlandev Ar iface +Associate the physical interface +.Ar iface +with a +.Xr vlan 4 +interface. +Packets transmitted through the +.Xr vlan 4 +interface will be +diverted to the specified physical interface +.Ar iface +with 802.1Q VLAN encapsulation. +Packets with 802.1Q encapsulation received +by the parent interface with the correct VLAN tag will be diverted to +the associated +.Xr vlan 4 +pseudo-interface. +The +.Xr vlan 4 +interface is assigned a +copy of the parent interface's flags and the parent's ethernet address. +The +.Cm vlandev +and +.Cm vlan +must both be set at the same time. +If the +.Xr vlan 4 +interface already has +a physical interface associated with it, this command will fail. +To +change the association to another physical interface, the existing +association must be cleared first. +.Pp +Note: if the hardware tagging capability +is set on the parent interface, the +.Xr vlan 4 +pseudo +interface's behavior changes: +the +.Xr vlan 4 +interface recognizes that the +parent interface supports insertion and extraction of VLAN tags on its +own (usually in firmware) and that it should pass packets to and from +the parent unaltered. +.It Fl vlandev Op Ar iface +If the driver is a +.Xr vlan 4 +pseudo device, disassociate the parent interface from it. +This breaks the link between the +.Xr vlan 4 +interface and its parent, +clears its VLAN tag, flags and its link address and shuts the interface down. +The +.Ar iface +argument is useless and hence deprecated. +.El +.Pp +The following parameters are specific to +.Xr carp 4 +interfaces: +.Bl -tag -width indent +.It Cm advbase Ar seconds +Specifies the base of the advertisement interval in seconds. +The acceptable values are 1 to 255. +The default value is 1. +.\" The default value is +.\" .Dv CARP_DFLTINTV . +.It Cm advskew Ar interval +Specifies the skew to add to the base advertisement interval to +make one host advertise slower than another host. +It is specified in 1/256 of seconds. +The acceptable values are 1 to 254. +The default value is 0. +.It Cm pass Ar phrase +Set the authentication key to +.Ar phrase . +.It Cm vhid Ar n +Set the virtual host ID. +This is a required setting. +Acceptable values are 1 to 255. +.El +.Pp +The +.Nm +utility displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +.Nm +will report only the details specific to that protocol family. +.Pp +If the +.Fl m +flag is passed before an interface name, +.Nm +will display the capability list and all +of the supported media for the specified interface. +If +.Fl L +flag is supplied, address lifetime is displayed for IPv6 addresses, +as time offset string. +.Pp +Optionally, the +.Fl a +flag may be used instead of an interface name. +This flag instructs +.Nm +to display information about all interfaces in the system. +The +.Fl d +flag limits this to interfaces that are down, and +.Fl u +limits this to interfaces that are up. +When no arguments are given, +.Fl a +is implied. +.Pp +The +.Fl l +flag may be used to list all available interfaces on the system, with +no other additional information. +Use of this flag is mutually exclusive +with all other flags and commands, except for +.Fl d +(only list interfaces that are down) +and +.Fl u +(only list interfaces that are up). +.Pp +The +.Fl v +flag may be used to get more verbose status for an interface. +.Pp +The +.Fl C +flag may be used to list all of the interface cloners available on +the system, with no additional information. +Use of this flag is mutually exclusive with all other flags and commands. +.Pp +The +.Fl k +flag causes keying information for the interface, if available, to be +printed. +For example, the values of 802.11 WEP keys will be printed, if accessible to +the current user. +This information is not printed by default, as it may be considered +sensitive. +.Pp +If the network interface driver is not present in the kernel then +.Nm +will attempt to load it. +The +.Fl n +flag disables this behavior. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh NOTES +The media selection system is relatively new and only some drivers support +it (or have need for it). +.Sh EXAMPLES +Assign the IPv4 address +.Li 192.0.2.10 , +with a network mask of +.Li 255.255.255.0 , +to the interface +.Li fxp0 : +.Dl # ifconfig fxp0 inet 192.0.2.10 netmask 255.255.255.0 +.Pp +Add the IPv4 address +.Li 192.0.2.45 , +with the CIDR network prefix +.Li /28 , +to the interface +.Li ed0 , +using +.Cm add +as a synonym for the canonical form of the option +.Cm alias : +.Dl # ifconfig ed0 inet 192.0.2.45/28 add +.Pp +Remove the IPv4 address +.Li 192.0.2.45 +from the interface +.Li ed0 : +.Dl # ifconfig ed0 inet 192.0.2.45 -alias +.Pp +Add the IPv6 address +.Li 2001:DB8:DBDB::123/48 +to the interface +.Li em0 : +.Dl # ifconfig em0 inet6 2001:db8:bdbd::123 prefixlen 48 alias +Note that lower case hexadecimal IPv6 addresses are acceptable. +.Pp +Remove the IPv6 address added in the above example, +using the +.Li / +character as shorthand for the network prefix, +and using +.Cm delete +as a synonym for the canonical form of the option +.Fl alias : +.Dl # ifconfig em0 inet6 2001:db8:bdbd::123/48 delete +.Pp +Configure the interface +.Li xl0 , +to use 100baseTX, full duplex Ethernet media options: +.Dl # ifconfig xl0 media 100baseTX mediaopt full-duplex +.Pp +Label the em0 interface as an uplink: +.Pp +.Dl # ifconfig em0 description \&"Uplink to Gigabit Switch 2\&" +.Pp +Create the software network interface +.Li gif1 : +.Dl # ifconfig gif1 create +.Pp +Destroy the software network interface +.Li gif1 : +.Dl # ifconfig gif1 destroy +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exist, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr carp 4 , +.Xr gif 4 , +.Xr netintro 4 , +.Xr pfsync 4 , +.Xr polling 4 , +.Xr vlan 4 , +.\" .Xr eon 5 , +.Xr rc 8 , +.Xr routed 8 , +.Xr jail 8 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . +.Sh BUGS +Basic IPv6 node operation requires a link-local address on each +interface configured for IPv6. +Normally, such an address is automatically configured by the +kernel on each interface added to the system; this behaviour may +be disabled by setting the sysctl MIB variable +.Va net.inet6.ip6.auto_linklocal +to 0. +.Pp +If you delete such an address using +.Nm , +the kernel may act very odd. +Do this at your own risk. diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifconfig.c b/freebsd-userspace/commands/sbin/ifconfig/ifconfig.c new file mode 100644 index 00000000..f0c77d11 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifconfig.c @@ -0,0 +1,1185 @@ +/* + * 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. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> +#ifdef __rtems__ +#include <freebsd/sys/module.h> +#include <freebsd/sys/linker.h> +#else +#include <sys/module.h> +#include <sys/linker.h> +#endif + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/net/if_var.h> +#else +#include <net/if_var.h> +#endif +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +/* IP */ +#include <netinet/in.h> +#ifdef __rtems__ +#include <freebsd/netinet/in_var.h> +#else +#include <netinet/in_var.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> + +#include <ifaddrs.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> + +#ifndef __rtems__ +#include <jail.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +/* + * 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]; +char *descr = NULL; +size_t descrlen = 64; +int setaddr; +int setmask; +int doalias; +int clearaddr; +int newaddr = 1; +int verbose; +int noload; + +int supmedia = 0; +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 void tunnel_status(int s); +static void usage(void); + +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, all, 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 = verbose = 0; + + /* Parse leading line options */ + strlcpy(options, "adklmnuv", 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 */ + all++; + break; + case 'd': /* restrict scan to "down" interfaces */ + downonly++; + break; + case 'k': + printkeys++; + break; + case 'l': /* scan interface names only */ + namesonly++; + break; + case 'm': /* show media choices in status */ + supmedia = 1; + break; + case 'n': /* suppress module loading */ + noload++; + 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 -m */ + if (namesonly && (all || supmedia)) + 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; + ifindex = 0; + 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++; + + /* check and maybe load support for this interface */ + ifmaybeload(ifname); + + 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); + } + /* + * 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 (argc > 0 && (strcmp(argv[0], "-vnet") == 0)) { + iflen = strlcpy(name, ifname, sizeof(name)); + if (iflen >= sizeof(name)) + errx(1, "%s: interface name too long", + ifname); + ifconfig(argc, argv, 0, 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)); + strncpy(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, int iscreate) +{ +#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) { + if (iscreate) { + if (p->c_iscloneop) + return p; + } else { + if (!p->c_iscloneop) + 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 *uafp) +{ + const struct afswtch *afp, *nafp; + const struct cmd *p; + struct callback *cb; + int s; + + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + afp = uafp != NULL ? uafp : af_getbyname("inet"); +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 != EPROTONOSUPPORT || + (s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)) + err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); + + while (argc > 0) { + p = cmd_lookup(*argv, iscreate); + if (iscreate && p == NULL) { + /* + * Push the clone create callback so the new + * device is created and can be used for any + * remaining arguments. + */ + cb = callbacks; + if (cb == NULL) + errx(1, "internal error, no callback"); + callbacks = cb->cb_next; + cb->cb_func(s, cb->cb_arg); + iscreate = 0; + /* + * Handle any address family spec that + * immediately follows and potentially + * recreate the socket. + */ + nafp = af_getbyname(*argv); + if (nafp != NULL) { + argc--, argv++; + if (nafp != afp) { + close(s); + afp = nafp; + goto top; + } + } + /* + * Look for a normal parameter. + */ + continue; + } + if (p == NULL) { + /* + * Not a recognized command, choose between setting + * the interface address and the dst address. + */ + p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); + } + if (p->c_u.c_func || p->c_u.c_func2) { + 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 + 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) { + int ret; + strncpy(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)) { + strncpy(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 +setifvnet(const char *jname, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifreq my_ifr; + + memcpy(&my_ifr, &ifr, sizeof(my_ifr)); + my_ifr.ifr_jid = jail_getid(jname); +#ifndef __rtems__ + if (my_ifr.ifr_jid < 0) + errx(1, "%s", jail_errmsg); +#endif + if (ioctl(s, SIOCSIFVNET, &my_ifr) < 0) + err(1, "SIOCSIFVNET"); +} + +static void +setifrvnet(const char *jname, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifreq my_ifr; + + memcpy(&my_ifr, &ifr, sizeof(my_ifr)); + my_ifr.ifr_jid = jail_getid(jname); +#ifndef __rtems__ + if (my_ifr.ifr_jid < 0) + errx(1, "%s", jail_errmsg); +#endif + if (ioctl(s, SIOCSIFRVNET, &my_ifr) < 0) + err(1, "SIOCSIFRVNET(%d, %s)", my_ifr.ifr_jid, my_ifr.ifr_name); +} + +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; + + memset(&my_ifr, 0, sizeof(my_ifr)); + (void) strlcpy(my_ifr.ifr_name, name, sizeof(my_ifr.ifr_name)); + + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + flags = (my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16); + + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + my_ifr.ifr_flags = flags & 0xffff; + my_ifr.ifr_flagshigh = flags >> 16; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) + Perror(vname); +} + +void +setifcap(const char *vname, int value, int s, const struct afswtch *afp) +{ + int flags; + + if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFCAP)"); + exit(1); + } + flags = ifr.ifr_curcap; + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + flags &= ifr.ifr_reqcap; + 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) +{ + strncpy(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) +{ + strncpy(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)"); +} + +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); +} + +/* ARGSUSED */ +static void +setifdescr(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + 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(s, SIOCSIFDESCR, (caddr_t)&ifr) < 0) + warn("ioctl (set descr)"); + + free(newdescr); +} + +/* ARGSUSED */ +static void +unsetifdescr(const char *val, int value, int s, const struct afswtch *afp) +{ + + setifdescr("", 0, s, 0); +} + +#define IFFBITS \ +"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ +"\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ +"\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP" + +#define IFCAPBITS \ +"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ +"\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ +"\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE" + +/* + * 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; + + if (afp == NULL) { + allfamilies = 1; + ifr.ifr_addr.sa_family = AF_LOCAL; + } else { + allfamilies = 0; + ifr.ifr_addr.sa_family = + afp->af_af == AF_LINK ? AF_LOCAL : afp->af_af; + } + strncpy(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) + printf(" metric %d", ifr.ifr_metric); + if (ioctl(s, SIOCGIFMTU, &ifr) != -1) + printf(" mtu %d", ifr.ifr_mtu); + putchar('\n'); + + for (;;) { + if ((descr = reallocf(descr, descrlen)) != NULL) { + ifr.ifr_buffer.buffer = descr; + ifr.ifr_buffer.length = descrlen; + if (ioctl(s, SIOCGIFDESCR, &ifr) == 0) { + if (ifr.ifr_buffer.buffer == descr) { + if (strlen(descr) > 0) + printf("\tdescription: %s\n", + descr); + } else if (ifr.ifr_buffer.length > descrlen) { + descrlen = ifr.ifr_buffer.length; + continue; + } + } + } else + warn("unable to allocate memory for interface" + "description"); + break; + } + + if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { + if (ifr.ifr_curcap != 0) { + 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); + } +#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); + + strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name); + if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) + printf("%s", ifs.ascii); + + close(s); + return; +} + +static void +tunnel_status(int s) +{ + af_all_tunnel_status(s); +} + +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('>'); + } +} + +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 */ + strcpy(ifkind, "if_"); + 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); +} + +static struct cmd basic_cmds[] = { + DEF_CMD("up", IFF_UP, setifflags), + DEF_CMD("down", -IFF_UP, setifflags), + DEF_CMD("arp", -IFF_NOARP, setifflags), + DEF_CMD("-arp", IFF_NOARP, setifflags), + DEF_CMD("debug", IFF_DEBUG, setifflags), + DEF_CMD("-debug", -IFF_DEBUG, setifflags), + DEF_CMD_ARG("description", setifdescr), + DEF_CMD_ARG("descr", setifdescr), + DEF_CMD("-description", 0, unsetifdescr), + DEF_CMD("-descr", 0, unsetifdescr), + DEF_CMD("promisc", IFF_PPROMISC, setifflags), + DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), + DEF_CMD("add", IFF_UP, notealias), + DEF_CMD("alias", IFF_UP, notealias), + DEF_CMD("-alias", -IFF_UP, notealias), + DEF_CMD("delete", -IFF_UP, notealias), + DEF_CMD("remove", -IFF_UP, notealias), +#ifdef notdef +#define EN_SWABIPS 0x1000 + DEF_CMD("swabips", EN_SWABIPS, setifflags), + DEF_CMD("-swabips", -EN_SWABIPS, setifflags), +#endif + DEF_CMD_ARG("netmask", setifnetmask), + DEF_CMD_ARG("metric", setifmetric), + DEF_CMD_ARG("broadcast", setifbroadaddr), + DEF_CMD_ARG("ipdst", setifipdst), + DEF_CMD_ARG2("tunnel", settunnel), + DEF_CMD("-tunnel", 0, deletetunnel), + DEF_CMD("deletetunnel", 0, deletetunnel), + DEF_CMD_ARG("vnet", setifvnet), + DEF_CMD_ARG("-vnet", setifrvnet), + DEF_CMD("link0", IFF_LINK0, setifflags), + DEF_CMD("-link0", -IFF_LINK0, setifflags), + DEF_CMD("link1", IFF_LINK1, setifflags), + DEF_CMD("-link1", -IFF_LINK1, setifflags), + DEF_CMD("link2", IFF_LINK2, setifflags), + DEF_CMD("-link2", -IFF_LINK2, setifflags), + DEF_CMD("monitor", IFF_MONITOR, setifflags), + DEF_CMD("-monitor", -IFF_MONITOR, setifflags), + DEF_CMD("staticarp", IFF_STATICARP, setifflags), + DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), + DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), + DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), + DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), + DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), + DEF_CMD("netcons", IFCAP_NETCONS, setifcap), + DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), + DEF_CMD("polling", IFCAP_POLLING, setifcap), + DEF_CMD("-polling", -IFCAP_POLLING, setifcap), + DEF_CMD("tso", IFCAP_TSO, setifcap), + DEF_CMD("-tso", -IFCAP_TSO, setifcap), + DEF_CMD("lro", IFCAP_LRO, setifcap), + DEF_CMD("-lro", -IFCAP_LRO, setifcap), + DEF_CMD("wol", IFCAP_WOL, setifcap), + DEF_CMD("-wol", -IFCAP_WOL, setifcap), + DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), + DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), + DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), + DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), + DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), + DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), + DEF_CMD("normal", -IFF_LINK0, setifflags), + DEF_CMD("compress", IFF_LINK0, setifflags), + DEF_CMD("noicmp", IFF_LINK1, setifflags), + DEF_CMD_ARG("mtu", setifmtu), + DEF_CMD_ARG("name", setifname), +}; + +static __constructor void +ifconfig_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(basic_cmds); i++) + cmd_register(&basic_cmds[i]); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifconfig.h b/freebsd-userspace/commands/sbin/ifconfig/ifconfig.h new file mode 100644 index 00000000..a624f602 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifconfig.h @@ -0,0 +1,154 @@ +/* + * 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$ + */ + +#ifdef __rtems__ +#include <freebsd/sys/sockio.h> +#endif + +#define __constructor __attribute__((constructor)) + +struct afswtch; +struct cmd; + +typedef void c_func(const char *cmd, int arg, int s, const struct afswtch *afp); +typedef void c_func2(const char *arg1, const char *arg2, int s, const struct afswtch *afp); + +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 */ + union { + c_func *c_func; + c_func2 *c_func2; + } c_u; + int c_iscloneop; + struct cmd *c_next; +}; +void cmd_register(struct cmd *); + +typedef void callback_func(int s, void *); +void callback_register(callback_func *, void *); + +/* + * Macros for declaring command functions and initializing entries. + */ +#define DECL_CMD_FUNC(name, cmd, arg) \ + void name(const char *cmd, int arg, int s, const struct afswtch *afp) +#define DECL_CMD_FUNC2(name, arg1, arg2) \ + void name(const char *arg1, const char *arg2, int s, const struct afswtch *afp) + +#define DEF_CMD(name, param, func) { name, param, { .c_func = func }, 0, NULL } +#define DEF_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func }, 0, NULL } +#define DEF_CMD_OPTARG(name, func) { name, OPTARG, { .c_func = func }, 0, NULL } +#define DEF_CMD_ARG2(name, func) { name, NEXTARG2, { .c_func2 = func }, 0, NULL } +#define DEF_CLONE_CMD(name, param, func) { name, param, { .c_func = func }, 1, NULL } +#define DEF_CLONE_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func }, 1, NULL } + +struct ifaddrs; +struct addrinfo; + +enum { + RIDADDR, + ADDR, + MASK, + DSTADDR, +}; + +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. + */ + void (*af_status)(int, const struct ifaddrs *); + void (*af_other_status)(int); + /* parse address method */ + void (*af_getaddr)(const char *, int); + /* parse prefix method (IPv6) */ + void (*af_getprefix)(const char *, int); + void (*af_postproc)(int s, const struct afswtch *); + u_long af_difaddr; /* set dst if address ioctl */ + u_long af_aifaddr; /* set if address ioctl */ + void *af_ridreq; /* */ + void *af_addreq; /* */ + struct afswtch *af_next; + + /* XXX doesn't fit model */ + void (*af_status_tunnel)(int); + void (*af_settunnel)(int s, struct addrinfo *srcres, + struct addrinfo *dstres); +}; +void af_register(struct afswtch *); + +struct option { + const char *opt; + const char *opt_usage; + void (*cb)(const char *arg); + struct option *next; +}; +void opt_register(struct option *); + +extern struct ifreq ifr; +extern char name[IFNAMSIZ]; /* name of interface */ +extern int allmedia; +extern int supmedia; +extern int printkeys; +extern int newaddr; +extern int verbose; + +void setifcap(const char *, int value, int s, const struct afswtch *); + +void Perror(const char *cmd); +void printb(const char *s, unsigned value, const char *bits); + +void ifmaybeload(const char *name); + +typedef void clone_callback_func(int, struct ifreq *); +void clone_setdefcallback(const char *, clone_callback_func *); + +/* + * XXX expose this so modules that neeed to know of any pending + * operations on ifmedia can avoid cmd line ordering confusion. + */ +struct ifmediareq *ifmedia_getstate(int s); diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifgif.c b/freebsd-userspace/commands/sbin/ifconfig/ifgif.c new file mode 100644 index 00000000..d23b384b --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifgif.c @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2009 Hiroki Sato. 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. + * + * 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 OR HIS RELATIVES 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 MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/net/if_gif.h> +#else +#include <net/if_gif.h> +#endif +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +static void gif_status(int); + +static struct { + const char *label; + u_int mask; +} gif_opts[] = { + { "ACCEPT_REV_ETHIP_VER", GIF_ACCEPT_REVETHIP }, + { "SEND_REV_ETHIP_VER", GIF_SEND_REVETHIP }, +}; + +static void +gif_status(int s) +{ + int opts; + int nopts = 0; + size_t i; + + ifr.ifr_data = (caddr_t)&opts; + if (ioctl(s, GIFGOPTS, &ifr) == -1) + return; + if (opts == 0) + return; + + printf("\toptions=%d<", opts); + for (i=0; i < sizeof(gif_opts)/sizeof(gif_opts[0]); i++) { + if (opts & gif_opts[i].mask) { + if (nopts++) + printf(","); + printf("%s", gif_opts[i].label); + } + } + printf(">\n"); +} + +static void +setgifopts(const char *val, + int d, int s, const struct afswtch *afp) +{ + int opts; + + ifr.ifr_data = (caddr_t)&opts; + if (ioctl(s, GIFGOPTS, &ifr) == -1) { + warn("ioctl(GIFGOPTS)"); + return; + } + + if (d < 0) + opts &= ~(-d); + else + opts |= d; + + if (ioctl(s, GIFSOPTS, &ifr) == -1) { + warn("ioctl(GIFSOPTS)"); + return; + } +} + +static struct cmd gif_cmds[] = { + DEF_CMD("accept_rev_ethip_ver", GIF_ACCEPT_REVETHIP, setgifopts), + DEF_CMD("-accept_rev_ethip_ver",-GIF_ACCEPT_REVETHIP, setgifopts), + DEF_CMD("send_rev_ethip_ver", GIF_SEND_REVETHIP, setgifopts), + DEF_CMD("-send_rev_ethip_ver", -GIF_SEND_REVETHIP, setgifopts), +}; + +static struct afswtch af_gif = { + .af_name = "af_gif", + .af_af = AF_UNSPEC, + .af_other_status = gif_status, +}; + +static __constructor void +gif_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(gif_cmds); i++) + cmd_register(&gif_cmds[i]); + af_register(&af_gif); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifgre.c b/freebsd-userspace/commands/sbin/ifconfig/ifgre.c new file mode 100644 index 00000000..58d5bcf5 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifgre.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2008 Andrew Thompson. 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. + * + * 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 OR HIS RELATIVES 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 MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/net/if_gre.h> +#else +#include <net/if_gre.h> +#endif +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +static void gre_status(int s); + +static void +gre_status(int s) +{ + int grekey = 0; + + ifr.ifr_data = (caddr_t)&grekey; + if (ioctl(s, GREGKEY, &ifr) == 0) + if (grekey != 0) + printf("\tgrekey: %d\n", grekey); +} + +static void +setifgrekey(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + uint32_t grekey = atol(val); + + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_data = (caddr_t)&grekey; + if (ioctl(s, GRESKEY, (caddr_t)&ifr) < 0) + warn("ioctl (set grekey)"); +} + +static struct cmd gre_cmds[] = { + DEF_CMD_ARG("grekey", setifgrekey), +}; +static struct afswtch af_gre = { + .af_name = "af_gre", + .af_af = AF_UNSPEC, + .af_other_status = gre_status, +}; + +static __constructor void +gre_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(gre_cmds); i++) + cmd_register(&gre_cmds[i]); + af_register(&af_gre); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifgroup.c b/freebsd-userspace/commands/sbin/ifconfig/ifgroup.c new file mode 100644 index 00000000..9eaac3b6 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifgroup.c @@ -0,0 +1,186 @@ +/*- + * Copyright (c) 2006 Max Laier. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +/* ARGSUSED */ +static void +setifgroup(const char *group_name, int d, int s, const struct afswtch *rafp) +{ + struct ifgroupreq ifgr; + + memset(&ifgr, 0, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, name, IFNAMSIZ); + + if (group_name[0] && isdigit(group_name[strlen(group_name) - 1])) + errx(1, "setifgroup: group names may not end in a digit"); + + if (strlcpy(ifgr.ifgr_group, group_name, IFNAMSIZ) >= IFNAMSIZ) + errx(1, "setifgroup: group name too long"); + if (ioctl(s, SIOCAIFGROUP, (caddr_t)&ifgr) == -1) + err(1," SIOCAIFGROUP"); +} + +/* ARGSUSED */ +static void +unsetifgroup(const char *group_name, int d, int s, const struct afswtch *rafp) +{ + struct ifgroupreq ifgr; + + memset(&ifgr, 0, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, name, IFNAMSIZ); + + if (group_name[0] && isdigit(group_name[strlen(group_name) - 1])) + errx(1, "unsetifgroup: group names may not end in a digit"); + + if (strlcpy(ifgr.ifgr_group, group_name, IFNAMSIZ) >= IFNAMSIZ) + errx(1, "unsetifgroup: group name too long"); + if (ioctl(s, SIOCDIFGROUP, (caddr_t)&ifgr) == -1) + err(1, "SIOCDIFGROUP"); +} + +static void +getifgroups(int s) +{ + int len, cnt; + struct ifgroupreq ifgr; + struct ifg_req *ifg; + + if (!verbose) + return; + + memset(&ifgr, 0, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, name, IFNAMSIZ); + + if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { + if (errno == EINVAL || errno == ENOTTY) + return; + else + err(1, "SIOCGIFGROUP"); + } + + len = ifgr.ifgr_len; + ifgr.ifgr_groups = + (struct ifg_req *)calloc(len / sizeof(struct ifg_req), + sizeof(struct ifg_req)); + if (ifgr.ifgr_groups == NULL) + err(1, "getifgroups"); + if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) + err(1, "SIOCGIFGROUP"); + + cnt = 0; + ifg = ifgr.ifgr_groups; + for (; ifg && len >= sizeof(struct ifg_req); ifg++) { + len -= sizeof(struct ifg_req); + if (strcmp(ifg->ifgrq_group, "all")) { + if (cnt == 0) + printf("\tgroups: "); + cnt++; + printf("%s ", ifg->ifgrq_group); + } + } + if (cnt) + printf("\n"); +} + +static void +printgroup(const char *groupname) +{ + struct ifgroupreq ifgr; + struct ifg_req *ifg; + int len, cnt = 0; + int s; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); + bzero(&ifgr, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, groupname, sizeof(ifgr.ifgr_name)); + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { + if (errno == EINVAL || errno == ENOTTY || + errno == ENOENT) + exit(0); + else + err(1, "SIOCGIFGMEMB"); + } + + len = ifgr.ifgr_len; + if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) + err(1, "printgroup"); + if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) + err(1, "SIOCGIFGMEMB"); + + for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); + ifg++) { + len -= sizeof(struct ifg_req); + printf("%s\n", ifg->ifgrq_member); + cnt++; + } + free(ifgr.ifgr_groups); + + exit(0); +} + +static struct cmd group_cmds[] = { + DEF_CMD_ARG("group", setifgroup), + DEF_CMD_ARG("-group", unsetifgroup), +}; +static struct afswtch af_group = { + .af_name = "af_group", + .af_af = AF_UNSPEC, + .af_other_status = getifgroups, +}; +static struct option group_gopt = { "g:", "[-g groupname]", printgroup }; + +static __constructor void +group_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(group_cmds); i++) + cmd_register(&group_cmds[i]); + af_register(&af_group); + opt_register(&group_gopt); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifieee80211.c b/freebsd-userspace/commands/sbin/ifconfig/ifieee80211.c new file mode 100644 index 00000000..57713ca4 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifieee80211.c @@ -0,0 +1,5295 @@ +/* + * Copyright 2001 The Aerospace Corporation. 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. The name of The Aerospace Corporation may not be used to endorse or + * promote products derived from this software. + * + * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``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 AEROSPACE CORPORATION BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/*- + * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#ifdef __rtems__ +#include <freebsd/net/if_media.h> +#else +#include <net/if_media.h> +#endif +#include <net/route.h> + +#ifdef __rtems__ +#include <freebsd/net80211/ieee80211_ioctl.h> +#include <freebsd/net80211/ieee80211_freebsd.h> +#include <freebsd/net80211/ieee80211_superg.h> +#include <freebsd/net80211/ieee80211_tdma.h> +#include <freebsd/net80211/ieee80211_mesh.h> +#else +#include <net80211/ieee80211_ioctl.h> +#include <net80211/ieee80211_freebsd.h> +#include <net80211/ieee80211_superg.h> +#include <net80211/ieee80211_tdma.h> +#include <net80211/ieee80211_mesh.h> +#endif + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <stddef.h> /* NB: for offsetof */ + +#include "ifconfig.h" +#include "regdomain.h" + +#ifndef IEEE80211_FIXED_RATE_NONE +#define IEEE80211_FIXED_RATE_NONE 0xff +#endif + +/* XXX need these publicly defined or similar */ +#ifndef IEEE80211_NODE_AUTH +#define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */ +#define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */ +#define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */ +#define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */ +#define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */ +#define IEEE80211_NODE_HT 0x000040 /* HT enabled */ +#define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */ +#define IEEE80211_NODE_WPS 0x000100 /* WPS association */ +#define IEEE80211_NODE_TSN 0x000200 /* TSN association */ +#define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */ +#define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */ +#define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */ +#define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */ +#define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */ +#define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */ +#define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */ +#define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */ +#define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */ +#define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */ +#endif + +#define MAXCHAN 1536 /* max 1.5K channels */ + +#define MAXCOL 78 +static int col; +static char spacer; + +static void LINE_INIT(char c); +static void LINE_BREAK(void); +static void LINE_CHECK(const char *fmt, ...); + +static const char *modename[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = "auto", + [IEEE80211_MODE_11A] = "11a", + [IEEE80211_MODE_11B] = "11b", + [IEEE80211_MODE_11G] = "11g", + [IEEE80211_MODE_FH] = "fh", + [IEEE80211_MODE_TURBO_A] = "turboA", + [IEEE80211_MODE_TURBO_G] = "turboG", + [IEEE80211_MODE_STURBO_A] = "sturbo", + [IEEE80211_MODE_11NA] = "11na", + [IEEE80211_MODE_11NG] = "11ng", + [IEEE80211_MODE_HALF] = "half", + [IEEE80211_MODE_QUARTER] = "quarter" +}; + +static void set80211(int s, int type, int val, int len, void *data); +static int get80211(int s, int type, void *data, int len); +static int get80211len(int s, int type, void *data, int len, int *plen); +static int get80211val(int s, int type, int *val); +static const char *get_string(const char *val, const char *sep, + u_int8_t *buf, int *lenp); +static void print_string(const u_int8_t *buf, int len); +static void print_regdomain(const struct ieee80211_regdomain *, int); +static void print_channels(int, const struct ieee80211req_chaninfo *, + int allchans, int verbose); +static void regdomain_makechannels(struct ieee80211_regdomain_req *, + const struct ieee80211_devcaps_req *); +static const char *mesh_linkstate_string(uint8_t state); + +static struct ieee80211req_chaninfo *chaninfo; +static struct ieee80211_regdomain regdomain; +static int gotregdomain = 0; +static struct ieee80211_roamparams_req roamparams; +static int gotroam = 0; +static struct ieee80211_txparams_req txparams; +static int gottxparams = 0; +static struct ieee80211_channel curchan; +static int gotcurchan = 0; +static struct ifmediareq *ifmr; +static int htconf = 0; +static int gothtconf = 0; + +static void +gethtconf(int s) +{ + if (gothtconf) + return; + if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0) + warn("unable to get HT configuration information"); + gothtconf = 1; +} + +/* + * Collect channel info from the kernel. We use this (mostly) + * to handle mapping between frequency and IEEE channel number. + */ +static void +getchaninfo(int s) +{ + if (chaninfo != NULL) + return; + chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN)); + if (chaninfo == NULL) + errx(1, "no space for channel list"); + if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo, + IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0) + err(1, "unable to get channel information"); + ifmr = ifmedia_getstate(s); + gethtconf(s); +} + +static struct regdata * +getregdata(void) +{ + static struct regdata *rdp = NULL; + if (rdp == NULL) { + rdp = lib80211_alloc_regdata(); + if (rdp == NULL) + errx(-1, "missing or corrupted regdomain database"); + } + return rdp; +} + +/* + * Given the channel at index i with attributes from, + * check if there is a channel with attributes to in + * the channel table. With suitable attributes this + * allows the caller to look for promotion; e.g. from + * 11b > 11g. + */ +static int +canpromote(int i, int from, int to) +{ + const struct ieee80211_channel *fc = &chaninfo->ic_chans[i]; + int j; + + if ((fc->ic_flags & from) != from) + return i; + /* NB: quick check exploiting ordering of chans w/ same frequency */ + if (i+1 < chaninfo->ic_nchans && + chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq && + (chaninfo->ic_chans[i+1].ic_flags & to) == to) + return i+1; + /* brute force search in case channel list is not ordered */ + for (j = 0; j < chaninfo->ic_nchans; j++) { + const struct ieee80211_channel *tc = &chaninfo->ic_chans[j]; + if (j != i && + tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to) + return j; + } + return i; +} + +/* + * Handle channel promotion. When a channel is specified with + * only a frequency we want to promote it to the ``best'' channel + * available. The channel list has separate entries for 11b, 11g, + * 11a, and 11n[ga] channels so specifying a frequency w/o any + * attributes requires we upgrade, e.g. from 11b -> 11g. This + * gets complicated when the channel is specified on the same + * command line with a media request that constrains the available + * channe list (e.g. mode 11a); we want to honor that to avoid + * confusing behaviour. + */ +static int +promote(int i) +{ + /* + * Query the current mode of the interface in case it's + * constrained (e.g. to 11a). We must do this carefully + * as there may be a pending ifmedia request in which case + * asking the kernel will give us the wrong answer. This + * is an unfortunate side-effect of the way ifconfig is + * structure for modularity (yech). + * + * NB: ifmr is actually setup in getchaninfo (above); we + * assume it's called coincident with to this call so + * we have a ``current setting''; otherwise we must pass + * the socket descriptor down to here so we can make + * the ifmedia_getstate call ourselves. + */ + int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO; + + /* when ambiguous promote to ``best'' */ + /* NB: we abitrarily pick HT40+ over HT40- */ + if (chanmode != IFM_IEEE80211_11B) + i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G); + if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) { + i = canpromote(i, IEEE80211_CHAN_G, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT20); + if (htconf & 2) { + i = canpromote(i, IEEE80211_CHAN_G, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D); + i = canpromote(i, IEEE80211_CHAN_G, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U); + } + } + if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) { + i = canpromote(i, IEEE80211_CHAN_A, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); + if (htconf & 2) { + i = canpromote(i, IEEE80211_CHAN_A, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); + i = canpromote(i, IEEE80211_CHAN_A, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); + } + } + return i; +} + +static void +mapfreq(struct ieee80211_channel *chan, int freq, int flags) +{ + int i; + + for (i = 0; i < chaninfo->ic_nchans; i++) { + const struct ieee80211_channel *c = &chaninfo->ic_chans[i]; + + if (c->ic_freq == freq && (c->ic_flags & flags) == flags) { + if (flags == 0) { + /* when ambiguous promote to ``best'' */ + c = &chaninfo->ic_chans[promote(i)]; + } + *chan = *c; + return; + } + } + errx(1, "unknown/undefined frequency %u/0x%x", freq, flags); +} + +static void +mapchan(struct ieee80211_channel *chan, int ieee, int flags) +{ + int i; + + for (i = 0; i < chaninfo->ic_nchans; i++) { + const struct ieee80211_channel *c = &chaninfo->ic_chans[i]; + + if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) { + if (flags == 0) { + /* when ambiguous promote to ``best'' */ + c = &chaninfo->ic_chans[promote(i)]; + } + *chan = *c; + return; + } + } + errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags); +} + +static const struct ieee80211_channel * +getcurchan(int s) +{ + if (gotcurchan) + return &curchan; + if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) { + int val; + /* fall back to legacy ioctl */ + if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0) + err(-1, "cannot figure out current channel"); + getchaninfo(s); + mapchan(&curchan, val, 0); + } + gotcurchan = 1; + return &curchan; +} + +static enum ieee80211_phymode +chan2mode(const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_HTA(c)) + return IEEE80211_MODE_11NA; + if (IEEE80211_IS_CHAN_HTG(c)) + return IEEE80211_MODE_11NG; + if (IEEE80211_IS_CHAN_108A(c)) + return IEEE80211_MODE_TURBO_A; + if (IEEE80211_IS_CHAN_108G(c)) + return IEEE80211_MODE_TURBO_G; + if (IEEE80211_IS_CHAN_ST(c)) + return IEEE80211_MODE_STURBO_A; + if (IEEE80211_IS_CHAN_FHSS(c)) + return IEEE80211_MODE_FH; + if (IEEE80211_IS_CHAN_HALF(c)) + return IEEE80211_MODE_HALF; + if (IEEE80211_IS_CHAN_QUARTER(c)) + return IEEE80211_MODE_QUARTER; + if (IEEE80211_IS_CHAN_A(c)) + return IEEE80211_MODE_11A; + if (IEEE80211_IS_CHAN_ANYG(c)) + return IEEE80211_MODE_11G; + if (IEEE80211_IS_CHAN_B(c)) + return IEEE80211_MODE_11B; + return IEEE80211_MODE_AUTO; +} + +static void +getroam(int s) +{ + if (gotroam) + return; + if (get80211(s, IEEE80211_IOC_ROAM, + &roamparams, sizeof(roamparams)) < 0) + err(1, "unable to get roaming parameters"); + gotroam = 1; +} + +static void +setroam_cb(int s, void *arg) +{ + struct ieee80211_roamparams_req *roam = arg; + set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam); +} + +static void +gettxparams(int s) +{ + if (gottxparams) + return; + if (get80211(s, IEEE80211_IOC_TXPARAMS, + &txparams, sizeof(txparams)) < 0) + err(1, "unable to get transmit parameters"); + gottxparams = 1; +} + +static void +settxparams_cb(int s, void *arg) +{ + struct ieee80211_txparams_req *txp = arg; + set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp); +} + +static void +getregdomain(int s) +{ + if (gotregdomain) + return; + if (get80211(s, IEEE80211_IOC_REGDOMAIN, + ®domain, sizeof(regdomain)) < 0) + err(1, "unable to get regulatory domain info"); + gotregdomain = 1; +} + +static void +getdevcaps(int s, struct ieee80211_devcaps_req *dc) +{ + if (get80211(s, IEEE80211_IOC_DEVCAPS, dc, + IEEE80211_DEVCAPS_SPACE(dc)) < 0) + err(1, "unable to get device capabilities"); +} + +static void +setregdomain_cb(int s, void *arg) +{ + struct ieee80211_regdomain_req *req; + struct ieee80211_regdomain *rd = arg; + struct ieee80211_devcaps_req *dc; + struct regdata *rdp = getregdata(); + + if (rd->country != NO_COUNTRY) { + const struct country *cc; + /* + * Check current country seting to make sure it's + * compatible with the new regdomain. If not, then + * override it with any default country for this + * SKU. If we cannot arrange a match, then abort. + */ + cc = lib80211_country_findbycc(rdp, rd->country); + if (cc == NULL) + errx(1, "unknown ISO country code %d", rd->country); + if (cc->rd->sku != rd->regdomain) { + const struct regdomain *rp; + /* + * Check if country is incompatible with regdomain. + * To enable multiple regdomains for a country code + * we permit a mismatch between the regdomain and + * the country's associated regdomain when the + * regdomain is setup w/o a default country. For + * example, US is bound to the FCC regdomain but + * we allow US to be combined with FCC3 because FCC3 + * has not default country. This allows bogus + * combinations like FCC3+DK which are resolved when + * constructing the channel list by deferring to the + * regdomain to construct the channel list. + */ + rp = lib80211_regdomain_findbysku(rdp, rd->regdomain); + if (rp == NULL) + errx(1, "country %s (%s) is not usable with " + "regdomain %d", cc->isoname, cc->name, + rd->regdomain); + else if (rp->cc != NULL && rp->cc != cc) + errx(1, "country %s (%s) is not usable with " + "regdomain %s", cc->isoname, cc->name, + rp->name); + } + } + /* + * Fetch the device capabilities and calculate the + * full set of netbands for which we request a new + * channel list be constructed. Once that's done we + * push the regdomain info + channel list to the kernel. + */ + dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN)); + if (dc == NULL) + errx(1, "no space for device capabilities"); + dc->dc_chaninfo.ic_nchans = MAXCHAN; + getdevcaps(s, dc); +#if 0 + if (verbose) { + printf("drivercaps: 0x%x\n", dc->dc_drivercaps); + printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps); + printf("htcaps : 0x%x\n", dc->dc_htcaps); + memcpy(chaninfo, &dc->dc_chaninfo, + IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo)); + print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/); + } +#endif + req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans)); + if (req == NULL) + errx(1, "no space for regdomain request"); + req->rd = *rd; + regdomain_makechannels(req, dc); + if (verbose) { + LINE_INIT(':'); + print_regdomain(rd, 1/*verbose*/); + LINE_BREAK(); + /* blech, reallocate channel list for new data */ + if (chaninfo != NULL) + free(chaninfo); + chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo)); + if (chaninfo == NULL) + errx(1, "no space for channel list"); + memcpy(chaninfo, &req->chaninfo, + IEEE80211_CHANINFO_SPACE(&req->chaninfo)); + print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/); + } + if (req->chaninfo.ic_nchans == 0) + errx(1, "no channels calculated"); + set80211(s, IEEE80211_IOC_REGDOMAIN, 0, + IEEE80211_REGDOMAIN_SPACE(req), req); + free(req); + free(dc); +} + +static int +ieee80211_mhz2ieee(int freq, int flags) +{ + struct ieee80211_channel chan; + mapfreq(&chan, freq, flags); + return chan.ic_ieee; +} + +static int +isanyarg(const char *arg) +{ + return (strncmp(arg, "-", 1) == 0 || + strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0); +} + +static void +set80211ssid(const char *val, int d, int s, const struct afswtch *rafp) +{ + int ssid; + int len; + u_int8_t data[IEEE80211_NWID_LEN]; + + ssid = 0; + len = strlen(val); + if (len > 2 && isdigit((int)val[0]) && val[1] == ':') { + ssid = atoi(val)-1; + val += 2; + } + + bzero(data, sizeof(data)); + len = sizeof(data); + if (get_string(val, NULL, data, &len) == NULL) + exit(1); + + set80211(s, IEEE80211_IOC_SSID, ssid, len, data); +} + +static void +set80211meshid(const char *val, int d, int s, const struct afswtch *rafp) +{ + int len; + u_int8_t data[IEEE80211_NWID_LEN]; + + memset(data, 0, sizeof(data)); + len = sizeof(data); + if (get_string(val, NULL, data, &len) == NULL) + exit(1); + + set80211(s, IEEE80211_IOC_MESH_ID, 0, len, data); +} + +static void +set80211stationname(const char *val, int d, int s, const struct afswtch *rafp) +{ + int len; + u_int8_t data[33]; + + bzero(data, sizeof(data)); + len = sizeof(data); + get_string(val, NULL, data, &len); + + set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data); +} + +/* + * Parse a channel specification for attributes/flags. + * The syntax is: + * freq/xx channel width (5,10,20,40,40+,40-) + * freq:mode channel mode (a,b,g,h,n,t,s,d) + * + * These can be combined in either order; e.g. 2437:ng/40. + * Modes are case insensitive. + * + * The result is not validated here; it's assumed to be + * checked against the channel table fetched from the kernel. + */ +static int +getchannelflags(const char *val, int freq) +{ +#define _CHAN_HT 0x80000000 + const char *cp; + int flags; + + flags = 0; + + cp = strchr(val, ':'); + if (cp != NULL) { + for (cp++; isalpha((int) *cp); cp++) { + /* accept mixed case */ + int c = *cp; + if (isupper(c)) + c = tolower(c); + switch (c) { + case 'a': /* 802.11a */ + flags |= IEEE80211_CHAN_A; + break; + case 'b': /* 802.11b */ + flags |= IEEE80211_CHAN_B; + break; + case 'g': /* 802.11g */ + flags |= IEEE80211_CHAN_G; + break; + case 'h': /* ht = 802.11n */ + case 'n': /* 802.11n */ + flags |= _CHAN_HT; /* NB: private */ + break; + case 'd': /* dt = Atheros Dynamic Turbo */ + flags |= IEEE80211_CHAN_TURBO; + break; + case 't': /* ht, dt, st, t */ + /* dt and unadorned t specify Dynamic Turbo */ + if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0) + flags |= IEEE80211_CHAN_TURBO; + break; + case 's': /* st = Atheros Static Turbo */ + flags |= IEEE80211_CHAN_STURBO; + break; + default: + errx(-1, "%s: Invalid channel attribute %c\n", + val, *cp); + } + } + } + cp = strchr(val, '/'); + if (cp != NULL) { + char *ep; + u_long cw = strtoul(cp+1, &ep, 10); + + switch (cw) { + case 5: + flags |= IEEE80211_CHAN_QUARTER; + break; + case 10: + flags |= IEEE80211_CHAN_HALF; + break; + case 20: + /* NB: this may be removed below */ + flags |= IEEE80211_CHAN_HT20; + break; + case 40: + if (ep != NULL && *ep == '+') + flags |= IEEE80211_CHAN_HT40U; + else if (ep != NULL && *ep == '-') + flags |= IEEE80211_CHAN_HT40D; + break; + default: + errx(-1, "%s: Invalid channel width\n", val); + } + } + /* + * Cleanup specifications. + */ + if ((flags & _CHAN_HT) == 0) { + /* + * If user specified freq/20 or freq/40 quietly remove + * HT cw attributes depending on channel use. To give + * an explicit 20/40 width for an HT channel you must + * indicate it is an HT channel since all HT channels + * are also usable for legacy operation; e.g. freq:n/40. + */ + flags &= ~IEEE80211_CHAN_HT; + } else { + /* + * Remove private indicator that this is an HT channel + * and if no explicit channel width has been given + * provide the default settings. + */ + flags &= ~_CHAN_HT; + if ((flags & IEEE80211_CHAN_HT) == 0) { + struct ieee80211_channel chan; + /* + * Consult the channel list to see if we can use + * HT40+ or HT40- (if both the map routines choose). + */ + if (freq > 255) + mapfreq(&chan, freq, 0); + else + mapchan(&chan, freq, 0); + flags |= (chan.ic_flags & IEEE80211_CHAN_HT); + } + } + return flags; +#undef _CHAN_HT +} + +static void +getchannel(int s, struct ieee80211_channel *chan, const char *val) +{ + int v, flags; + char *eptr; + + memset(chan, 0, sizeof(*chan)); + if (isanyarg(val)) { + chan->ic_freq = IEEE80211_CHAN_ANY; + return; + } + getchaninfo(s); + errno = 0; + v = strtol(val, &eptr, 10); + if (val[0] == '\0' || val == eptr || errno == ERANGE || + /* channel may be suffixed with nothing, :flag, or /width */ + (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/')) + errx(1, "invalid channel specification%s", + errno == ERANGE ? " (out of range)" : ""); + flags = getchannelflags(val, v); + if (v > 255) { /* treat as frequency */ + mapfreq(chan, v, flags); + } else { + mapchan(chan, v, flags); + } +} + +static void +set80211channel(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct ieee80211_channel chan; + + getchannel(s, &chan, val); + set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan); +} + +static void +set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct ieee80211_chanswitch_req csr; + + getchannel(s, &csr.csa_chan, val); + csr.csa_mode = 1; + csr.csa_count = 5; + set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr); +} + +static void +set80211authmode(const char *val, int d, int s, const struct afswtch *rafp) +{ + int mode; + + if (strcasecmp(val, "none") == 0) { + mode = IEEE80211_AUTH_NONE; + } else if (strcasecmp(val, "open") == 0) { + mode = IEEE80211_AUTH_OPEN; + } else if (strcasecmp(val, "shared") == 0) { + mode = IEEE80211_AUTH_SHARED; + } else if (strcasecmp(val, "8021x") == 0) { + mode = IEEE80211_AUTH_8021X; + } else if (strcasecmp(val, "wpa") == 0) { + mode = IEEE80211_AUTH_WPA; + } else { + errx(1, "unknown authmode"); + } + + set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL); +} + +static void +set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp) +{ + int mode; + + if (strcasecmp(val, "off") == 0) { + mode = IEEE80211_POWERSAVE_OFF; + } else if (strcasecmp(val, "on") == 0) { + mode = IEEE80211_POWERSAVE_ON; + } else if (strcasecmp(val, "cam") == 0) { + mode = IEEE80211_POWERSAVE_CAM; + } else if (strcasecmp(val, "psp") == 0) { + mode = IEEE80211_POWERSAVE_PSP; + } else if (strcasecmp(val, "psp-cam") == 0) { + mode = IEEE80211_POWERSAVE_PSP_CAM; + } else { + errx(1, "unknown powersavemode"); + } + + set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL); +} + +static void +set80211powersave(const char *val, int d, int s, const struct afswtch *rafp) +{ + if (d == 0) + set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF, + 0, NULL); + else + set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON, + 0, NULL); +} + +static void +set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL); +} + +static void +set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp) +{ + int mode; + + if (strcasecmp(val, "off") == 0) { + mode = IEEE80211_WEP_OFF; + } else if (strcasecmp(val, "on") == 0) { + mode = IEEE80211_WEP_ON; + } else if (strcasecmp(val, "mixed") == 0) { + mode = IEEE80211_WEP_MIXED; + } else { + errx(1, "unknown wep mode"); + } + + set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL); +} + +static void +set80211wep(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_WEP, d, 0, NULL); +} + +static int +isundefarg(const char *arg) +{ + return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0); +} + +static void +set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp) +{ + if (isundefarg(val)) + set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL); + else + set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL); +} + +static void +set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp) +{ + int key = 0; + int len; + u_int8_t data[IEEE80211_KEYBUF_SIZE]; + + if (isdigit((int)val[0]) && val[1] == ':') { + key = atoi(val)-1; + val += 2; + } + + bzero(data, sizeof(data)); + len = sizeof(data); + get_string(val, NULL, data, &len); + + set80211(s, IEEE80211_IOC_WEPKEY, key, len, data); +} + +/* + * This function is purely a NetBSD compatability interface. The NetBSD + * interface is too inflexible, but it's there so we'll support it since + * it's not all that hard. + */ +static void +set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp) +{ + int txkey; + int i, len; + u_int8_t data[IEEE80211_KEYBUF_SIZE]; + + set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL); + + if (isdigit((int)val[0]) && val[1] == ':') { + txkey = val[0]-'0'-1; + val += 2; + + for (i = 0; i < 4; i++) { + bzero(data, sizeof(data)); + len = sizeof(data); + val = get_string(val, ",", data, &len); + if (val == NULL) + exit(1); + + set80211(s, IEEE80211_IOC_WEPKEY, i, len, data); + } + } else { + bzero(data, sizeof(data)); + len = sizeof(data); + get_string(val, NULL, data, &len); + txkey = 0; + + set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data); + + bzero(data, sizeof(data)); + for (i = 1; i < 4; i++) + set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data); + } + + set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL); +} + +static void +set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_RTSTHRESHOLD, + isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL); +} + +static void +set80211protmode(const char *val, int d, int s, const struct afswtch *rafp) +{ + int mode; + + if (strcasecmp(val, "off") == 0) { + mode = IEEE80211_PROTMODE_OFF; + } else if (strcasecmp(val, "cts") == 0) { + mode = IEEE80211_PROTMODE_CTS; + } else if (strncasecmp(val, "rtscts", 3) == 0) { + mode = IEEE80211_PROTMODE_RTSCTS; + } else { + errx(1, "unknown protection mode"); + } + + set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL); +} + +static void +set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp) +{ + int mode; + + if (strcasecmp(val, "off") == 0) { + mode = IEEE80211_PROTMODE_OFF; + } else if (strncasecmp(val, "rts", 3) == 0) { + mode = IEEE80211_PROTMODE_RTSCTS; + } else { + errx(1, "unknown protection mode"); + } + + set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL); +} + +static void +set80211txpower(const char *val, int d, int s, const struct afswtch *rafp) +{ + double v = atof(val); + int txpow; + + txpow = (int) (2*v); + if (txpow != 2*v) + errx(-1, "invalid tx power (must be .5 dBm units)"); + set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL); +} + +#define IEEE80211_ROAMING_DEVICE 0 +#define IEEE80211_ROAMING_AUTO 1 +#define IEEE80211_ROAMING_MANUAL 2 + +static void +set80211roaming(const char *val, int d, int s, const struct afswtch *rafp) +{ + int mode; + + if (strcasecmp(val, "device") == 0) { + mode = IEEE80211_ROAMING_DEVICE; + } else if (strcasecmp(val, "auto") == 0) { + mode = IEEE80211_ROAMING_AUTO; + } else if (strcasecmp(val, "manual") == 0) { + mode = IEEE80211_ROAMING_MANUAL; + } else { + errx(1, "unknown roaming mode"); + } + set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL); +} + +static void +set80211wme(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_WME, d, 0, NULL); +} + +static void +set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL); +} + +static void +set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL); +} + +static void +set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_FF, d, 0, NULL); +} + +static void +set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL); +} + +static void +set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct ieee80211req_chanlist chanlist; + char *temp, *cp, *tp; + + temp = malloc(strlen(val) + 1); + if (temp == NULL) + errx(1, "malloc failed"); + strcpy(temp, val); + memset(&chanlist, 0, sizeof(chanlist)); + cp = temp; + for (;;) { + int first, last, f, c; + + tp = strchr(cp, ','); + if (tp != NULL) + *tp++ = '\0'; + switch (sscanf(cp, "%u-%u", &first, &last)) { + case 1: + if (first > IEEE80211_CHAN_MAX) + errx(-1, "channel %u out of range, max %u", + first, IEEE80211_CHAN_MAX); + setbit(chanlist.ic_channels, first); + break; + case 2: + if (first > IEEE80211_CHAN_MAX) + errx(-1, "channel %u out of range, max %u", + first, IEEE80211_CHAN_MAX); + if (last > IEEE80211_CHAN_MAX) + errx(-1, "channel %u out of range, max %u", + last, IEEE80211_CHAN_MAX); + if (first > last) + errx(-1, "void channel range, %u > %u", + first, last); + for (f = first; f <= last; f++) + setbit(chanlist.ic_channels, f); + break; + } + if (tp == NULL) + break; + c = *tp; + while (isspace(c)) + tp++; + if (!isdigit(c)) + break; + cp = tp; + } + set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist); +} + +static void +set80211bssid(const char *val, int d, int s, const struct afswtch *rafp) +{ + + if (!isanyarg(val)) { + char *temp; + struct sockaddr_dl sdl; + + temp = malloc(strlen(val) + 2); /* ':' and '\0' */ + if (temp == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strcpy(temp + 1, val); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen != IEEE80211_ADDR_LEN) + errx(1, "malformed link-level address"); + set80211(s, IEEE80211_IOC_BSSID, 0, + IEEE80211_ADDR_LEN, LLADDR(&sdl)); + } else { + uint8_t zerobssid[IEEE80211_ADDR_LEN]; + memset(zerobssid, 0, sizeof(zerobssid)); + set80211(s, IEEE80211_IOC_BSSID, 0, + IEEE80211_ADDR_LEN, zerobssid); + } +} + +static int +getac(const char *ac) +{ + if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0) + return WME_AC_BE; + if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0) + return WME_AC_BK; + if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0) + return WME_AC_VI; + if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0) + return WME_AC_VO; + errx(1, "unknown wme access class %s", ac); +} + +static +DECL_CMD_FUNC2(set80211cwmin, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL); +} + +static +DECL_CMD_FUNC2(set80211cwmax, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL); +} + +static +DECL_CMD_FUNC2(set80211aifs, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL); +} + +static +DECL_CMD_FUNC2(set80211txoplimit, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL); +} + +static +DECL_CMD_FUNC(set80211acm, ac, d) +{ + set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL); +} +static +DECL_CMD_FUNC(set80211noacm, ac, d) +{ + set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL); +} + +static +DECL_CMD_FUNC(set80211ackpolicy, ac, d) +{ + set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL); +} +static +DECL_CMD_FUNC(set80211noackpolicy, ac, d) +{ + set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL); +} + +static +DECL_CMD_FUNC2(set80211bsscwmin, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), + getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); +} + +static +DECL_CMD_FUNC2(set80211bsscwmax, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), + getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); +} + +static +DECL_CMD_FUNC2(set80211bssaifs, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), + getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); +} + +static +DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val) +{ + set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), + getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); +} + +static +DECL_CMD_FUNC(set80211dtimperiod, val, d) +{ + set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211bintval, val, d) +{ + set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL); +} + +static void +set80211macmac(int s, int op, const char *val) +{ + char *temp; + struct sockaddr_dl sdl; + + temp = malloc(strlen(val) + 2); /* ':' and '\0' */ + if (temp == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strcpy(temp + 1, val); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen != IEEE80211_ADDR_LEN) + errx(1, "malformed link-level address"); + set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl)); +} + +static +DECL_CMD_FUNC(set80211addmac, val, d) +{ + set80211macmac(s, IEEE80211_IOC_ADDMAC, val); +} + +static +DECL_CMD_FUNC(set80211delmac, val, d) +{ + set80211macmac(s, IEEE80211_IOC_DELMAC, val); +} + +static +DECL_CMD_FUNC(set80211kickmac, val, d) +{ + char *temp; + struct sockaddr_dl sdl; + struct ieee80211req_mlme mlme; + + temp = malloc(strlen(val) + 2); /* ':' and '\0' */ + if (temp == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strcpy(temp + 1, val); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen != IEEE80211_ADDR_LEN) + errx(1, "malformed link-level address"); + memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; + memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN); + set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme); +} + +static +DECL_CMD_FUNC(set80211maccmd, val, d) +{ + set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL); +} + +static void +set80211meshrtmac(int s, int req, const char *val) +{ + char *temp; + struct sockaddr_dl sdl; + + temp = malloc(strlen(val) + 2); /* ':' and '\0' */ + if (temp == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strcpy(temp + 1, val); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen != IEEE80211_ADDR_LEN) + errx(1, "malformed link-level address"); + set80211(s, IEEE80211_IOC_MESH_RTCMD, req, + IEEE80211_ADDR_LEN, LLADDR(&sdl)); +} + +static +DECL_CMD_FUNC(set80211addmeshrt, val, d) +{ + set80211meshrtmac(s, IEEE80211_MESH_RTCMD_ADD, val); +} + +static +DECL_CMD_FUNC(set80211delmeshrt, val, d) +{ + set80211meshrtmac(s, IEEE80211_MESH_RTCMD_DELETE, val); +} + +static +DECL_CMD_FUNC(set80211meshrtcmd, val, d) +{ + set80211(s, IEEE80211_IOC_MESH_RTCMD, d, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211hwmprootmode, val, d) +{ + int mode; + + if (strcasecmp(val, "normal") == 0) + mode = IEEE80211_HWMP_ROOTMODE_NORMAL; + else if (strcasecmp(val, "proactive") == 0) + mode = IEEE80211_HWMP_ROOTMODE_PROACTIVE; + else if (strcasecmp(val, "rann") == 0) + mode = IEEE80211_HWMP_ROOTMODE_RANN; + else + mode = IEEE80211_HWMP_ROOTMODE_DISABLED; + set80211(s, IEEE80211_IOC_HWMP_ROOTMODE, mode, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211hwmpmaxhops, val, d) +{ + set80211(s, IEEE80211_IOC_HWMP_MAXHOPS, atoi(val), 0, NULL); +} + +static void +set80211pureg(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL); +} + +static void +set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211bgscanidle, val, d) +{ + set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211bgscanintvl, val, d) +{ + set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211scanvalid, val, d) +{ + set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL); +} + +/* + * Parse an optional trailing specification of which netbands + * to apply a parameter to. This is basically the same syntax + * as used for channels but you can concatenate to specify + * multiple. For example: + * 14:abg apply to 11a, 11b, and 11g + * 6:ht apply to 11na and 11ng + * We don't make a big effort to catch silly things; this is + * really a convenience mechanism. + */ +static int +getmodeflags(const char *val) +{ + const char *cp; + int flags; + + flags = 0; + + cp = strchr(val, ':'); + if (cp != NULL) { + for (cp++; isalpha((int) *cp); cp++) { + /* accept mixed case */ + int c = *cp; + if (isupper(c)) + c = tolower(c); + switch (c) { + case 'a': /* 802.11a */ + flags |= IEEE80211_CHAN_A; + break; + case 'b': /* 802.11b */ + flags |= IEEE80211_CHAN_B; + break; + case 'g': /* 802.11g */ + flags |= IEEE80211_CHAN_G; + break; + case 'n': /* 802.11n */ + flags |= IEEE80211_CHAN_HT; + break; + case 'd': /* dt = Atheros Dynamic Turbo */ + flags |= IEEE80211_CHAN_TURBO; + break; + case 't': /* ht, dt, st, t */ + /* dt and unadorned t specify Dynamic Turbo */ + if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0) + flags |= IEEE80211_CHAN_TURBO; + break; + case 's': /* st = Atheros Static Turbo */ + flags |= IEEE80211_CHAN_STURBO; + break; + case 'h': /* 1/2-width channels */ + flags |= IEEE80211_CHAN_HALF; + break; + case 'q': /* 1/4-width channels */ + flags |= IEEE80211_CHAN_QUARTER; + break; + default: + errx(-1, "%s: Invalid mode attribute %c\n", + val, *cp); + } + } + } + return flags; +} + +#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ) +#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ) + +#define _APPLY(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\ + _base.params[IEEE80211_MODE_11NA]._param = _v; \ + _base.params[IEEE80211_MODE_11NG]._param = _v; \ + } else if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_11NA]._param = _v; \ + else \ + _base.params[IEEE80211_MODE_11NG]._param = _v; \ + } \ + if (_flags & IEEE80211_CHAN_TURBO) { \ + if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\ + _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \ + _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \ + } else if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \ + else \ + _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \ + } \ + if (_flags & IEEE80211_CHAN_STURBO) \ + _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \ + if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \ + _base.params[IEEE80211_MODE_11A]._param = _v; \ + if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \ + _base.params[IEEE80211_MODE_11G]._param = _v; \ + if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \ + _base.params[IEEE80211_MODE_11B]._param = _v; \ + if (_flags & IEEE80211_CHAN_HALF) \ + _base.params[IEEE80211_MODE_HALF]._param = _v; \ + if (_flags & IEEE80211_CHAN_QUARTER) \ + _base.params[IEEE80211_MODE_QUARTER]._param = _v; \ +} while (0) +#define _APPLY1(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + if (_flags & IEEE80211_CHAN_5GHZ) \ + _base.params[IEEE80211_MODE_11NA]._param = _v; \ + else \ + _base.params[IEEE80211_MODE_11NG]._param = _v; \ + } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \ + _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \ + _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \ + _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \ + else if (_flags & IEEE80211_CHAN_HALF) \ + _base.params[IEEE80211_MODE_HALF]._param = _v; \ + else if (_flags & IEEE80211_CHAN_QUARTER) \ + _base.params[IEEE80211_MODE_QUARTER]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \ + _base.params[IEEE80211_MODE_11A]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \ + _base.params[IEEE80211_MODE_11G]._param = _v; \ + else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \ + _base.params[IEEE80211_MODE_11B]._param = _v; \ +} while (0) +#define _APPLY_RATE(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + (_v) = (_v / 2) | IEEE80211_RATE_MCS; \ + } \ + _APPLY(_flags, _base, _param, _v); \ +} while (0) +#define _APPLY_RATE1(_flags, _base, _param, _v) do { \ + if (_flags & IEEE80211_CHAN_HT) { \ + (_v) = (_v / 2) | IEEE80211_RATE_MCS; \ + } \ + _APPLY1(_flags, _base, _param, _v); \ +} while (0) + +static +DECL_CMD_FUNC(set80211roamrssi, val, d) +{ + double v = atof(val); + int rssi, flags; + + rssi = (int) (2*v); + if (rssi != 2*v) + errx(-1, "invalid rssi (must be .5 dBm units)"); + flags = getmodeflags(val); + getroam(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY1(flags, roamparams, rssi, rssi); + } else + _APPLY(flags, roamparams, rssi, rssi); + callback_register(setroam_cb, &roamparams); +} + +static int +getrate(const char *val, const char *tag) +{ + double v = atof(val); + int rate; + + rate = (int) (2*v); + if (rate != 2*v) + errx(-1, "invalid %s rate (must be .5 Mb/s units)", tag); + return rate; /* NB: returns 2x the specified value */ +} + +static +DECL_CMD_FUNC(set80211roamrate, val, d) +{ + int rate, flags; + + rate = getrate(val, "roam"); + flags = getmodeflags(val); + getroam(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, roamparams, rate, rate); + } else + _APPLY_RATE(flags, roamparams, rate, rate); + callback_register(setroam_cb, &roamparams); +} + +static +DECL_CMD_FUNC(set80211mcastrate, val, d) +{ + int rate, flags; + + rate = getrate(val, "mcast"); + flags = getmodeflags(val); + gettxparams(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, txparams, mcastrate, rate); + } else + _APPLY_RATE(flags, txparams, mcastrate, rate); + callback_register(settxparams_cb, &txparams); +} + +static +DECL_CMD_FUNC(set80211mgtrate, val, d) +{ + int rate, flags; + + rate = getrate(val, "mgmt"); + flags = getmodeflags(val); + gettxparams(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, txparams, mgmtrate, rate); + } else + _APPLY_RATE(flags, txparams, mgmtrate, rate); + callback_register(settxparams_cb, &txparams); +} + +static +DECL_CMD_FUNC(set80211ucastrate, val, d) +{ + int flags; + + gettxparams(s); + flags = getmodeflags(val); + if (isanyarg(val)) { + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY1(flags, txparams, ucastrate, + IEEE80211_FIXED_RATE_NONE); + } else + _APPLY(flags, txparams, ucastrate, + IEEE80211_FIXED_RATE_NONE); + } else { + int rate = getrate(val, "ucast"); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY_RATE1(flags, txparams, ucastrate, rate); + } else + _APPLY_RATE(flags, txparams, ucastrate, rate); + } + callback_register(settxparams_cb, &txparams); +} + +static +DECL_CMD_FUNC(set80211maxretry, val, d) +{ + int v = atoi(val), flags; + + flags = getmodeflags(val); + gettxparams(s); + if (flags == 0) { /* NB: no flags => current channel */ + flags = getcurchan(s)->ic_flags; + _APPLY1(flags, txparams, maxretry, v); + } else + _APPLY(flags, txparams, maxretry, v); + callback_register(settxparams_cb, &txparams); +} +#undef _APPLY_RATE +#undef _APPLY +#undef IEEE80211_CHAN_HTA +#undef IEEE80211_CHAN_HTG + +static +DECL_CMD_FUNC(set80211fragthreshold, val, d) +{ + set80211(s, IEEE80211_IOC_FRAGTHRESHOLD, + isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211bmissthreshold, val, d) +{ + set80211(s, IEEE80211_IOC_BMISSTHRESHOLD, + isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL); +} + +static void +set80211burst(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_BURST, d, 0, NULL); +} + +static void +set80211doth(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL); +} + +static void +set80211dfs(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DFS, d, 0, NULL); +} + +static void +set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_SHORTGI, + d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0, + 0, NULL); +} + +static void +set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp) +{ + int ampdu; + + if (get80211val(s, IEEE80211_IOC_AMPDU, &du) < 0) + errx(-1, "cannot get AMPDU setting"); + if (d < 0) { + d = -d; + ampdu &= ~d; + } else + ampdu |= d; + set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211ampdulimit, val, d) +{ + int v; + + switch (atoi(val)) { + case 8: + case 8*1024: + v = IEEE80211_HTCAP_MAXRXAMPDU_8K; + break; + case 16: + case 16*1024: + v = IEEE80211_HTCAP_MAXRXAMPDU_16K; + break; + case 32: + case 32*1024: + v = IEEE80211_HTCAP_MAXRXAMPDU_32K; + break; + case 64: + case 64*1024: + v = IEEE80211_HTCAP_MAXRXAMPDU_64K; + break; + default: + errx(-1, "invalid A-MPDU limit %s", val); + } + set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211ampdudensity, val, d) +{ + int v; + + if (isanyarg(val) || strcasecmp(val, "na") == 0) + v = IEEE80211_HTCAP_MPDUDENSITY_NA; + else switch ((int)(atof(val)*4)) { + case 0: + v = IEEE80211_HTCAP_MPDUDENSITY_NA; + break; + case 1: + v = IEEE80211_HTCAP_MPDUDENSITY_025; + break; + case 2: + v = IEEE80211_HTCAP_MPDUDENSITY_05; + break; + case 4: + v = IEEE80211_HTCAP_MPDUDENSITY_1; + break; + case 8: + v = IEEE80211_HTCAP_MPDUDENSITY_2; + break; + case 16: + v = IEEE80211_HTCAP_MPDUDENSITY_4; + break; + case 32: + v = IEEE80211_HTCAP_MPDUDENSITY_8; + break; + case 64: + v = IEEE80211_HTCAP_MPDUDENSITY_16; + break; + default: + errx(-1, "invalid A-MPDU density %s", val); + } + set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL); +} + +static void +set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp) +{ + int amsdu; + + if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0) + err(-1, "cannot get AMSDU setting"); + if (d < 0) { + d = -d; + amsdu &= ~d; + } else + amsdu |= d; + set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211amsdulimit, val, d) +{ + set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL); +} + +static void +set80211puren(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL); +} + +static void +set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL); +} + +static void +set80211htconf(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL); + htconf = d; +} + +static void +set80211dwds(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL); +} + +static void +set80211inact(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL); +} + +static void +set80211tsn(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_TSN, d, 0, NULL); +} + +static void +set80211dotd(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL); +} + +static void +set80211smps(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL); +} + +static void +set80211rifs(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211tdmaslot, val, d) +{ + set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211tdmaslotcnt, val, d) +{ + set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211tdmaslotlen, val, d) +{ + set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211tdmabintval, val, d) +{ + set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211meshttl, val, d) +{ + set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211meshforward, val, d) +{ + set80211(s, IEEE80211_IOC_MESH_FWRD, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211meshpeering, val, d) +{ + set80211(s, IEEE80211_IOC_MESH_AP, atoi(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211meshmetric, val, d) +{ + char v[12]; + + memcpy(v, val, sizeof(v)); + set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v); +} + +static +DECL_CMD_FUNC(set80211meshpath, val, d) +{ + char v[12]; + + memcpy(v, val, sizeof(v)); + set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v); +} + +static int +regdomain_sort(const void *a, const void *b) +{ +#define CHAN_ALL \ + (IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER) + const struct ieee80211_channel *ca = a; + const struct ieee80211_channel *cb = b; + + return ca->ic_freq == cb->ic_freq ? + (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) : + ca->ic_freq - cb->ic_freq; +#undef CHAN_ALL +} + +static const struct ieee80211_channel * +chanlookup(const struct ieee80211_channel chans[], int nchans, + int freq, int flags) +{ + int i; + + flags &= IEEE80211_CHAN_ALLTURBO; + for (i = 0; i < nchans; i++) { + const struct ieee80211_channel *c = &chans[i]; + if (c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + } + return NULL; +} + +static int +chanfind(const struct ieee80211_channel chans[], int nchans, int flags) +{ + int i; + + for (i = 0; i < nchans; i++) { + const struct ieee80211_channel *c = &chans[i]; + if ((c->ic_flags & flags) == flags) + return 1; + } + return 0; +} + +/* + * Check channel compatibility. + */ +static int +checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags) +{ + flags &= ~REQ_FLAGS; + /* + * Check if exact channel is in the calibration table; + * everything below is to deal with channels that we + * want to include but that are not explicitly listed. + */ + if (flags & IEEE80211_CHAN_HT40) { + /* NB: we use an HT40 channel center that matches HT20 */ + flags = (flags &~ IEEE80211_CHAN_HT40) | IEEE80211_CHAN_HT20; + } + if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL) + return 1; + if (flags & IEEE80211_CHAN_GSM) { + /* + * XXX GSM frequency mapping is handled in the kernel + * so we cannot find them in the calibration table; + * just accept the channel and the kernel will reject + * the channel list if it's wrong. + */ + return 1; + } + /* + * If this is a 1/2 or 1/4 width channel allow it if a full + * width channel is present for this frequency, and the device + * supports fractional channels on this band. This is a hack + * that avoids bloating the calibration table; it may be better + * by per-band attributes though (we are effectively calculating + * this attribute by scanning the channel list ourself). + */ + if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0) + return 0; + if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, + flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL) + return 0; + if (flags & IEEE80211_CHAN_HALF) { + return chanfind(avail->ic_chans, avail->ic_nchans, + IEEE80211_CHAN_HALF | + (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ))); + } else { + return chanfind(avail->ic_chans, avail->ic_nchans, + IEEE80211_CHAN_QUARTER | + (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ))); + } +} + +static void +regdomain_addchans(struct ieee80211req_chaninfo *ci, + const netband_head *bands, + const struct ieee80211_regdomain *reg, + uint32_t chanFlags, + const struct ieee80211req_chaninfo *avail) +{ + const struct netband *nb; + const struct freqband *b; + struct ieee80211_channel *c, *prev; + int freq, hi_adj, lo_adj, channelSep; + uint32_t flags; + + hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0; + lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0; + channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40; + LIST_FOREACH(nb, bands, next) { + b = nb->band; + if (verbose) { + printf("%s:", __func__); + printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS); + printb(" bandFlags", nb->flags | b->flags, + IEEE80211_CHAN_BITS); + putchar('\n'); + } + prev = NULL; + for (freq = b->freqStart + lo_adj; + freq <= b->freqEnd + hi_adj; freq += b->chanSep) { + /* + * Construct flags for the new channel. We take + * the attributes from the band descriptions except + * for HT40 which is enabled generically (i.e. +/- + * extension channel) in the band description and + * then constrained according by channel separation. + */ + flags = nb->flags | b->flags; + if (flags & IEEE80211_CHAN_HT) { + /* + * HT channels are generated specially; we're + * called to add HT20, HT40+, and HT40- chan's + * so we need to expand only band specs for + * the HT channel type being added. + */ + if ((chanFlags & IEEE80211_CHAN_HT20) && + (flags & IEEE80211_CHAN_HT20) == 0) { + if (verbose) + printf("%u: skip, not an " + "HT20 channel\n", freq); + continue; + } + if ((chanFlags & IEEE80211_CHAN_HT40) && + (flags & IEEE80211_CHAN_HT40) == 0) { + if (verbose) + printf("%u: skip, not an " + "HT40 channel\n", freq); + continue; + } + /* + * DFS and HT40 don't mix. This should be + * expressed in the regdomain database but + * just in case enforce it here. + */ + if ((chanFlags & IEEE80211_CHAN_HT40) && + (flags & IEEE80211_CHAN_DFS)) { + if (verbose) + printf("%u: skip, HT40+DFS " + "not permitted\n", freq); + continue; + } + /* NB: HT attribute comes from caller */ + flags &= ~IEEE80211_CHAN_HT; + flags |= chanFlags & IEEE80211_CHAN_HT; + } + /* + * Check if device can operate on this frequency. + */ + if (!checkchan(avail, freq, flags)) { + if (verbose) { + printf("%u: skip, ", freq); + printb("flags", flags, + IEEE80211_CHAN_BITS); + printf(" not available\n"); + } + continue; + } + if ((flags & REQ_ECM) && !reg->ecm) { + if (verbose) + printf("%u: skip, ECM channel\n", freq); + continue; + } + if ((flags & REQ_INDOOR) && reg->location == 'O') { + if (verbose) + printf("%u: skip, indoor channel\n", + freq); + continue; + } + if ((flags & REQ_OUTDOOR) && reg->location == 'I') { + if (verbose) + printf("%u: skip, outdoor channel\n", + freq); + continue; + } + if ((flags & IEEE80211_CHAN_HT40) && + prev != NULL && (freq - prev->ic_freq) < channelSep) { + if (verbose) + printf("%u: skip, only %u channel " + "separation, need %d\n", freq, + freq - prev->ic_freq, channelSep); + continue; + } + if (ci->ic_nchans == IEEE80211_CHAN_MAX) { + if (verbose) + printf("%u: skip, channel table full\n", + freq); + break; + } + c = &ci->ic_chans[ci->ic_nchans++]; + memset(c, 0, sizeof(*c)); + c->ic_freq = freq; + c->ic_flags = flags; + if (c->ic_flags & IEEE80211_CHAN_DFS) + c->ic_maxregpower = nb->maxPowerDFS; + else + c->ic_maxregpower = nb->maxPower; + if (verbose) { + printf("[%3d] add freq %u ", + ci->ic_nchans-1, c->ic_freq); + printb("flags", c->ic_flags, IEEE80211_CHAN_BITS); + printf(" power %u\n", c->ic_maxregpower); + } + /* NB: kernel fills in other fields */ + prev = c; + } + } +} + +static void +regdomain_makechannels( + struct ieee80211_regdomain_req *req, + const struct ieee80211_devcaps_req *dc) +{ + struct regdata *rdp = getregdata(); + const struct country *cc; + const struct ieee80211_regdomain *reg = &req->rd; + struct ieee80211req_chaninfo *ci = &req->chaninfo; + const struct regdomain *rd; + + /* + * Locate construction table for new channel list. We treat + * the regdomain/SKU as definitive so a country can be in + * multiple with different properties (e.g. US in FCC+FCC3). + * If no regdomain is specified then we fallback on the country + * code to find the associated regdomain since countries always + * belong to at least one regdomain. + */ + if (reg->regdomain == 0) { + cc = lib80211_country_findbycc(rdp, reg->country); + if (cc == NULL) + errx(1, "internal error, country %d not found", + reg->country); + rd = cc->rd; + } else + rd = lib80211_regdomain_findbysku(rdp, reg->regdomain); + if (rd == NULL) + errx(1, "internal error, regdomain %d not found", + reg->regdomain); + if (rd->sku != SKU_DEBUG) { + /* + * regdomain_addchans incrememnts the channel count for + * each channel it adds so initialize ic_nchans to zero. + * Note that we know we have enough space to hold all possible + * channels because the devcaps list size was used to + * allocate our request. + */ + ci->ic_nchans = 0; + if (!LIST_EMPTY(&rd->bands_11b)) + regdomain_addchans(ci, &rd->bands_11b, reg, + IEEE80211_CHAN_B, &dc->dc_chaninfo); + if (!LIST_EMPTY(&rd->bands_11g)) + regdomain_addchans(ci, &rd->bands_11g, reg, + IEEE80211_CHAN_G, &dc->dc_chaninfo); + if (!LIST_EMPTY(&rd->bands_11a)) + regdomain_addchans(ci, &rd->bands_11a, reg, + IEEE80211_CHAN_A, &dc->dc_chaninfo); + if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) { + regdomain_addchans(ci, &rd->bands_11na, reg, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, + &dc->dc_chaninfo); + if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) { + regdomain_addchans(ci, &rd->bands_11na, reg, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, + &dc->dc_chaninfo); + regdomain_addchans(ci, &rd->bands_11na, reg, + IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, + &dc->dc_chaninfo); + } + } + if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) { + regdomain_addchans(ci, &rd->bands_11ng, reg, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, + &dc->dc_chaninfo); + if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) { + regdomain_addchans(ci, &rd->bands_11ng, reg, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, + &dc->dc_chaninfo); + regdomain_addchans(ci, &rd->bands_11ng, reg, + IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, + &dc->dc_chaninfo); + } + } + qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]), + regdomain_sort); + } else + memcpy(ci, &dc->dc_chaninfo, + IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo)); +} + +static void +list_countries(void) +{ + struct regdata *rdp = getregdata(); + const struct country *cp; + const struct regdomain *dp; + int i; + + i = 0; + printf("\nCountry codes:\n"); + LIST_FOREACH(cp, &rdp->countries, next) { + printf("%2s %-15.15s%s", cp->isoname, + cp->name, ((i+1)%4) == 0 ? "\n" : " "); + i++; + } + i = 0; + printf("\nRegulatory domains:\n"); + LIST_FOREACH(dp, &rdp->domains, next) { + printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " "); + i++; + } + printf("\n"); +} + +static void +defaultcountry(const struct regdomain *rd) +{ + struct regdata *rdp = getregdata(); + const struct country *cc; + + cc = lib80211_country_findbycc(rdp, rd->cc->code); + if (cc == NULL) + errx(1, "internal error, ISO country code %d not " + "defined for regdomain %s", rd->cc->code, rd->name); + regdomain.country = cc->code; + regdomain.isocc[0] = cc->isoname[0]; + regdomain.isocc[1] = cc->isoname[1]; +} + +static +DECL_CMD_FUNC(set80211regdomain, val, d) +{ + struct regdata *rdp = getregdata(); + const struct regdomain *rd; + + rd = lib80211_regdomain_findbyname(rdp, val); + if (rd == NULL) { + char *eptr; + long sku = strtol(val, &eptr, 0); + + if (eptr != val) + rd = lib80211_regdomain_findbysku(rdp, sku); + if (eptr == val || rd == NULL) + errx(1, "unknown regdomain %s", val); + } + getregdomain(s); + regdomain.regdomain = rd->sku; + if (regdomain.country == 0 && rd->cc != NULL) { + /* + * No country code setup and there's a default + * one for this regdomain fill it in. + */ + defaultcountry(rd); + } + callback_register(setregdomain_cb, ®domain); +} + +static +DECL_CMD_FUNC(set80211country, val, d) +{ + struct regdata *rdp = getregdata(); + const struct country *cc; + + cc = lib80211_country_findbyname(rdp, val); + if (cc == NULL) { + char *eptr; + long code = strtol(val, &eptr, 0); + + if (eptr != val) + cc = lib80211_country_findbycc(rdp, code); + if (eptr == val || cc == NULL) + errx(1, "unknown ISO country code %s", val); + } + getregdomain(s); + regdomain.regdomain = cc->rd->sku; + regdomain.country = cc->code; + regdomain.isocc[0] = cc->isoname[0]; + regdomain.isocc[1] = cc->isoname[1]; + callback_register(setregdomain_cb, ®domain); +} + +static void +set80211location(const char *val, int d, int s, const struct afswtch *rafp) +{ + getregdomain(s); + regdomain.location = d; + callback_register(setregdomain_cb, ®domain); +} + +static void +set80211ecm(const char *val, int d, int s, const struct afswtch *rafp) +{ + getregdomain(s); + regdomain.ecm = d; + callback_register(setregdomain_cb, ®domain); +} + +static void +LINE_INIT(char c) +{ + spacer = c; + if (c == '\t') + col = 8; + else + col = 1; +} + +static void +LINE_BREAK(void) +{ + if (spacer != '\t') { + printf("\n"); + spacer = '\t'; + } + col = 8; /* 8-col tab */ +} + +static void +LINE_CHECK(const char *fmt, ...) +{ + char buf[80]; + va_list ap; + int n; + + va_start(ap, fmt); + n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap); + va_end(ap); + col += 1+n; + if (col > MAXCOL) { + LINE_BREAK(); + col += n; + } + buf[0] = spacer; + printf("%s", buf); + spacer = ' '; +} + +static int +getmaxrate(const uint8_t rates[15], uint8_t nrates) +{ + int i, maxrate = -1; + + for (i = 0; i < nrates; i++) { + int rate = rates[i] & IEEE80211_RATE_VAL; + if (rate > maxrate) + maxrate = rate; + } + return maxrate / 2; +} + +static const char * +getcaps(int capinfo) +{ + static char capstring[32]; + char *cp = capstring; + + if (capinfo & IEEE80211_CAPINFO_ESS) + *cp++ = 'E'; + if (capinfo & IEEE80211_CAPINFO_IBSS) + *cp++ = 'I'; + if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE) + *cp++ = 'c'; + if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ) + *cp++ = 'C'; + if (capinfo & IEEE80211_CAPINFO_PRIVACY) + *cp++ = 'P'; + if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) + *cp++ = 'S'; + if (capinfo & IEEE80211_CAPINFO_PBCC) + *cp++ = 'B'; + if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY) + *cp++ = 'A'; + if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) + *cp++ = 's'; + if (capinfo & IEEE80211_CAPINFO_RSN) + *cp++ = 'R'; + if (capinfo & IEEE80211_CAPINFO_DSSSOFDM) + *cp++ = 'D'; + *cp = '\0'; + return capstring; +} + +static const char * +getflags(int flags) +{ + static char flagstring[32]; + char *cp = flagstring; + + if (flags & IEEE80211_NODE_AUTH) + *cp++ = 'A'; + if (flags & IEEE80211_NODE_QOS) + *cp++ = 'Q'; + if (flags & IEEE80211_NODE_ERP) + *cp++ = 'E'; + if (flags & IEEE80211_NODE_PWR_MGT) + *cp++ = 'P'; + if (flags & IEEE80211_NODE_HT) { + *cp++ = 'H'; + if (flags & IEEE80211_NODE_HTCOMPAT) + *cp++ = '+'; + } + if (flags & IEEE80211_NODE_WPS) + *cp++ = 'W'; + if (flags & IEEE80211_NODE_TSN) + *cp++ = 'N'; + if (flags & IEEE80211_NODE_AMPDU_TX) + *cp++ = 'T'; + if (flags & IEEE80211_NODE_AMPDU_RX) + *cp++ = 'R'; + if (flags & IEEE80211_NODE_MIMO_PS) { + *cp++ = 'M'; + if (flags & IEEE80211_NODE_MIMO_RTS) + *cp++ = '+'; + } + if (flags & IEEE80211_NODE_RIFS) + *cp++ = 'I'; + if (flags & IEEE80211_NODE_SGI40) { + *cp++ = 'S'; + if (flags & IEEE80211_NODE_SGI20) + *cp++ = '+'; + } else if (flags & IEEE80211_NODE_SGI20) + *cp++ = 's'; + if (flags & IEEE80211_NODE_AMSDU_TX) + *cp++ = 't'; + if (flags & IEEE80211_NODE_AMSDU_RX) + *cp++ = 'r'; + *cp = '\0'; + return flagstring; +} + +static void +printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen) +{ + printf("%s", tag); + if (verbose) { + maxlen -= strlen(tag)+2; + if (2*ielen > maxlen) + maxlen--; + printf("<"); + for (; ielen > 0; ie++, ielen--) { + if (maxlen-- <= 0) + break; + printf("%02x", *ie); + } + if (ielen != 0) + printf("-"); + printf(">"); + } +} + +#define LE_READ_2(p) \ + ((u_int16_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8))) +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8) | \ + (((const u_int8_t *)(p))[2] << 16) | \ + (((const u_int8_t *)(p))[3] << 24))) + +/* + * NB: The decoding routines assume a properly formatted ie + * which should be safe as the kernel only retains them + * if they parse ok. + */ + +static void +printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + static const char *acnames[] = { "BE", "BK", "VO", "VI" }; + const struct ieee80211_wme_param *wme = + (const struct ieee80211_wme_param *) ie; + int i; + + printf("%s", tag); + if (!verbose) + return; + printf("<qosinfo 0x%x", wme->param_qosInfo); + ie += offsetof(struct ieee80211_wme_param, params_acParams); + for (i = 0; i < WME_NUM_AC; i++) { + const struct ieee80211_wme_acparams *ac = + &wme->params_acParams[i]; + + printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]" + , acnames[i] + , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : "" + , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN) + , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN) + , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX) + , LE_READ_2(&ac->acp_txop) + ); + } + printf(">"); +#undef MS +} + +static void +printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + printf("%s", tag); + if (verbose) { + const struct ieee80211_wme_info *wme = + (const struct ieee80211_wme_info *) ie; + printf("<version 0x%x info 0x%x>", + wme->wme_version, wme->wme_info); + } +} + +static void +printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + printf("%s", tag); + if (verbose) { + const struct ieee80211_ie_htcap *htcap = + (const struct ieee80211_ie_htcap *) ie; + const char *sep; + int i, j; + + printf("<cap 0x%x param 0x%x", + LE_READ_2(&htcap->hc_cap), htcap->hc_param); + printf(" mcsset["); + sep = ""; + for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) + if (isset(htcap->hc_mcsset, i)) { + for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++) + if (isclr(htcap->hc_mcsset, j)) + break; + j--; + if (i == j) + printf("%s%u", sep, i); + else + printf("%s%u-%u", sep, i, j); + i += j-i; + sep = ","; + } + printf("] extcap 0x%x txbf 0x%x antenna 0x%x>", + LE_READ_2(&htcap->hc_extcap), + LE_READ_4(&htcap->hc_txbf), + htcap->hc_antenna); + } +} + +static void +printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + printf("%s", tag); + if (verbose) { + const struct ieee80211_ie_htinfo *htinfo = + (const struct ieee80211_ie_htinfo *) ie; + const char *sep; + int i, j; + + printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel, + htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3, + LE_READ_2(&htinfo->hi_byte45)); + printf(" basicmcs["); + sep = ""; + for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) + if (isset(htinfo->hi_basicmcsset, i)) { + for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++) + if (isclr(htinfo->hi_basicmcsset, j)) + break; + j--; + if (i == j) + printf("%s%u", sep, i); + else + printf("%s%u-%u", sep, i, j); + i += j-i; + sep = ","; + } + printf("]>"); + } +} + +static void +printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + + printf("%s", tag); + if (verbose) { + const struct ieee80211_ath_ie *ath = + (const struct ieee80211_ath_ie *)ie; + + printf("<"); + if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME) + printf("DTURBO,"); + if (ath->ath_capability & ATHEROS_CAP_COMPRESSION) + printf("COMP,"); + if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME) + printf("FF,"); + if (ath->ath_capability & ATHEROS_CAP_XR) + printf("XR,"); + if (ath->ath_capability & ATHEROS_CAP_AR) + printf("AR,"); + if (ath->ath_capability & ATHEROS_CAP_BURST) + printf("BURST,"); + if (ath->ath_capability & ATHEROS_CAP_WME) + printf("WME,"); + if (ath->ath_capability & ATHEROS_CAP_BOOST) + printf("BOOST,"); + printf("0x%x>", LE_READ_2(ath->ath_defkeyix)); + } +} + + +static void +printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen) +{ +#define MATCHOUI(field, oui, string) \ +do { \ + if (memcmp(field, oui, 4) == 0) \ + printf("%s", string); \ +} while (0) + + printf("%s", tag); + if (verbose) { + const struct ieee80211_meshconf_ie *mconf = + (const struct ieee80211_meshconf_ie *)ie; + printf("<PATH:"); + if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP) + printf("HWMP"); + else + printf("UNKNOWN"); + printf(" LINK:"); + if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME) + printf("AIRTIME"); + else + printf("UNKNOWN"); + printf(" CONGESTION:"); + if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED) + printf("DISABLED"); + else + printf("UNKNOWN"); + printf(" SYNC:"); + if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF) + printf("NEIGHOFF"); + else + printf("UNKNOWN"); + printf(" AUTH:"); + if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED) + printf("DISABLED"); + else + printf("UNKNOWN"); + printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form, + mconf->conf_cap); + } +#undef MATCHOUI +} + +static const char * +wpa_cipher(const u_int8_t *sel) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_CSE_NULL): + return "NONE"; + case WPA_SEL(WPA_CSE_WEP40): + return "WEP40"; + case WPA_SEL(WPA_CSE_WEP104): + return "WEP104"; + case WPA_SEL(WPA_CSE_TKIP): + return "TKIP"; + case WPA_SEL(WPA_CSE_CCMP): + return "AES-CCMP"; + } + return "?"; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +static const char * +wpa_keymgmt(const u_int8_t *sel) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_ASE_8021X_UNSPEC): + return "8021X-UNSPEC"; + case WPA_SEL(WPA_ASE_8021X_PSK): + return "8021X-PSK"; + case WPA_SEL(WPA_ASE_NONE): + return "NONE"; + } + return "?"; +#undef WPA_SEL +} + +static void +printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + u_int8_t len = ie[1]; + + printf("%s", tag); + if (verbose) { + const char *sep; + int n; + + ie += 6, len -= 4; /* NB: len is payload only */ + + printf("<v%u", LE_READ_2(ie)); + ie += 2, len -= 2; + + printf(" mc:%s", wpa_cipher(ie)); + ie += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(ie); + ie += 2, len -= 2; + sep = " uc:"; + for (; n > 0; n--) { + printf("%s%s", sep, wpa_cipher(ie)); + ie += 4, len -= 4; + sep = "+"; + } + + /* key management algorithms */ + n = LE_READ_2(ie); + ie += 2, len -= 2; + sep = " km:"; + for (; n > 0; n--) { + printf("%s%s", sep, wpa_keymgmt(ie)); + ie += 4, len -= 4; + sep = "+"; + } + + if (len > 2) /* optional capabilities */ + printf(", caps 0x%x", LE_READ_2(ie)); + printf(">"); + } +} + +static const char * +rsn_cipher(const u_int8_t *sel) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_CSE_NULL): + return "NONE"; + case RSN_SEL(RSN_CSE_WEP40): + return "WEP40"; + case RSN_SEL(RSN_CSE_WEP104): + return "WEP104"; + case RSN_SEL(RSN_CSE_TKIP): + return "TKIP"; + case RSN_SEL(RSN_CSE_CCMP): + return "AES-CCMP"; + case RSN_SEL(RSN_CSE_WRAP): + return "AES-OCB"; + } + return "?"; +#undef WPA_SEL +} + +static const char * +rsn_keymgmt(const u_int8_t *sel) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_ASE_8021X_UNSPEC): + return "8021X-UNSPEC"; + case RSN_SEL(RSN_ASE_8021X_PSK): + return "8021X-PSK"; + case RSN_SEL(RSN_ASE_NONE): + return "NONE"; + } + return "?"; +#undef RSN_SEL +} + +static void +printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + printf("%s", tag); + if (verbose) { + const char *sep; + int n; + + ie += 2, ielen -= 2; + + printf("<v%u", LE_READ_2(ie)); + ie += 2, ielen -= 2; + + printf(" mc:%s", rsn_cipher(ie)); + ie += 4, ielen -= 4; + + /* unicast ciphers */ + n = LE_READ_2(ie); + ie += 2, ielen -= 2; + sep = " uc:"; + for (; n > 0; n--) { + printf("%s%s", sep, rsn_cipher(ie)); + ie += 4, ielen -= 4; + sep = "+"; + } + + /* key management algorithms */ + n = LE_READ_2(ie); + ie += 2, ielen -= 2; + sep = " km:"; + for (; n > 0; n--) { + printf("%s%s", sep, rsn_keymgmt(ie)); + ie += 4, ielen -= 4; + sep = "+"; + } + + if (ielen > 2) /* optional capabilities */ + printf(", caps 0x%x", LE_READ_2(ie)); + /* XXXPMKID */ + printf(">"); + } +} + +/* XXX move to a public include file */ +#define IEEE80211_WPS_DEV_PASS_ID 0x1012 +#define IEEE80211_WPS_SELECTED_REG 0x1041 +#define IEEE80211_WPS_SETUP_STATE 0x1044 +#define IEEE80211_WPS_UUID_E 0x1047 +#define IEEE80211_WPS_VERSION 0x104a + +#define BE_READ_2(p) \ + ((u_int16_t) \ + ((((const u_int8_t *)(p))[1] ) | \ + (((const u_int8_t *)(p))[0] << 8))) + +static void +printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + u_int8_t len = ie[1]; + + printf("%s", tag); + if (verbose) { + static const char *dev_pass_id[] = { + "D", /* Default (PIN) */ + "U", /* User-specified */ + "M", /* Machine-specified */ + "K", /* Rekey */ + "P", /* PushButton */ + "R" /* Registrar-specified */ + }; + int n; + + ie +=6, len -= 4; /* NB: len is payload only */ + + /* WPS IE in Beacon and Probe Resp frames have different fields */ + printf("<"); + while (len) { + uint16_t tlv_type = BE_READ_2(ie); + uint16_t tlv_len = BE_READ_2(ie + 2); + + ie += 4, len -= 4; + + switch (tlv_type) { + case IEEE80211_WPS_VERSION: + printf("v:%d.%d", *ie >> 4, *ie & 0xf); + break; + case IEEE80211_WPS_SETUP_STATE: + /* Only 1 and 2 are valid */ + if (*ie == 0 || *ie >= 3) + printf(" state:B"); + else + printf(" st:%s", *ie == 1 ? "N" : "C"); + break; + case IEEE80211_WPS_SELECTED_REG: + printf(" sel:%s", *ie ? "T" : "F"); + break; + case IEEE80211_WPS_DEV_PASS_ID: + n = LE_READ_2(ie); + if (n < N(dev_pass_id)) + printf(" dpi:%s", dev_pass_id[n]); + break; + case IEEE80211_WPS_UUID_E: + printf(" uuid-e:"); + for (n = 0; n < (tlv_len - 1); n++) + printf("%02x-", ie[n]); + printf("%02x", ie[n]); + break; + } + ie += tlv_len, len -= tlv_len; + } + printf(">"); + } +#undef N +} + +static void +printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + printf("%s", tag); + if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) { + const struct ieee80211_tdma_param *tdma = + (const struct ieee80211_tdma_param *) ie; + + /* XXX tstamp */ + printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>", + tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt, + LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval, + tdma->tdma_inuse[0]); + } +} + +/* + * Copy the ssid string contents into buf, truncating to fit. If the + * ssid is entirely printable then just copy intact. Otherwise convert + * to hexadecimal. If the result is truncated then replace the last + * three characters with "...". + */ +static int +copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len) +{ + const u_int8_t *p; + size_t maxlen; + int i; + + if (essid_len > bufsize) + maxlen = bufsize; + else + maxlen = essid_len; + /* determine printable or not */ + for (i = 0, p = essid; i < maxlen; i++, p++) { + if (*p < ' ' || *p > 0x7e) + break; + } + if (i != maxlen) { /* not printable, print as hex */ + if (bufsize < 3) + return 0; + strlcpy(buf, "0x", bufsize); + bufsize -= 2; + p = essid; + for (i = 0; i < maxlen && bufsize >= 2; i++) { + sprintf(&buf[2+2*i], "%02x", p[i]); + bufsize -= 2; + } + if (i != essid_len) + memcpy(&buf[2+2*i-3], "...", 3); + } else { /* printable, truncate as needed */ + memcpy(buf, essid, maxlen); + if (maxlen != essid_len) + memcpy(&buf[maxlen-3], "...", 3); + } + return maxlen; +} + +static void +printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + char ssid[2*IEEE80211_NWID_LEN+1]; + + printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid); +} + +static void +printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + const char *sep; + int i; + + printf("%s", tag); + sep = "<"; + for (i = 2; i < ielen; i++) { + printf("%s%s%d", sep, + ie[i] & IEEE80211_RATE_BASIC ? "B" : "", + ie[i] & IEEE80211_RATE_VAL); + sep = ","; + } + printf(">"); +} + +static void +printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) +{ + const struct ieee80211_country_ie *cie = + (const struct ieee80211_country_ie *) ie; + int i, nbands, schan, nchan; + + printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]); + nbands = (cie->len - 3) / sizeof(cie->band[0]); + for (i = 0; i < nbands; i++) { + schan = cie->band[i].schan; + nchan = cie->band[i].nchan; + if (nchan != 1) + printf(" %u-%u,%u", schan, schan + nchan-1, + cie->band[i].maxtxpwr); + else + printf(" %u,%u", schan, cie->band[i].maxtxpwr); + } + printf(">"); +} + +/* unaligned little endian access */ +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8) | \ + (((const u_int8_t *)(p))[2] << 16) | \ + (((const u_int8_t *)(p))[3] << 24))) + +static __inline int +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static __inline int +iswmeinfo(const u_int8_t *frm) +{ + return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && + frm[6] == WME_INFO_OUI_SUBTYPE; +} + +static __inline int +iswmeparam(const u_int8_t *frm) +{ + return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && + frm[6] == WME_PARAM_OUI_SUBTYPE; +} + +static __inline int +isatherosoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); +} + +static __inline int +istdmaoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI); +} + +static __inline int +iswpsoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI); +} + +static const char * +iename(int elemid) +{ + switch (elemid) { + case IEEE80211_ELEMID_FHPARMS: return " FHPARMS"; + case IEEE80211_ELEMID_CFPARMS: return " CFPARMS"; + case IEEE80211_ELEMID_TIM: return " TIM"; + case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS"; + case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE"; + case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR"; + case IEEE80211_ELEMID_PWRCAP: return " PWRCAP"; + case IEEE80211_ELEMID_TPCREQ: return " TPCREQ"; + case IEEE80211_ELEMID_TPCREP: return " TPCREP"; + case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN"; + case IEEE80211_ELEMID_CSA: return " CSA"; + case IEEE80211_ELEMID_MEASREQ: return " MEASREQ"; + case IEEE80211_ELEMID_MEASREP: return " MEASREP"; + case IEEE80211_ELEMID_QUIET: return " QUIET"; + case IEEE80211_ELEMID_IBSSDFS: return " IBSSDFS"; + case IEEE80211_ELEMID_TPC: return " TPC"; + case IEEE80211_ELEMID_CCKM: return " CCKM"; + } + return " ???"; +} + +static void +printies(const u_int8_t *vp, int ielen, int maxcols) +{ + while (ielen > 0) { + switch (vp[0]) { + case IEEE80211_ELEMID_SSID: + if (verbose) + printssid(" SSID", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_RATES: + case IEEE80211_ELEMID_XRATES: + if (verbose) + printrates(vp[0] == IEEE80211_ELEMID_RATES ? + " RATES" : " XRATES", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_DSPARMS: + if (verbose) + printf(" DSPARMS<%u>", vp[2]); + break; + case IEEE80211_ELEMID_COUNTRY: + if (verbose) + printcountry(" COUNTRY", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_ERP: + if (verbose) + printf(" ERP<0x%x>", vp[2]); + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(vp)) + printwpaie(" WPA", vp, 2+vp[1], maxcols); + else if (iswmeinfo(vp)) + printwmeinfo(" WME", vp, 2+vp[1], maxcols); + else if (iswmeparam(vp)) + printwmeparam(" WME", vp, 2+vp[1], maxcols); + else if (isatherosoui(vp)) + printathie(" ATH", vp, 2+vp[1], maxcols); + else if (iswpsoui(vp)) + printwpsie(" WPS", vp, 2+vp[1], maxcols); + else if (istdmaoui(vp)) + printtdmaie(" TDMA", vp, 2+vp[1], maxcols); + else if (verbose) + printie(" VEN", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_RSN: + printrsnie(" RSN", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_HTCAP: + printhtcap(" HTCAP", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_HTINFO: + if (verbose) + printhtinfo(" HTINFO", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_MESHID: + if (verbose) + printssid(" MESHID", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_MESHCONF: + printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols); + break; + default: + if (verbose) + printie(iename(vp[0]), vp, 2+vp[1], maxcols); + break; + } + ielen -= 2+vp[1]; + vp += 2+vp[1]; + } +} + +static void +printmimo(const struct ieee80211_mimo_info *mi) +{ + /* NB: don't muddy display unless there's something to show */ + if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) { + /* XXX ignore EVM for now */ + printf(" (rssi %d:%d:%d nf %d:%d:%d)", + mi->rssi[0], mi->rssi[1], mi->rssi[2], + mi->noise[0], mi->noise[1], mi->noise[2]); + } +} + +static void +list_scan(int s) +{ + uint8_t buf[24*1024]; + char ssid[IEEE80211_NWID_LEN+1]; + const uint8_t *cp; + int len, ssidmax, idlen; + + if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0) + errx(1, "unable to get scan results"); + if (len < sizeof(struct ieee80211req_scan_result)) + return; + + getchaninfo(s); + + ssidmax = verbose ? IEEE80211_NWID_LEN - 1 : 14; + printf("%-*.*s %-17.17s %4s %4s %-7s %3s %4s\n" + , ssidmax, ssidmax, "SSID/MESH ID" + , "BSSID" + , "CHAN" + , "RATE" + , " S:N" + , "INT" + , "CAPS" + ); + cp = buf; + do { + const struct ieee80211req_scan_result *sr; + const uint8_t *vp, *idp; + + sr = (const struct ieee80211req_scan_result *) cp; + vp = cp + sr->isr_ie_off; + if (sr->isr_meshid_len) { + idp = vp + sr->isr_ssid_len; + idlen = sr->isr_meshid_len; + } else { + idp = vp; + idlen = sr->isr_ssid_len; + } + printf("%-*.*s %s %3d %3dM %3d:%-3d %3d %-4.4s" + , ssidmax + , copy_essid(ssid, ssidmax, idp, idlen) + , ssid + , ether_ntoa((const struct ether_addr *) sr->isr_bssid) + , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags) + , getmaxrate(sr->isr_rates, sr->isr_nrates) + , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise + , sr->isr_intval + , getcaps(sr->isr_capinfo) + ); + printies(vp + sr->isr_ssid_len + sr->isr_meshid_len, + sr->isr_ie_len, 24); + printf("\n"); + cp += sr->isr_len, len -= sr->isr_len; + } while (len >= sizeof(struct ieee80211req_scan_result)); +} + +static void +scan_and_wait(int s) +{ + struct ieee80211_scan_req sr; + struct ieee80211req ireq; + int sroute; + + sroute = socket(PF_ROUTE, SOCK_RAW, 0); + if (sroute < 0) { + perror("socket(PF_ROUTE,SOCK_RAW)"); + return; + } + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_SCAN_REQ; + + memset(&sr, 0, sizeof(sr)); + sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE + | IEEE80211_IOC_SCAN_NOPICK + | IEEE80211_IOC_SCAN_ONCE; + sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; + sr.sr_nssid = 0; + + ireq.i_data = &sr; + ireq.i_len = sizeof(sr); + /* NB: only root can trigger a scan so ignore errors */ + if (ioctl(s, SIOCS80211, &ireq) >= 0) { + char buf[2048]; + struct if_announcemsghdr *ifan; + struct rt_msghdr *rtm; + + do { + if (read(sroute, buf, sizeof(buf)) < 0) { + perror("read(PF_ROUTE)"); + break; + } + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) + break; + ifan = (struct if_announcemsghdr *) rtm; + } while (rtm->rtm_type != RTM_IEEE80211 || + ifan->ifan_what != RTM_IEEE80211_SCAN); + } + close(sroute); +} + +static +DECL_CMD_FUNC(set80211scan, val, d) +{ + scan_and_wait(s); + list_scan(s); +} + +static enum ieee80211_opmode get80211opmode(int s); + +static int +gettxseq(const struct ieee80211req_sta_info *si) +{ + int i, txseq; + + if ((si->isi_state & IEEE80211_NODE_QOS) == 0) + return si->isi_txseqs[0]; + /* XXX not right but usually what folks want */ + txseq = 0; + for (i = 0; i < IEEE80211_TID_SIZE; i++) + if (si->isi_txseqs[i] > txseq) + txseq = si->isi_txseqs[i]; + return txseq; +} + +static int +getrxseq(const struct ieee80211req_sta_info *si) +{ + int i, rxseq; + + if ((si->isi_state & IEEE80211_NODE_QOS) == 0) + return si->isi_rxseqs[0]; + /* XXX not right but usually what folks want */ + rxseq = 0; + for (i = 0; i < IEEE80211_TID_SIZE; i++) + if (si->isi_rxseqs[i] > rxseq) + rxseq = si->isi_rxseqs[i]; + return rxseq; +} + +static void +list_stations(int s) +{ + union { + struct ieee80211req_sta_req req; + uint8_t buf[24*1024]; + } u; + enum ieee80211_opmode opmode = get80211opmode(s); + const uint8_t *cp; + int len; + + /* broadcast address =>'s get all stations */ + (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); + if (opmode == IEEE80211_M_STA) { + /* + * Get information about the associated AP. + */ + (void) get80211(s, IEEE80211_IOC_BSSID, + u.req.is_u.macaddr, IEEE80211_ADDR_LEN); + } + if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0) + errx(1, "unable to get station information"); + if (len < sizeof(struct ieee80211req_sta_info)) + return; + + getchaninfo(s); + + if (opmode == IEEE80211_M_MBSS) + printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n" + , "ADDR" + , "CHAN" + , "LOCAL" + , "PEER" + , "STATE" + , "RATE" + , "RSSI" + , "IDLE" + , "TXSEQ" + , "RXSEQ" + ); + else + printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n" + , "ADDR" + , "AID" + , "CHAN" + , "RATE" + , "RSSI" + , "IDLE" + , "TXSEQ" + , "RXSEQ" + , "CAPS" + , "FLAG" + ); + cp = (const uint8_t *) u.req.info; + do { + const struct ieee80211req_sta_info *si; + + si = (const struct ieee80211req_sta_info *) cp; + if (si->isi_len < sizeof(*si)) + break; + if (opmode == IEEE80211_M_MBSS) + printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d" + , ether_ntoa((const struct ether_addr*) + si->isi_macaddr) + , ieee80211_mhz2ieee(si->isi_freq, + si->isi_flags) + , si->isi_localid + , si->isi_peerid + , mesh_linkstate_string(si->isi_peerstate) + , si->isi_txmbps/2 + , si->isi_rssi/2. + , si->isi_inact + , gettxseq(si) + , getrxseq(si) + ); + else + printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-7.7s" + , ether_ntoa((const struct ether_addr*) + si->isi_macaddr) + , IEEE80211_AID(si->isi_associd) + , ieee80211_mhz2ieee(si->isi_freq, + si->isi_flags) + , si->isi_txmbps/2 + , si->isi_rssi/2. + , si->isi_inact + , gettxseq(si) + , getrxseq(si) + , getcaps(si->isi_capinfo) + , getflags(si->isi_state) + ); + printies(cp + si->isi_ie_off, si->isi_ie_len, 24); + printmimo(&si->isi_mimo); + printf("\n"); + cp += si->isi_len, len -= si->isi_len; + } while (len >= sizeof(struct ieee80211req_sta_info)); +} + +static const char * +mesh_linkstate_string(uint8_t state) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const char *state_names[] = { + [0] = "IDLE", + [1] = "OPEN-TX", + [2] = "OPEN-RX", + [3] = "CONF-RX", + [4] = "ESTAB", + [5] = "HOLDING", + }; + + if (state >= N(state_names)) { + static char buf[10]; + snprintf(buf, sizeof(buf), "#%u", state); + return buf; + } else + return state_names[state]; +#undef N +} + +static const char * +get_chaninfo(const struct ieee80211_channel *c, int precise, + char buf[], size_t bsize) +{ + buf[0] = '\0'; + if (IEEE80211_IS_CHAN_FHSS(c)) + strlcat(buf, " FHSS", bsize); + if (IEEE80211_IS_CHAN_A(c)) + strlcat(buf, " 11a", bsize); + else if (IEEE80211_IS_CHAN_ANYG(c)) + strlcat(buf, " 11g", bsize); + else if (IEEE80211_IS_CHAN_B(c)) + strlcat(buf, " 11b", bsize); + if (IEEE80211_IS_CHAN_HALF(c)) + strlcat(buf, "/10MHz", bsize); + if (IEEE80211_IS_CHAN_QUARTER(c)) + strlcat(buf, "/5MHz", bsize); + if (IEEE80211_IS_CHAN_TURBO(c)) + strlcat(buf, " Turbo", bsize); + if (precise) { + if (IEEE80211_IS_CHAN_HT20(c)) + strlcat(buf, " ht/20", bsize); + else if (IEEE80211_IS_CHAN_HT40D(c)) + strlcat(buf, " ht/40-", bsize); + else if (IEEE80211_IS_CHAN_HT40U(c)) + strlcat(buf, " ht/40+", bsize); + } else { + if (IEEE80211_IS_CHAN_HT(c)) + strlcat(buf, " ht", bsize); + } + return buf; +} + +static void +print_chaninfo(const struct ieee80211_channel *c, int verb) +{ + char buf[14]; + + printf("Channel %3u : %u%c MHz%-14.14s", + ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq, + IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', + get_chaninfo(c, verb, buf, sizeof(buf))); +} + +static int +chanpref(const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_HT40(c)) + return 40; + if (IEEE80211_IS_CHAN_HT20(c)) + return 30; + if (IEEE80211_IS_CHAN_HALF(c)) + return 10; + if (IEEE80211_IS_CHAN_QUARTER(c)) + return 5; + if (IEEE80211_IS_CHAN_TURBO(c)) + return 25; + if (IEEE80211_IS_CHAN_A(c)) + return 20; + if (IEEE80211_IS_CHAN_G(c)) + return 20; + if (IEEE80211_IS_CHAN_B(c)) + return 15; + if (IEEE80211_IS_CHAN_PUREG(c)) + return 15; + return 0; +} + +static void +print_channels(int s, const struct ieee80211req_chaninfo *chans, + int allchans, int verb) +{ + struct ieee80211req_chaninfo *achans; + uint8_t reported[IEEE80211_CHAN_BYTES]; + const struct ieee80211_channel *c; + int i, half; + + achans = malloc(IEEE80211_CHANINFO_SPACE(chans)); + if (achans == NULL) + errx(1, "no space for active channel list"); + achans->ic_nchans = 0; + memset(reported, 0, sizeof(reported)); + if (!allchans) { + struct ieee80211req_chanlist active; + + if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0) + errx(1, "unable to get active channel list"); + for (i = 0; i < chans->ic_nchans; i++) { + c = &chans->ic_chans[i]; + if (!isset(active.ic_channels, c->ic_ieee)) + continue; + /* + * Suppress compatible duplicates unless + * verbose. The kernel gives us it's + * complete channel list which has separate + * entries for 11g/11b and 11a/turbo. + */ + if (isset(reported, c->ic_ieee) && !verb) { + /* XXX we assume duplicates are adjacent */ + achans->ic_chans[achans->ic_nchans-1] = *c; + } else { + achans->ic_chans[achans->ic_nchans++] = *c; + setbit(reported, c->ic_ieee); + } + } + } else { + for (i = 0; i < chans->ic_nchans; i++) { + c = &chans->ic_chans[i]; + /* suppress duplicates as above */ + if (isset(reported, c->ic_ieee) && !verb) { + /* XXX we assume duplicates are adjacent */ + struct ieee80211_channel *a = + &achans->ic_chans[achans->ic_nchans-1]; + if (chanpref(c) > chanpref(a)) + *a = *c; + } else { + achans->ic_chans[achans->ic_nchans++] = *c; + setbit(reported, c->ic_ieee); + } + } + } + half = achans->ic_nchans / 2; + if (achans->ic_nchans % 2) + half++; + + for (i = 0; i < achans->ic_nchans / 2; i++) { + print_chaninfo(&achans->ic_chans[i], verb); + print_chaninfo(&achans->ic_chans[half+i], verb); + printf("\n"); + } + if (achans->ic_nchans % 2) { + print_chaninfo(&achans->ic_chans[i], verb); + printf("\n"); + } + free(achans); +} + +static void +list_channels(int s, int allchans) +{ + getchaninfo(s); + print_channels(s, chaninfo, allchans, verbose); +} + +static void +print_txpow(const struct ieee80211_channel *c) +{ + printf("Channel %3u : %u MHz %3.1f reg %2d ", + c->ic_ieee, c->ic_freq, + c->ic_maxpower/2., c->ic_maxregpower); +} + +static void +print_txpow_verbose(const struct ieee80211_channel *c) +{ + print_chaninfo(c, 1); + printf("min %4.1f dBm max %3.1f dBm reg %2d dBm", + c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower); + /* indicate where regulatory cap limits power use */ + if (c->ic_maxpower > 2*c->ic_maxregpower) + printf(" <"); +} + +static void +list_txpow(int s) +{ + struct ieee80211req_chaninfo *achans; + uint8_t reported[IEEE80211_CHAN_BYTES]; + struct ieee80211_channel *c, *prev; + int i, half; + + getchaninfo(s); + achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo)); + if (achans == NULL) + errx(1, "no space for active channel list"); + achans->ic_nchans = 0; + memset(reported, 0, sizeof(reported)); + for (i = 0; i < chaninfo->ic_nchans; i++) { + c = &chaninfo->ic_chans[i]; + /* suppress duplicates as above */ + if (isset(reported, c->ic_ieee) && !verbose) { + /* XXX we assume duplicates are adjacent */ + prev = &achans->ic_chans[achans->ic_nchans-1]; + /* display highest power on channel */ + if (c->ic_maxpower > prev->ic_maxpower) + *prev = *c; + } else { + achans->ic_chans[achans->ic_nchans++] = *c; + setbit(reported, c->ic_ieee); + } + } + if (!verbose) { + half = achans->ic_nchans / 2; + if (achans->ic_nchans % 2) + half++; + + for (i = 0; i < achans->ic_nchans / 2; i++) { + print_txpow(&achans->ic_chans[i]); + print_txpow(&achans->ic_chans[half+i]); + printf("\n"); + } + if (achans->ic_nchans % 2) { + print_txpow(&achans->ic_chans[i]); + printf("\n"); + } + } else { + for (i = 0; i < achans->ic_nchans; i++) { + print_txpow_verbose(&achans->ic_chans[i]); + printf("\n"); + } + } + free(achans); +} + +static void +list_keys(int s) +{ +} + +#define IEEE80211_C_BITS \ + "\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \ + "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \ + "\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \ + "\37TXFRAG\40TDMA" + +static void +list_capabilities(int s) +{ + struct ieee80211_devcaps_req *dc; + + if (verbose) + dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN)); + else + dc = malloc(IEEE80211_DEVCAPS_SIZE(1)); + if (dc == NULL) + errx(1, "no space for device capabilities"); + dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1; + getdevcaps(s, dc); + printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS); + if (dc->dc_cryptocaps != 0 || verbose) { + putchar('\n'); + printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS); + } + if (dc->dc_htcaps != 0 || verbose) { + putchar('\n'); + printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS); + } + putchar('\n'); + if (verbose) { + chaninfo = &dc->dc_chaninfo; /* XXX */ + print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose); + } + free(dc); +} + +static int +get80211wme(int s, int param, int ac, int *val) +{ + struct ieee80211req ireq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = param; + ireq.i_len = ac; + if (ioctl(s, SIOCG80211, &ireq) < 0) { + warn("cannot get WME parameter %d, ac %d%s", + param, ac & IEEE80211_WMEPARAM_VAL, + ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : ""); + return -1; + } + *val = ireq.i_val; + return 0; +} + +static void +list_wme_aci(int s, const char *tag, int ac) +{ + int val; + + printf("\t%s", tag); + + /* show WME BSS parameters */ + if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1) + printf(" cwmin %2u", val); + if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1) + printf(" cwmax %2u", val); + if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1) + printf(" aifs %2u", val); + if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1) + printf(" txopLimit %3u", val); + if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) { + if (val) + printf(" acm"); + else if (verbose) + printf(" -acm"); + } + /* !BSS only */ + if ((ac & IEEE80211_WMEPARAM_BSS) == 0) { + if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) { + if (!val) + printf(" -ack"); + else if (verbose) + printf(" ack"); + } + } + printf("\n"); +} + +static void +list_wme(int s) +{ + static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" }; + int ac; + + if (verbose) { + /* display both BSS and local settings */ + for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) { + again: + if (ac & IEEE80211_WMEPARAM_BSS) + list_wme_aci(s, " ", ac); + else + list_wme_aci(s, acnames[ac], ac); + if ((ac & IEEE80211_WMEPARAM_BSS) == 0) { + ac |= IEEE80211_WMEPARAM_BSS; + goto again; + } else + ac &= ~IEEE80211_WMEPARAM_BSS; + } + } else { + /* display only channel settings */ + for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) + list_wme_aci(s, acnames[ac], ac); + } +} + +static void +list_roam(int s) +{ + const struct ieee80211_roamparam *rp; + int mode; + + getroam(s); + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { + rp = &roamparams.params[mode]; + if (rp->rssi == 0 && rp->rate == 0) + continue; + if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) { + if (rp->rssi & 1) + LINE_CHECK("roam:%-7.7s rssi %2u.5dBm MCS %2u ", + modename[mode], rp->rssi/2, + rp->rate &~ IEEE80211_RATE_MCS); + else + LINE_CHECK("roam:%-7.7s rssi %4udBm MCS %2u ", + modename[mode], rp->rssi/2, + rp->rate &~ IEEE80211_RATE_MCS); + } else { + if (rp->rssi & 1) + LINE_CHECK("roam:%-7.7s rssi %2u.5dBm rate %2u Mb/s", + modename[mode], rp->rssi/2, rp->rate/2); + else + LINE_CHECK("roam:%-7.7s rssi %4udBm rate %2u Mb/s", + modename[mode], rp->rssi/2, rp->rate/2); + } + } +} + +static void +list_txparams(int s) +{ + const struct ieee80211_txparam *tp; + int mode; + + gettxparams(s); + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { + tp = &txparams.params[mode]; + if (tp->mgmtrate == 0 && tp->mcastrate == 0) + continue; + if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) { + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + LINE_CHECK("%-7.7s ucast NONE mgmt %2u MCS " + "mcast %2u MCS maxretry %u", + modename[mode], + tp->mgmtrate &~ IEEE80211_RATE_MCS, + tp->mcastrate &~ IEEE80211_RATE_MCS, + tp->maxretry); + else + LINE_CHECK("%-7.7s ucast %2u MCS mgmt %2u MCS " + "mcast %2u MCS maxretry %u", + modename[mode], + tp->ucastrate &~ IEEE80211_RATE_MCS, + tp->mgmtrate &~ IEEE80211_RATE_MCS, + tp->mcastrate &~ IEEE80211_RATE_MCS, + tp->maxretry); + } else { + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + LINE_CHECK("%-7.7s ucast NONE mgmt %2u Mb/s " + "mcast %2u Mb/s maxretry %u", + modename[mode], + tp->mgmtrate/2, + tp->mcastrate/2, tp->maxretry); + else + LINE_CHECK("%-7.7s ucast %2u Mb/s mgmt %2u Mb/s " + "mcast %2u Mb/s maxretry %u", + modename[mode], + tp->ucastrate/2, tp->mgmtrate/2, + tp->mcastrate/2, tp->maxretry); + } + } +} + +static void +printpolicy(int policy) +{ + switch (policy) { + case IEEE80211_MACCMD_POLICY_OPEN: + printf("policy: open\n"); + break; + case IEEE80211_MACCMD_POLICY_ALLOW: + printf("policy: allow\n"); + break; + case IEEE80211_MACCMD_POLICY_DENY: + printf("policy: deny\n"); + break; + case IEEE80211_MACCMD_POLICY_RADIUS: + printf("policy: radius\n"); + break; + default: + printf("policy: unknown (%u)\n", policy); + break; + } +} + +static void +list_mac(int s) +{ + struct ieee80211req ireq; + struct ieee80211req_maclist *acllist; + int i, nacls, policy, len; + uint8_t *data; + char c; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */ + ireq.i_type = IEEE80211_IOC_MACCMD; + ireq.i_val = IEEE80211_MACCMD_POLICY; + if (ioctl(s, SIOCG80211, &ireq) < 0) { + if (errno == EINVAL) { + printf("No acl policy loaded\n"); + return; + } + err(1, "unable to get mac policy"); + } + policy = ireq.i_val; + if (policy == IEEE80211_MACCMD_POLICY_OPEN) { + c = '*'; + } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) { + c = '+'; + } else if (policy == IEEE80211_MACCMD_POLICY_DENY) { + c = '-'; + } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) { + c = 'r'; /* NB: should never have entries */ + } else { + printf("policy: unknown (%u)\n", policy); + c = '?'; + } + if (verbose || c == '?') + printpolicy(policy); + + ireq.i_val = IEEE80211_MACCMD_LIST; + ireq.i_len = 0; + if (ioctl(s, SIOCG80211, &ireq) < 0) + err(1, "unable to get mac acl list size"); + if (ireq.i_len == 0) { /* NB: no acls */ + if (!(verbose || c == '?')) + printpolicy(policy); + return; + } + len = ireq.i_len; + + data = malloc(len); + if (data == NULL) + err(1, "out of memory for acl list"); + + ireq.i_data = data; + if (ioctl(s, SIOCG80211, &ireq) < 0) + err(1, "unable to get mac acl list"); + nacls = len / sizeof(*acllist); + acllist = (struct ieee80211req_maclist *) data; + for (i = 0; i < nacls; i++) + printf("%c%s\n", c, ether_ntoa( + (const struct ether_addr *) acllist[i].ml_macaddr)); + free(data); +} + +static void +print_regdomain(const struct ieee80211_regdomain *reg, int verb) +{ + if ((reg->regdomain != 0 && + reg->regdomain != reg->country) || verb) { + const struct regdomain *rd = + lib80211_regdomain_findbysku(getregdata(), reg->regdomain); + if (rd == NULL) + LINE_CHECK("regdomain %d", reg->regdomain); + else + LINE_CHECK("regdomain %s", rd->name); + } + if (reg->country != 0 || verb) { + const struct country *cc = + lib80211_country_findbycc(getregdata(), reg->country); + if (cc == NULL) + LINE_CHECK("country %d", reg->country); + else + LINE_CHECK("country %s", cc->isoname); + } + if (reg->location == 'I') + LINE_CHECK("indoor"); + else if (reg->location == 'O') + LINE_CHECK("outdoor"); + else if (verb) + LINE_CHECK("anywhere"); + if (reg->ecm) + LINE_CHECK("ecm"); + else if (verb) + LINE_CHECK("-ecm"); +} + +static void +list_regdomain(int s, int channelsalso) +{ + getregdomain(s); + if (channelsalso) { + getchaninfo(s); + spacer = ':'; + print_regdomain(®domain, 1); + LINE_BREAK(); + print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/); + } else + print_regdomain(®domain, verbose); +} + +static void +list_mesh(int s) +{ + struct ieee80211req ireq; + struct ieee80211req_mesh_route routes[128]; + struct ieee80211req_mesh_route *rt; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_MESH_RTCMD; + ireq.i_val = IEEE80211_MESH_RTCMD_LIST; + ireq.i_data = &routes; + ireq.i_len = sizeof(routes); + if (ioctl(s, SIOCG80211, &ireq) < 0) + err(1, "unable to get the Mesh routing table"); + + printf("%-17.17s %-17.17s %4s %4s %4s %6s %s\n" + , "DEST" + , "NEXT HOP" + , "HOPS" + , "METRIC" + , "LIFETIME" + , "MSEQ" + , "FLAGS"); + + for (rt = &routes[0]; rt - &routes[0] < ireq.i_len / sizeof(*rt); rt++){ + printf("%s ", + ether_ntoa((const struct ether_addr *)rt->imr_dest)); + printf("%s %4u %4u %6u %6u %c%c\n", + ether_ntoa((const struct ether_addr *)rt->imr_nexthop), + rt->imr_nhops, rt->imr_metric, rt->imr_lifetime, + rt->imr_lastmseq, + (rt->imr_flags & IEEE80211_MESHRT_FLAGS_VALID) ? + 'V' : '!', + (rt->imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) ? + 'P' : ' '); + } +} + +static +DECL_CMD_FUNC(set80211list, arg, d) +{ +#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) + + LINE_INIT('\t'); + + if (iseq(arg, "sta")) + list_stations(s); + else if (iseq(arg, "scan") || iseq(arg, "ap")) + list_scan(s); + else if (iseq(arg, "chan") || iseq(arg, "freq")) + list_channels(s, 1); + else if (iseq(arg, "active")) + list_channels(s, 0); + else if (iseq(arg, "keys")) + list_keys(s); + else if (iseq(arg, "caps")) + list_capabilities(s); + else if (iseq(arg, "wme") || iseq(arg, "wmm")) + list_wme(s); + else if (iseq(arg, "mac")) + list_mac(s); + else if (iseq(arg, "txpow")) + list_txpow(s); + else if (iseq(arg, "roam")) + list_roam(s); + else if (iseq(arg, "txparam") || iseq(arg, "txparm")) + list_txparams(s); + else if (iseq(arg, "regdomain")) + list_regdomain(s, 1); + else if (iseq(arg, "countries")) + list_countries(); + else if (iseq(arg, "mesh")) + list_mesh(s); + else + errx(1, "Don't know how to list %s for %s", arg, name); + LINE_BREAK(); +#undef iseq +} + +static enum ieee80211_opmode +get80211opmode(int s) +{ + struct ifmediareq ifmr; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { + if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { + if (ifmr.ifm_current & IFM_FLAG0) + return IEEE80211_M_AHDEMO; + else + return IEEE80211_M_IBSS; + } + if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + return IEEE80211_M_HOSTAP; + if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + return IEEE80211_M_MONITOR; + if (ifmr.ifm_current & IFM_IEEE80211_MBSS) + return IEEE80211_M_MBSS; + } + return IEEE80211_M_STA; +} + +#if 0 +static void +printcipher(int s, struct ieee80211req *ireq, int keylenop) +{ + switch (ireq->i_val) { + case IEEE80211_CIPHER_WEP: + ireq->i_type = keylenop; + if (ioctl(s, SIOCG80211, ireq) != -1) + printf("WEP-%s", + ireq->i_len <= 5 ? "40" : + ireq->i_len <= 13 ? "104" : "128"); + else + printf("WEP"); + break; + case IEEE80211_CIPHER_TKIP: + printf("TKIP"); + break; + case IEEE80211_CIPHER_AES_OCB: + printf("AES-OCB"); + break; + case IEEE80211_CIPHER_AES_CCM: + printf("AES-CCM"); + break; + case IEEE80211_CIPHER_CKIP: + printf("CKIP"); + break; + case IEEE80211_CIPHER_NONE: + printf("NONE"); + break; + default: + printf("UNKNOWN (0x%x)", ireq->i_val); + break; + } +} +#endif + +static void +printkey(const struct ieee80211req_key *ik) +{ + static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; + int keylen = ik->ik_keylen; + int printcontents; + + printcontents = printkeys && + (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose); + if (printcontents) + LINE_BREAK(); + switch (ik->ik_type) { + case IEEE80211_CIPHER_WEP: + /* compatibility */ + LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1, + keylen <= 5 ? "40-bit" : + keylen <= 13 ? "104-bit" : "128-bit"); + break; + case IEEE80211_CIPHER_TKIP: + if (keylen > 128/8) + keylen -= 128/8; /* ignore MIC for now */ + LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_AES_OCB: + LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_AES_CCM: + LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_CKIP: + LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_NONE: + LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen); + break; + default: + LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit", + ik->ik_type, ik->ik_keyix+1, 8*keylen); + break; + } + if (printcontents) { + int i; + + printf(" <"); + for (i = 0; i < keylen; i++) + printf("%02x", ik->ik_keydata[i]); + printf(">"); + if (ik->ik_type != IEEE80211_CIPHER_WEP && + (ik->ik_keyrsc != 0 || verbose)) + printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc); + if (ik->ik_type != IEEE80211_CIPHER_WEP && + (ik->ik_keytsc != 0 || verbose)) + printf(" tsc %ju", (uintmax_t)ik->ik_keytsc); + if (ik->ik_flags != 0 && verbose) { + const char *sep = " "; + + if (ik->ik_flags & IEEE80211_KEY_XMIT) + printf("%stx", sep), sep = "+"; + if (ik->ik_flags & IEEE80211_KEY_RECV) + printf("%srx", sep), sep = "+"; + if (ik->ik_flags & IEEE80211_KEY_DEFAULT) + printf("%sdef", sep), sep = "+"; + } + LINE_BREAK(); + } +} + +static void +printrate(const char *tag, int v, int defrate, int defmcs) +{ + if ((v & IEEE80211_RATE_MCS) == 0) { + if (v != defrate) { + if (v & 1) + LINE_CHECK("%s %d.5", tag, v/2); + else + LINE_CHECK("%s %d", tag, v/2); + } + } else { + if (v != defmcs) + LINE_CHECK("%s %d", tag, v &~ 0x80); + } +} + +static int +getid(int s, int ix, void *data, size_t len, int *plen, int mesh) +{ + struct ieee80211req ireq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID; + ireq.i_val = ix; + ireq.i_data = data; + ireq.i_len = len; + if (ioctl(s, SIOCG80211, &ireq) < 0) + return -1; + *plen = ireq.i_len; + return 0; +} + +static void +ieee80211_status(int s) +{ + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + enum ieee80211_opmode opmode = get80211opmode(s); + int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode; + uint8_t data[32]; + const struct ieee80211_channel *c; + const struct ieee80211_roamparam *rp; + const struct ieee80211_txparam *tp; + + if (getid(s, -1, data, sizeof(data), &len, 0) < 0) { + /* If we can't get the SSID, this isn't an 802.11 device. */ + return; + } + + /* + * Invalidate cached state so printing status for multiple + * if's doesn't reuse the first interfaces' cached state. + */ + gotcurchan = 0; + gotroam = 0; + gottxparams = 0; + gothtconf = 0; + gotregdomain = 0; + + printf("\t"); + if (opmode == IEEE80211_M_MBSS) { + printf("meshid "); + getid(s, 0, data, sizeof(data), &len, 1); + print_string(data, len); + } else { + if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0) + num = 0; + printf("ssid "); + if (num > 1) { + for (i = 0; i < num; i++) { + if (getid(s, i, data, sizeof(data), &len, 0) >= 0 && len > 0) { + printf(" %d:", i + 1); + print_string(data, len); + } + } + } else + print_string(data, len); + } + c = getcurchan(s); + if (c->ic_freq != IEEE80211_CHAN_ANY) { + char buf[14]; + printf(" channel %d (%u MHz%s)", c->ic_ieee, c->ic_freq, + get_chaninfo(c, 1, buf, sizeof(buf))); + } else if (verbose) + printf(" channel UNDEF"); + + if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 && + (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose)) + printf(" bssid %s", ether_ntoa((struct ether_addr *)data)); + + if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) { + printf("\n\tstationname "); + print_string(data, len); + } + + spacer = ' '; /* force first break */ + LINE_BREAK(); + + list_regdomain(s, 0); + + wpa = 0; + if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) { + switch (val) { + case IEEE80211_AUTH_NONE: + LINE_CHECK("authmode NONE"); + break; + case IEEE80211_AUTH_OPEN: + LINE_CHECK("authmode OPEN"); + break; + case IEEE80211_AUTH_SHARED: + LINE_CHECK("authmode SHARED"); + break; + case IEEE80211_AUTH_8021X: + LINE_CHECK("authmode 802.1x"); + break; + case IEEE80211_AUTH_WPA: + if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0) + wpa = 1; /* default to WPA1 */ + switch (wpa) { + case 2: + LINE_CHECK("authmode WPA2/802.11i"); + break; + case 3: + LINE_CHECK("authmode WPA1+WPA2/802.11i"); + break; + default: + LINE_CHECK("authmode WPA"); + break; + } + break; + case IEEE80211_AUTH_AUTO: + LINE_CHECK("authmode AUTO"); + break; + default: + LINE_CHECK("authmode UNKNOWN (0x%x)", val); + break; + } + } + + if (wpa || verbose) { + if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) { + if (val) + LINE_CHECK("wps"); + else if (verbose) + LINE_CHECK("-wps"); + } + if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) { + if (val) + LINE_CHECK("tsn"); + else if (verbose) + LINE_CHECK("-tsn"); + } + if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) { + if (val) + LINE_CHECK("countermeasures"); + else if (verbose) + LINE_CHECK("-countermeasures"); + } +#if 0 + /* XXX not interesting with WPA done in user space */ + ireq.i_type = IEEE80211_IOC_KEYMGTALGS; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + } + + ireq.i_type = IEEE80211_IOC_MCASTCIPHER; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + LINE_CHECK("mcastcipher "); + printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN); + spacer = ' '; + } + + ireq.i_type = IEEE80211_IOC_UCASTCIPHER; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + LINE_CHECK("ucastcipher "); + printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN); + } + + if (wpa & 2) { + ireq.i_type = IEEE80211_IOC_RSNCAPS; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + LINE_CHECK("RSN caps 0x%x", ireq.i_val); + spacer = ' '; + } + } + + ireq.i_type = IEEE80211_IOC_UCASTCIPHERS; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + } +#endif + } + + if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 && + wepmode != IEEE80211_WEP_NOSUP) { + int firstkey; + + switch (wepmode) { + case IEEE80211_WEP_OFF: + LINE_CHECK("privacy OFF"); + break; + case IEEE80211_WEP_ON: + LINE_CHECK("privacy ON"); + break; + case IEEE80211_WEP_MIXED: + LINE_CHECK("privacy MIXED"); + break; + default: + LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode); + break; + } + + /* + * If we get here then we've got WEP support so we need + * to print WEP status. + */ + + if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) { + warn("WEP support, but no tx key!"); + goto end; + } + if (val != -1) + LINE_CHECK("deftxkey %d", val+1); + else if (wepmode != IEEE80211_WEP_OFF || verbose) + LINE_CHECK("deftxkey UNDEF"); + + if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) { + warn("WEP support, but no NUMWEPKEYS support!"); + goto end; + } + + firstkey = 1; + for (i = 0; i < num; i++) { + struct ieee80211req_key ik; + + memset(&ik, 0, sizeof(ik)); + ik.ik_keyix = i; + if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) { + warn("WEP support, but can get keys!"); + goto end; + } + if (ik.ik_keylen != 0) { + if (verbose) + LINE_BREAK(); + printkey(&ik); + firstkey = 0; + } + } +end: + ; + } + + if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 && + val != IEEE80211_POWERSAVE_NOSUP ) { + if (val != IEEE80211_POWERSAVE_OFF || verbose) { + switch (val) { + case IEEE80211_POWERSAVE_OFF: + LINE_CHECK("powersavemode OFF"); + break; + case IEEE80211_POWERSAVE_CAM: + LINE_CHECK("powersavemode CAM"); + break; + case IEEE80211_POWERSAVE_PSP: + LINE_CHECK("powersavemode PSP"); + break; + case IEEE80211_POWERSAVE_PSP_CAM: + LINE_CHECK("powersavemode PSP-CAM"); + break; + } + if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1) + LINE_CHECK("powersavesleep %d", val); + } + } + + if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) { + if (val & 1) + LINE_CHECK("txpower %d.5", val/2); + else + LINE_CHECK("txpower %d", val/2); + } + if (verbose) { + if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1) + LINE_CHECK("txpowmax %.1f", val/2.); + } + + if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) { + if (val) + LINE_CHECK("dotd"); + else if (verbose) + LINE_CHECK("-dotd"); + } + + if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) { + if (val != IEEE80211_RTS_MAX || verbose) + LINE_CHECK("rtsthreshold %d", val); + } + + if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) { + if (val != IEEE80211_FRAG_MAX || verbose) + LINE_CHECK("fragthreshold %d", val); + } + if (opmode == IEEE80211_M_STA || verbose) { + if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) { + if (val != IEEE80211_HWBMISS_MAX || verbose) + LINE_CHECK("bmiss %d", val); + } + } + + if (!verbose) { + gettxparams(s); + tp = &txparams.params[chan2mode(c)]; + printrate("ucastrate", tp->ucastrate, + IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE); + printrate("mcastrate", tp->mcastrate, 2*1, + IEEE80211_RATE_MCS|0); + printrate("mgmtrate", tp->mgmtrate, 2*1, + IEEE80211_RATE_MCS|0); + if (tp->maxretry != 6) /* XXX */ + LINE_CHECK("maxretry %d", tp->maxretry); + } else { + LINE_BREAK(); + list_txparams(s); + } + + bgscaninterval = -1; + (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval); + + if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) { + if (val != bgscaninterval || verbose) + LINE_CHECK("scanvalid %u", val); + } + + bgscan = 0; + if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) { + if (bgscan) + LINE_CHECK("bgscan"); + else if (verbose) + LINE_CHECK("-bgscan"); + } + if (bgscan || verbose) { + if (bgscaninterval != -1) + LINE_CHECK("bgscanintvl %u", bgscaninterval); + if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1) + LINE_CHECK("bgscanidle %u", val); + if (!verbose) { + getroam(s); + rp = &roamparams.params[chan2mode(c)]; + if (rp->rssi & 1) + LINE_CHECK("roam:rssi %u.5", rp->rssi/2); + else + LINE_CHECK("roam:rssi %u", rp->rssi/2); + LINE_CHECK("roam:rate %u", rp->rate/2); + } else { + LINE_BREAK(); + list_roam(s); + LINE_BREAK(); + } + } + + if (IEEE80211_IS_CHAN_ANYG(c) || verbose) { + if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) { + if (val) + LINE_CHECK("pureg"); + else if (verbose) + LINE_CHECK("-pureg"); + } + if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) { + switch (val) { + case IEEE80211_PROTMODE_OFF: + LINE_CHECK("protmode OFF"); + break; + case IEEE80211_PROTMODE_CTS: + LINE_CHECK("protmode CTS"); + break; + case IEEE80211_PROTMODE_RTSCTS: + LINE_CHECK("protmode RTSCTS"); + break; + default: + LINE_CHECK("protmode UNKNOWN (0x%x)", val); + break; + } + } + } + + if (IEEE80211_IS_CHAN_HT(c) || verbose) { + gethtconf(s); + switch (htconf & 3) { + case 0: + case 2: + LINE_CHECK("-ht"); + break; + case 1: + LINE_CHECK("ht20"); + break; + case 3: + if (verbose) + LINE_CHECK("ht"); + break; + } + if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) { + if (!val) + LINE_CHECK("-htcompat"); + else if (verbose) + LINE_CHECK("htcompat"); + } + if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) { + switch (val) { + case 0: + LINE_CHECK("-ampdu"); + break; + case 1: + LINE_CHECK("ampdutx -ampdurx"); + break; + case 2: + LINE_CHECK("-ampdutx ampdurx"); + break; + case 3: + if (verbose) + LINE_CHECK("ampdu"); + break; + } + } + if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) { + switch (val) { + case IEEE80211_HTCAP_MAXRXAMPDU_8K: + LINE_CHECK("ampdulimit 8k"); + break; + case IEEE80211_HTCAP_MAXRXAMPDU_16K: + LINE_CHECK("ampdulimit 16k"); + break; + case IEEE80211_HTCAP_MAXRXAMPDU_32K: + LINE_CHECK("ampdulimit 32k"); + break; + case IEEE80211_HTCAP_MAXRXAMPDU_64K: + LINE_CHECK("ampdulimit 64k"); + break; + } + } + if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) { + switch (val) { + case IEEE80211_HTCAP_MPDUDENSITY_NA: + if (verbose) + LINE_CHECK("ampdudensity NA"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_025: + LINE_CHECK("ampdudensity .25"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_05: + LINE_CHECK("ampdudensity .5"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_1: + LINE_CHECK("ampdudensity 1"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_2: + LINE_CHECK("ampdudensity 2"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_4: + LINE_CHECK("ampdudensity 4"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_8: + LINE_CHECK("ampdudensity 8"); + break; + case IEEE80211_HTCAP_MPDUDENSITY_16: + LINE_CHECK("ampdudensity 16"); + break; + } + } + if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) { + switch (val) { + case 0: + LINE_CHECK("-amsdu"); + break; + case 1: + LINE_CHECK("amsdutx -amsdurx"); + break; + case 2: + LINE_CHECK("-amsdutx amsdurx"); + break; + case 3: + if (verbose) + LINE_CHECK("amsdu"); + break; + } + } + /* XXX amsdu limit */ + if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) { + if (val) + LINE_CHECK("shortgi"); + else if (verbose) + LINE_CHECK("-shortgi"); + } + if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) { + if (val == IEEE80211_PROTMODE_OFF) + LINE_CHECK("htprotmode OFF"); + else if (val != IEEE80211_PROTMODE_RTSCTS) + LINE_CHECK("htprotmode UNKNOWN (0x%x)", val); + else if (verbose) + LINE_CHECK("htprotmode RTSCTS"); + } + if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) { + if (val) + LINE_CHECK("puren"); + else if (verbose) + LINE_CHECK("-puren"); + } + if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) { + if (val == IEEE80211_HTCAP_SMPS_DYNAMIC) + LINE_CHECK("smpsdyn"); + else if (val == IEEE80211_HTCAP_SMPS_ENA) + LINE_CHECK("smps"); + else if (verbose) + LINE_CHECK("-smps"); + } + if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) { + if (val) + LINE_CHECK("rifs"); + else if (verbose) + LINE_CHECK("-rifs"); + } + } + + if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) { + if (wme) + LINE_CHECK("wme"); + else if (verbose) + LINE_CHECK("-wme"); + } else + wme = 0; + + if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) { + if (val) + LINE_CHECK("burst"); + else if (verbose) + LINE_CHECK("-burst"); + } + + if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) { + if (val) + LINE_CHECK("ff"); + else if (verbose) + LINE_CHECK("-ff"); + } + if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) { + if (val) + LINE_CHECK("dturbo"); + else if (verbose) + LINE_CHECK("-dturbo"); + } + if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) { + if (val) + LINE_CHECK("dwds"); + else if (verbose) + LINE_CHECK("-dwds"); + } + + if (opmode == IEEE80211_M_HOSTAP) { + if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) { + if (val) + LINE_CHECK("hidessid"); + else if (verbose) + LINE_CHECK("-hidessid"); + } + if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) { + if (!val) + LINE_CHECK("-apbridge"); + else if (verbose) + LINE_CHECK("apbridge"); + } + if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1) + LINE_CHECK("dtimperiod %u", val); + + if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) { + if (!val) + LINE_CHECK("-doth"); + else if (verbose) + LINE_CHECK("doth"); + } + if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) { + if (!val) + LINE_CHECK("-dfs"); + else if (verbose) + LINE_CHECK("dfs"); + } + if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) { + if (!val) + LINE_CHECK("-inact"); + else if (verbose) + LINE_CHECK("inact"); + } + } else { + if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) { + if (val != IEEE80211_ROAMING_AUTO || verbose) { + switch (val) { + case IEEE80211_ROAMING_DEVICE: + LINE_CHECK("roaming DEVICE"); + break; + case IEEE80211_ROAMING_AUTO: + LINE_CHECK("roaming AUTO"); + break; + case IEEE80211_ROAMING_MANUAL: + LINE_CHECK("roaming MANUAL"); + break; + default: + LINE_CHECK("roaming UNKNOWN (0x%x)", + val); + break; + } + } + } + } + + if (opmode == IEEE80211_M_AHDEMO) { + if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1) + LINE_CHECK("tdmaslot %u", val); + if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1) + LINE_CHECK("tdmaslotcnt %u", val); + if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1) + LINE_CHECK("tdmaslotlen %u", val); + if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1) + LINE_CHECK("tdmabintval %u", val); + } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) { + /* XXX default define not visible */ + if (val != 100 || verbose) + LINE_CHECK("bintval %u", val); + } + + if (wme && verbose) { + LINE_BREAK(); + list_wme(s); + } + + if (opmode == IEEE80211_M_MBSS) { + if (get80211val(s, IEEE80211_IOC_MESH_TTL, &val) != -1) { + LINE_CHECK("meshttl %u", val); + } + if (get80211val(s, IEEE80211_IOC_MESH_AP, &val) != -1) { + if (val) + LINE_CHECK("meshpeering"); + else + LINE_CHECK("-meshpeering"); + } + if (get80211val(s, IEEE80211_IOC_MESH_FWRD, &val) != -1) { + if (val) + LINE_CHECK("meshforward"); + else + LINE_CHECK("-meshforward"); + } + if (get80211len(s, IEEE80211_IOC_MESH_PR_METRIC, data, 12, + &len) != -1) { + data[len] = '\0'; + LINE_CHECK("meshmetric %s", data); + } + if (get80211len(s, IEEE80211_IOC_MESH_PR_PATH, data, 12, + &len) != -1) { + data[len] = '\0'; + LINE_CHECK("meshpath %s", data); + } + if (get80211val(s, IEEE80211_IOC_HWMP_ROOTMODE, &val) != -1) { + switch (val) { + case IEEE80211_HWMP_ROOTMODE_DISABLED: + LINE_CHECK("hwmprootmode DISABLED"); + break; + case IEEE80211_HWMP_ROOTMODE_NORMAL: + LINE_CHECK("hwmprootmode NORMAL"); + break; + case IEEE80211_HWMP_ROOTMODE_PROACTIVE: + LINE_CHECK("hwmprootmode PROACTIVE"); + break; + case IEEE80211_HWMP_ROOTMODE_RANN: + LINE_CHECK("hwmprootmode RANN"); + break; + default: + LINE_CHECK("hwmprootmode UNKNOWN(%d)", val); + break; + } + } + if (get80211val(s, IEEE80211_IOC_HWMP_MAXHOPS, &val) != -1) { + LINE_CHECK("hwmpmaxhops %u", val); + } + } + + LINE_BREAK(); +} + +static int +get80211(int s, int type, void *data, int len) +{ + struct ieee80211req ireq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = type; + ireq.i_data = data; + ireq.i_len = len; + return ioctl(s, SIOCG80211, &ireq); +} + +static int +get80211len(int s, int type, void *data, int len, int *plen) +{ + struct ieee80211req ireq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = type; + ireq.i_len = len; + assert(ireq.i_len == len); /* NB: check for 16-bit truncation */ + ireq.i_data = data; + if (ioctl(s, SIOCG80211, &ireq) < 0) + return -1; + *plen = ireq.i_len; + return 0; +} + +static int +get80211val(int s, int type, int *val) +{ + struct ieee80211req ireq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = type; + if (ioctl(s, SIOCG80211, &ireq) < 0) + return -1; + *val = ireq.i_val; + return 0; +} + +static void +set80211(int s, int type, int val, int len, void *data) +{ + struct ieee80211req ireq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = type; + ireq.i_val = val; + ireq.i_len = len; + assert(ireq.i_len == len); /* NB: check for 16-bit truncation */ + ireq.i_data = data; + if (ioctl(s, SIOCS80211, &ireq) < 0) + err(1, "SIOCS80211"); +} + +static const char * +get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp) +{ + int len; + int hexstr; + u_int8_t *p; + + len = *lenp; + p = buf; + hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x'); + if (hexstr) + val += 2; + for (;;) { + if (*val == '\0') + break; + if (sep != NULL && strchr(sep, *val) != NULL) { + val++; + break; + } + if (hexstr) { + if (!isxdigit((u_char)val[0])) { + warnx("bad hexadecimal digits"); + return NULL; + } + if (!isxdigit((u_char)val[1])) { + warnx("odd count hexadecimal digits"); + return NULL; + } + } + if (p >= buf + len) { + if (hexstr) + warnx("hexadecimal digits too long"); + else + warnx("string too long"); + return NULL; + } + if (hexstr) { +#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10) + *p++ = (tohex((u_char)val[0]) << 4) | + tohex((u_char)val[1]); +#undef tohex + val += 2; + } else + *p++ = *val++; + } + len = p - buf; + /* The string "-" is treated as the empty string. */ + if (!hexstr && len == 1 && buf[0] == '-') { + len = 0; + memset(buf, 0, *lenp); + } else if (len < *lenp) + memset(p, 0, *lenp - len); + *lenp = len; + return val; +} + +static void +print_string(const u_int8_t *buf, int len) +{ + int i; + int hasspc; + + i = 0; + hasspc = 0; + for (; i < len; i++) { + if (!isprint(buf[i]) && buf[i] != '\0') + break; + if (isspace(buf[i])) + hasspc++; + } + if (i == len) { + if (hasspc || len == 0 || buf[0] == '\0') + printf("\"%.*s\"", len, buf); + else + printf("%.*s", len, buf); + } else { + printf("0x"); + for (i = 0; i < len; i++) + printf("%02x", buf[i]); + } +} + +/* + * Virtual AP cloning support. + */ +static struct ieee80211_clone_params params = { + .icp_opmode = IEEE80211_M_STA, /* default to station mode */ +}; + +static void +wlan_create(int s, struct ifreq *ifr) +{ + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + + if (params.icp_parent[0] == '\0') + errx(1, "must specify a parent device (wlandev) when creating " + "a wlan device"); + if (params.icp_opmode == IEEE80211_M_WDS && + memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0) + errx(1, "no bssid specified for WDS (use wlanbssid)"); + ifr->ifr_data = (caddr_t) ¶ms; + if (ioctl(s, SIOCIFCREATE2, ifr) < 0) + err(1, "SIOCIFCREATE2"); +} + +static +DECL_CMD_FUNC(set80211clone_wlandev, arg, d) +{ + strlcpy(params.icp_parent, arg, IFNAMSIZ); +} + +static +DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d) +{ + const struct ether_addr *ea; + + ea = ether_aton(arg); + if (ea == NULL) + errx(1, "%s: cannot parse bssid", arg); + memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN); +} + +static +DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d) +{ + const struct ether_addr *ea; + + ea = ether_aton(arg); + if (ea == NULL) + errx(1, "%s: cannot parse addres", arg); + memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN); + params.icp_flags |= IEEE80211_CLONE_MACADDR; +} + +static +DECL_CMD_FUNC(set80211clone_wlanmode, arg, d) +{ +#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) + if (iseq(arg, "sta")) + params.icp_opmode = IEEE80211_M_STA; + else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo")) + params.icp_opmode = IEEE80211_M_AHDEMO; + else if (iseq(arg, "ibss") || iseq(arg, "adhoc")) + params.icp_opmode = IEEE80211_M_IBSS; + else if (iseq(arg, "ap") || iseq(arg, "host")) + params.icp_opmode = IEEE80211_M_HOSTAP; + else if (iseq(arg, "wds")) + params.icp_opmode = IEEE80211_M_WDS; + else if (iseq(arg, "monitor")) + params.icp_opmode = IEEE80211_M_MONITOR; + else if (iseq(arg, "tdma")) { + params.icp_opmode = IEEE80211_M_AHDEMO; + params.icp_flags |= IEEE80211_CLONE_TDMA; + } else if (iseq(arg, "mesh") || iseq(arg, "mp")) /* mesh point */ + params.icp_opmode = IEEE80211_M_MBSS; + else + errx(1, "Don't know to create %s for %s", arg, name); +#undef iseq +} + +static void +set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp) +{ + /* NB: inverted sense */ + if (d) + params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS; + else + params.icp_flags |= IEEE80211_CLONE_NOBEACONS; +} + +static void +set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp) +{ + if (d) + params.icp_flags |= IEEE80211_CLONE_BSSID; + else + params.icp_flags &= ~IEEE80211_CLONE_BSSID; +} + +static void +set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp) +{ + if (d) + params.icp_flags |= IEEE80211_CLONE_WDSLEGACY; + else + params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY; +} + +static struct cmd ieee80211_cmds[] = { + DEF_CMD_ARG("ssid", set80211ssid), + DEF_CMD_ARG("nwid", set80211ssid), + DEF_CMD_ARG("meshid", set80211meshid), + DEF_CMD_ARG("stationname", set80211stationname), + DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */ + DEF_CMD_ARG("channel", set80211channel), + DEF_CMD_ARG("authmode", set80211authmode), + DEF_CMD_ARG("powersavemode", set80211powersavemode), + DEF_CMD("powersave", 1, set80211powersave), + DEF_CMD("-powersave", 0, set80211powersave), + DEF_CMD_ARG("powersavesleep", set80211powersavesleep), + DEF_CMD_ARG("wepmode", set80211wepmode), + DEF_CMD("wep", 1, set80211wep), + DEF_CMD("-wep", 0, set80211wep), + DEF_CMD_ARG("deftxkey", set80211weptxkey), + DEF_CMD_ARG("weptxkey", set80211weptxkey), + DEF_CMD_ARG("wepkey", set80211wepkey), + DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */ + DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */ + DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold), + DEF_CMD_ARG("protmode", set80211protmode), + DEF_CMD_ARG("txpower", set80211txpower), + DEF_CMD_ARG("roaming", set80211roaming), + DEF_CMD("wme", 1, set80211wme), + DEF_CMD("-wme", 0, set80211wme), + DEF_CMD("wmm", 1, set80211wme), + DEF_CMD("-wmm", 0, set80211wme), + DEF_CMD("hidessid", 1, set80211hidessid), + DEF_CMD("-hidessid", 0, set80211hidessid), + DEF_CMD("apbridge", 1, set80211apbridge), + DEF_CMD("-apbridge", 0, set80211apbridge), + DEF_CMD_ARG("chanlist", set80211chanlist), + DEF_CMD_ARG("bssid", set80211bssid), + DEF_CMD_ARG("ap", set80211bssid), + DEF_CMD("scan", 0, set80211scan), + DEF_CMD_ARG("list", set80211list), + DEF_CMD_ARG2("cwmin", set80211cwmin), + DEF_CMD_ARG2("cwmax", set80211cwmax), + DEF_CMD_ARG2("aifs", set80211aifs), + DEF_CMD_ARG2("txoplimit", set80211txoplimit), + DEF_CMD_ARG("acm", set80211acm), + DEF_CMD_ARG("-acm", set80211noacm), + DEF_CMD_ARG("ack", set80211ackpolicy), + DEF_CMD_ARG("-ack", set80211noackpolicy), + DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin), + DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax), + DEF_CMD_ARG2("bss:aifs", set80211bssaifs), + DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit), + DEF_CMD_ARG("dtimperiod", set80211dtimperiod), + DEF_CMD_ARG("bintval", set80211bintval), + DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd), + DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd), + DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd), + DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd), + DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd), + DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd), + DEF_CMD_ARG("mac:add", set80211addmac), + DEF_CMD_ARG("mac:del", set80211delmac), + DEF_CMD_ARG("mac:kick", set80211kickmac), + DEF_CMD("pureg", 1, set80211pureg), + DEF_CMD("-pureg", 0, set80211pureg), + DEF_CMD("ff", 1, set80211fastframes), + DEF_CMD("-ff", 0, set80211fastframes), + DEF_CMD("dturbo", 1, set80211dturbo), + DEF_CMD("-dturbo", 0, set80211dturbo), + DEF_CMD("bgscan", 1, set80211bgscan), + DEF_CMD("-bgscan", 0, set80211bgscan), + DEF_CMD_ARG("bgscanidle", set80211bgscanidle), + DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl), + DEF_CMD_ARG("scanvalid", set80211scanvalid), + DEF_CMD_ARG("roam:rssi", set80211roamrssi), + DEF_CMD_ARG("roam:rate", set80211roamrate), + DEF_CMD_ARG("mcastrate", set80211mcastrate), + DEF_CMD_ARG("ucastrate", set80211ucastrate), + DEF_CMD_ARG("mgtrate", set80211mgtrate), + DEF_CMD_ARG("mgmtrate", set80211mgtrate), + DEF_CMD_ARG("maxretry", set80211maxretry), + DEF_CMD_ARG("fragthreshold", set80211fragthreshold), + DEF_CMD("burst", 1, set80211burst), + DEF_CMD("-burst", 0, set80211burst), + DEF_CMD_ARG("bmiss", set80211bmissthreshold), + DEF_CMD_ARG("bmissthreshold", set80211bmissthreshold), + DEF_CMD("shortgi", 1, set80211shortgi), + DEF_CMD("-shortgi", 0, set80211shortgi), + DEF_CMD("ampdurx", 2, set80211ampdu), + DEF_CMD("-ampdurx", -2, set80211ampdu), + DEF_CMD("ampdutx", 1, set80211ampdu), + DEF_CMD("-ampdutx", -1, set80211ampdu), + DEF_CMD("ampdu", 3, set80211ampdu), /* NB: tx+rx */ + DEF_CMD("-ampdu", -3, set80211ampdu), + DEF_CMD_ARG("ampdulimit", set80211ampdulimit), + DEF_CMD_ARG("ampdudensity", set80211ampdudensity), + DEF_CMD("amsdurx", 2, set80211amsdu), + DEF_CMD("-amsdurx", -2, set80211amsdu), + DEF_CMD("amsdutx", 1, set80211amsdu), + DEF_CMD("-amsdutx", -1, set80211amsdu), + DEF_CMD("amsdu", 3, set80211amsdu), /* NB: tx+rx */ + DEF_CMD("-amsdu", -3, set80211amsdu), + DEF_CMD_ARG("amsdulimit", set80211amsdulimit), + DEF_CMD("puren", 1, set80211puren), + DEF_CMD("-puren", 0, set80211puren), + DEF_CMD("doth", 1, set80211doth), + DEF_CMD("-doth", 0, set80211doth), + DEF_CMD("dfs", 1, set80211dfs), + DEF_CMD("-dfs", 0, set80211dfs), + DEF_CMD("htcompat", 1, set80211htcompat), + DEF_CMD("-htcompat", 0, set80211htcompat), + DEF_CMD("dwds", 1, set80211dwds), + DEF_CMD("-dwds", 0, set80211dwds), + DEF_CMD("inact", 1, set80211inact), + DEF_CMD("-inact", 0, set80211inact), + DEF_CMD("tsn", 1, set80211tsn), + DEF_CMD("-tsn", 0, set80211tsn), + DEF_CMD_ARG("regdomain", set80211regdomain), + DEF_CMD_ARG("country", set80211country), + DEF_CMD("indoor", 'I', set80211location), + DEF_CMD("-indoor", 'O', set80211location), + DEF_CMD("outdoor", 'O', set80211location), + DEF_CMD("-outdoor", 'I', set80211location), + DEF_CMD("anywhere", ' ', set80211location), + DEF_CMD("ecm", 1, set80211ecm), + DEF_CMD("-ecm", 0, set80211ecm), + DEF_CMD("dotd", 1, set80211dotd), + DEF_CMD("-dotd", 0, set80211dotd), + DEF_CMD_ARG("htprotmode", set80211htprotmode), + DEF_CMD("ht20", 1, set80211htconf), + DEF_CMD("-ht20", 0, set80211htconf), + DEF_CMD("ht40", 3, set80211htconf), /* NB: 20+40 */ + DEF_CMD("-ht40", 0, set80211htconf), + DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */ + DEF_CMD("-ht", 0, set80211htconf), + DEF_CMD("rifs", 1, set80211rifs), + DEF_CMD("-rifs", 0, set80211rifs), + DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps), + DEF_CMD("smpsdyn", IEEE80211_HTCAP_SMPS_DYNAMIC, set80211smps), + DEF_CMD("-smps", IEEE80211_HTCAP_SMPS_OFF, set80211smps), + /* XXX for testing */ + DEF_CMD_ARG("chanswitch", set80211chanswitch), + + DEF_CMD_ARG("tdmaslot", set80211tdmaslot), + DEF_CMD_ARG("tdmaslotcnt", set80211tdmaslotcnt), + DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen), + DEF_CMD_ARG("tdmabintval", set80211tdmabintval), + + DEF_CMD_ARG("meshttl", set80211meshttl), + DEF_CMD("meshforward", 1, set80211meshforward), + DEF_CMD("-meshforward", 0, set80211meshforward), + DEF_CMD("meshpeering", 1, set80211meshpeering), + DEF_CMD("-meshpeering", 0, set80211meshpeering), + DEF_CMD_ARG("meshmetric", set80211meshmetric), + DEF_CMD_ARG("meshpath", set80211meshpath), + DEF_CMD("meshrt:flush", IEEE80211_MESH_RTCMD_FLUSH, set80211meshrtcmd), + DEF_CMD_ARG("meshrt:add", set80211addmeshrt), + DEF_CMD_ARG("meshrt:del", set80211delmeshrt), + DEF_CMD_ARG("hwmprootmode", set80211hwmprootmode), + DEF_CMD_ARG("hwmpmaxhops", set80211hwmpmaxhops), + + /* vap cloning support */ + DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr), + DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid), + DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev), + DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode), + DEF_CLONE_CMD("beacons", 1, set80211clone_beacons), + DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons), + DEF_CLONE_CMD("bssid", 1, set80211clone_bssid), + DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid), + DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy), + DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy), +}; +static struct afswtch af_ieee80211 = { + .af_name = "af_ieee80211", + .af_af = AF_UNSPEC, + .af_other_status = ieee80211_status, +}; + +static __constructor void +ieee80211_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(ieee80211_cmds); i++) + cmd_register(&ieee80211_cmds[i]); + af_register(&af_ieee80211); + clone_setdefcallback("wlan", wlan_create); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/iflagg.c b/freebsd-userspace/commands/sbin/ifconfig/iflagg.c new file mode 100644 index 00000000..4204d929 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/iflagg.c @@ -0,0 +1,198 @@ +/*- + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/net/if_lagg.h> +#else +#include <net/if_lagg.h> +#endif +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +char lacpbuf[120]; /* LACP peer '[(a,a,a),(p,p,p)]' */ + +static void +setlaggport(const char *val, int d, int s, const struct afswtch *afp) +{ + struct lagg_reqport rp; + + bzero(&rp, sizeof(rp)); + strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); + strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); + + if (ioctl(s, SIOCSLAGGPORT, &rp)) + err(1, "SIOCSLAGGPORT"); +} + +static void +unsetlaggport(const char *val, int d, int s, const struct afswtch *afp) +{ + struct lagg_reqport rp; + + bzero(&rp, sizeof(rp)); + strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); + strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); + + if (ioctl(s, SIOCSLAGGDELPORT, &rp)) + err(1, "SIOCSLAGGDELPORT"); +} + +static void +setlaggproto(const char *val, int d, int s, const struct afswtch *afp) +{ + struct lagg_protos lpr[] = LAGG_PROTOS; + struct lagg_reqall ra; + int i; + + bzero(&ra, sizeof(ra)); + ra.ra_proto = LAGG_PROTO_MAX; + + for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) { + if (strcmp(val, lpr[i].lpr_name) == 0) { + ra.ra_proto = lpr[i].lpr_proto; + break; + } + } + if (ra.ra_proto == LAGG_PROTO_MAX) + errx(1, "Invalid aggregation protocol: %s", val); + + strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname)); + if (ioctl(s, SIOCSLAGG, &ra) != 0) + err(1, "SIOCSLAGG"); +} + +static char * +lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) +{ + snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", + (int)mac[0], (int)mac[1], (int)mac[2], (int)mac[3], + (int)mac[4], (int)mac[5]); + + return (buf); +} + +static char * +lacp_format_peer(struct lacp_opreq *req, const char *sep) +{ + char macbuf1[20]; + char macbuf2[20]; + + snprintf(lacpbuf, sizeof(lacpbuf), + "[(%04X,%s,%04X,%04X,%04X),%s(%04X,%s,%04X,%04X,%04X)]", + req->actor_prio, + lacp_format_mac(req->actor_mac, macbuf1, sizeof(macbuf1)), + req->actor_key, req->actor_portprio, req->actor_portno, sep, + req->partner_prio, + lacp_format_mac(req->partner_mac, macbuf2, sizeof(macbuf2)), + req->partner_key, req->partner_portprio, req->partner_portno); + + return(lacpbuf); +} + +static void +lagg_status(int s) +{ + struct lagg_protos lpr[] = LAGG_PROTOS; + struct lagg_reqport rp, rpbuf[LAGG_MAX_PORTS]; + struct lagg_reqall ra; + struct lacp_opreq *lp; + const char *proto = "<unknown>"; + int i, isport = 0; + + bzero(&rp, sizeof(rp)); + bzero(&ra, sizeof(ra)); + + strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); + strlcpy(rp.rp_portname, name, sizeof(rp.rp_portname)); + + if (ioctl(s, SIOCGLAGGPORT, &rp) == 0) + isport = 1; + + strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname)); + ra.ra_size = sizeof(rpbuf); + ra.ra_port = rpbuf; + + if (ioctl(s, SIOCGLAGG, &ra) == 0) { + lp = (struct lacp_opreq *)&ra.ra_lacpreq; + + for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) { + if (ra.ra_proto == lpr[i].lpr_proto) { + proto = lpr[i].lpr_name; + break; + } + } + + printf("\tlaggproto %s", proto); + if (isport) + printf(" laggdev %s", rp.rp_ifname); + putchar('\n'); + if (verbose && ra.ra_proto == LAGG_PROTO_LACP) + printf("\tlag id: %s\n", + lacp_format_peer(lp, "\n\t\t ")); + + for (i = 0; i < ra.ra_ports; i++) { + lp = (struct lacp_opreq *)&rpbuf[i].rp_lacpreq; + printf("\tlaggport: %s ", rpbuf[i].rp_portname); + printb("flags", rpbuf[i].rp_flags, LAGG_PORT_BITS); + if (verbose && ra.ra_proto == LAGG_PROTO_LACP) + printf(" state=%X", lp->actor_state); + putchar('\n'); + if (verbose && ra.ra_proto == LAGG_PROTO_LACP) + printf("\t\t%s\n", + lacp_format_peer(lp, "\n\t\t ")); + } + + if (0 /* XXX */) { + printf("\tsupported aggregation protocols:\n"); + for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) + printf("\t\tlaggproto %s\n", lpr[i].lpr_name); + } + } +} + +static struct cmd lagg_cmds[] = { + DEF_CMD_ARG("laggport", setlaggport), + DEF_CMD_ARG("-laggport", unsetlaggport), + DEF_CMD_ARG("laggproto", setlaggproto), +}; +static struct afswtch af_lagg = { + .af_name = "af_lagg", + .af_af = AF_UNSPEC, + .af_other_status = lagg_status, +}; + +static __constructor void +lagg_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(lagg_cmds); i++) + cmd_register(&lagg_cmds[i]); + af_register(&af_lagg); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifmac.c b/freebsd-userspace/commands/sbin/ifconfig/ifmac.c new file mode 100644 index 00000000..31d40b13 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifmac.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by NAI Labs, the + * Security Research Division of Network Associates, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * 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. 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#ifdef __rtems__ +#include <freebsd/sys/mac.h> +#else +#include <sys/mac.h> +#endif +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <net/if.h> +#include <net/route.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ifconfig.h" + +static void +maclabel_status(int s) +{ + struct ifreq ifr; + mac_t label; + char *label_text; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (mac_prepare_ifnet_label(&label) == -1) + return; + ifr.ifr_ifru.ifru_data = (void *)label; + if (ioctl(s, SIOCGIFMAC, &ifr) == -1) + goto mac_free; + + + if (mac_to_text(label, &label_text) == -1) + goto mac_free; + + if (strlen(label_text) != 0) + printf("\tmaclabel %s\n", label_text); + free(label_text); + +mac_free: + mac_free(label); +} + +static void +setifmaclabel(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct ifreq ifr; + mac_t label; + int error; + + if (mac_from_text(&label, val) == -1) { + perror(val); + return; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_data = (void *)label; + + error = ioctl(s, SIOCSIFMAC, &ifr); + mac_free(label); + if (error == -1) + perror("setifmac"); +} + +static struct cmd mac_cmds[] = { + DEF_CMD_ARG("maclabel", setifmaclabel), +}; +static struct afswtch af_mac = { + .af_name = "af_maclabel", + .af_af = AF_UNSPEC, + .af_other_status = maclabel_status, +}; + +static __constructor void +mac_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(mac_cmds); i++) + cmd_register(&mac_cmds[i]); + af_register(&af_mac); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifmedia.c b/freebsd-userspace/commands/sbin/ifconfig/ifmedia.c new file mode 100644 index 00000000..ebca67de --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifmedia.c @@ -0,0 +1,844 @@ +/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1997 Jason R. Thorpe. + * 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 NetBSD Project + * by Jason R. Thorpe. + * 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. + */ + +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +#ifdef __rtems__ +#define _KERNEL +#include <freebsd/net/if_media.h> +#undef _KERNEL +#else +#include <net/if_media.h> +#endif + +#include <net/route.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +static void domediaopt(const char *, int, int); +static int get_media_subtype(int, const char *); +static int get_media_mode(int, const char *); +static int get_media_options(int, const char *); +static int lookup_media_word(struct ifmedia_description *, const char *); +static void print_media_word(int, int); +static void print_media_word_ifconfig(int); + +static struct ifmedia_description *get_toptype_desc(int); +static struct ifmedia_type_to_subtype *get_toptype_ttos(int); +static struct ifmedia_description *get_subtype_desc(int, + struct ifmedia_type_to_subtype *ttos); + +#define IFM_OPMODE(x) \ + ((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP | \ + IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR | \ + IFM_IEEE80211_MBSS)) +#define IFM_IEEE80211_STA 0 + +static void +media_status(int s) +{ + struct ifmediareq ifmr; + int *media_list, i; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFMEDIA. + */ + return; + } + + if (ifmr.ifm_count == 0) { + warnx("%s: no media types?", name); + return; + } + + media_list = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (media_list == NULL) + err(1, "malloc"); + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + + printf("\tmedia: "); + print_media_word(ifmr.ifm_current, 1); + if (ifmr.ifm_active != ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(ifmr.ifm_active, 0); + putchar(')'); + } + + putchar('\n'); + + if (ifmr.ifm_status & IFM_AVALID) { + printf("\tstatus: "); + switch (IFM_TYPE(ifmr.ifm_active)) { + case IFM_ETHER: + case IFM_ATM: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + break; + + case IFM_FDDI: + case IFM_TOKEN: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("inserted"); + else + printf("no ring"); + break; + + case IFM_IEEE80211: + if (ifmr.ifm_status & IFM_ACTIVE) { + /* NB: only sta mode associates */ + if (IFM_OPMODE(ifmr.ifm_active) == IFM_IEEE80211_STA) + printf("associated"); + else + printf("running"); + } else + printf("no carrier"); + break; + } + putchar('\n'); + } + + if (ifmr.ifm_count > 0 && supmedia) { + printf("\tsupported media:\n"); + for (i = 0; i < ifmr.ifm_count; i++) { + printf("\t\t"); + print_media_word_ifconfig(media_list[i]); + putchar('\n'); + } + } + + free(media_list); +} + +struct ifmediareq * +ifmedia_getstate(int s) +{ + static struct ifmediareq *ifmr = NULL; + int *mwords; + + if (ifmr == NULL) { + ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq)); + if (ifmr == NULL) + err(1, "malloc"); + + (void) memset(ifmr, 0, sizeof(struct ifmediareq)); + (void) strncpy(ifmr->ifm_name, name, + sizeof(ifmr->ifm_name)); + + ifmr->ifm_count = 0; + ifmr->ifm_ulist = NULL; + + /* + * We must go through the motions of reading all + * supported media because we need to know both + * the current media type and the top-level type. + */ + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) { + err(1, "SIOCGIFMEDIA"); + } + + if (ifmr->ifm_count == 0) + errx(1, "%s: no media types?", name); + + mwords = (int *)malloc(ifmr->ifm_count * sizeof(int)); + if (mwords == NULL) + err(1, "malloc"); + + ifmr->ifm_ulist = mwords; + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + } + + return ifmr; +} + +static void +setifmediacallback(int s, void *arg) +{ + struct ifmediareq *ifmr = (struct ifmediareq *)arg; + static int did_it = 0; + + if (!did_it) { + ifr.ifr_media = ifmr->ifm_current; + if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, "SIOCSIFMEDIA (media)"); + free(ifmr->ifm_ulist); + free(ifmr); + did_it = 1; + } +} + +static void +setmedia(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int subtype; + + ifmr = ifmedia_getstate(s); + + /* + * We are primarily concerned with the top-level type. + * However, "current" may be only IFM_NONE, so we just look + * for the top-level type in the first "supported type" + * entry. + * + * (I'm assuming that all supported media types for a given + * interface will be the same top-level type..) + */ + subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~(IFM_NMASK|IFM_TMASK)) | + IFM_TYPE(ifmr->ifm_ulist[0]) | subtype; + + if ((ifr.ifr_media & IFM_TMASK) == 0) { + ifr.ifr_media &= ~(IFM_GMASK | IFM_OMASK); + } + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 0, s); +} + +static void +unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 1, s); +} + +static void +domediaopt(const char *val, int clear, int s) +{ + struct ifmediareq *ifmr; + int options; + + ifmr = ifmedia_getstate(s); + + options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = ifmr->ifm_current; + if (clear) + ifr.ifr_media &= ~options; + else { + if (options & IFM_HDX) { + ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + ifr.ifr_media |= options; + } + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediainst(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int inst; + + ifmr = ifmedia_getstate(s); + + inst = atoi(val); + if (inst < 0 || inst > (int)IFM_INST_MAX) + errx(1, "invalid media instance: %s", val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediamode(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int mode; + + ifmr = ifmedia_getstate(s); + + mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +/********************************************************************** + * A good chunk of this is duplicated from sys/net/ifmedia.c + **********************************************************************/ + +static struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_aliases[] = + IFM_SUBTYPE_ETHERNET_ALIASES; + +static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_aliases[] = + IFM_SUBTYPE_TOKENRING_ALIASES; + +static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_aliases[] = + IFM_SUBTYPE_FDDI_ALIASES; + +static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_aliases[] = + IFM_SUBTYPE_IEEE80211_ALIASES; + +static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] = + IFM_SUBTYPE_IEEE80211_MODE_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_atm_aliases[] = + IFM_SUBTYPE_ATM_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_aliases[] = + IFM_SUBTYPE_SHARED_ALIASES; + +static struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +struct ifmedia_type_to_subtype { + struct { + struct ifmedia_description *desc; + int alias; + } subtypes[5]; + struct { + struct ifmedia_description *desc; + int alias; + } options[3]; + struct { + struct ifmedia_description *desc; + int alias; + } modes[3]; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ethernet_descriptions[0], 0 }, + { &ifm_subtype_ethernet_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ethernet_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_tokenring_descriptions[0], 0 }, + { &ifm_subtype_tokenring_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_tokenring_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_fddi_descriptions[0], 0 }, + { &ifm_subtype_fddi_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_fddi_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_subtype_ieee80211_mode_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_mode_aliases[0], 0 }, + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_atm_descriptions[0], 0 }, + { &ifm_subtype_atm_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_atm_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +}; + +static int +get_media_subtype(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->subtypes[i].desc, val); + if (rval != -1) + return (rval); + } + errx(1, "unknown media subtype: %s", val); + /*NOTREACHED*/ +} + +static int +get_media_mode(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media mode 0x%x", type); + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->modes[i].desc, val); + if (rval != -1) + return (rval); + } + return -1; +} + +static int +get_media_options(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + char *optlist, *optptr; + int option = 0, i, rval = 0; + + /* We muck with the string, so copy it. */ + optlist = strdup(val); + if (optlist == NULL) + err(1, "strdup"); + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + /* + * Look up the options in the user-provided comma-separated + * list. + */ + optptr = optlist; + for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) { + for (i = 0; ttos->options[i].desc != NULL; i++) { + option = lookup_media_word(ttos->options[i].desc, optptr); + if (option != -1) + break; + } + if (option == 0) + errx(1, "unknown option: %s", optptr); + rval |= option; + } + + free(optlist); + return (rval); +} + +static int +lookup_media_word(struct ifmedia_description *desc, const char *val) +{ + + for (; desc->ifmt_string != NULL; desc++) + if (strcasecmp(desc->ifmt_string, val) == 0) + return (desc->ifmt_word); + + return (-1); +} + +static struct ifmedia_description *get_toptype_desc(int ifmw) +{ + struct ifmedia_description *desc; + + for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return desc; +} + +static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return ttos; +} + +static struct ifmedia_description *get_subtype_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + if (ttos->subtypes[i].alias) + continue; + for (desc = ttos->subtypes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +static struct ifmedia_description *get_mode_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + if (ttos->modes[i].alias) + continue; + for (desc = ttos->modes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_MODE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +static void +print_media_word(int ifmw, int print_toptype) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } else if (print_toptype) { + printf("%s", desc->ifmt_string); + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + if (print_toptype) + putchar(' '); + + printf("%s", desc->ifmt_string); + + if (print_toptype) { + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string)) + printf(" mode %s", desc->ifmt_string); + } + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + printf("%s", seen_option ? ">" : ""); + + if (print_toptype && IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +static void +print_media_word_ifconfig(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + printf("media %s", desc->ifmt_string); + + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL) + printf(" mode %s", desc->ifmt_string); + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" mediaopt "); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + + if (IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +/********************************************************************** + * ...until here. + **********************************************************************/ + +static struct cmd media_cmds[] = { + DEF_CMD_ARG("media", setmedia), + DEF_CMD_ARG("mode", setmediamode), + DEF_CMD_ARG("mediaopt", setmediaopt), + DEF_CMD_ARG("-mediaopt",unsetmediaopt), + DEF_CMD_ARG("inst", setmediainst), + DEF_CMD_ARG("instance", setmediainst), +}; +static struct afswtch af_media = { + .af_name = "af_media", + .af_af = AF_UNSPEC, + .af_other_status = media_status, +}; + +static __constructor void +ifmedia_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(media_cmds); i++) + cmd_register(&media_cmds[i]); + af_register(&af_media); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifpfsync.c b/freebsd-userspace/commands/sbin/ifconfig/ifpfsync.c new file mode 100644 index 00000000..886a75e6 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifpfsync.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2003 Ryan McBride. All rights reserved. + * Copyright (c) 2004 Max Laier. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> +#ifdef __rtems__ +#include <freebsd/net/pfvar.h> +#include <freebsd/net/if_pfsync.h> +#else +#include <net/pfvar.h> +#include <net/if_pfsync.h> +#endif +#include <net/route.h> +#include <arpa/inet.h> + +#include <err.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +void setpfsync_syncdev(const char *, int, int, const struct afswtch *); +void unsetpfsync_syncdev(const char *, int, int, const struct afswtch *); +void setpfsync_syncpeer(const char *, int, int, const struct afswtch *); +void unsetpfsync_syncpeer(const char *, int, int, const struct afswtch *); +void setpfsync_syncpeer(const char *, int, int, const struct afswtch *); +void setpfsync_maxupd(const char *, int, int, const struct afswtch *); +void pfsync_status(int); + +void +setpfsync_syncdev(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct pfsyncreq preq; + + bzero((char *)&preq, sizeof(struct pfsyncreq)); + ifr.ifr_data = (caddr_t)&preq; + + if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCGETPFSYNC"); + + strlcpy(preq.pfsyncr_syncdev, val, sizeof(preq.pfsyncr_syncdev)); + + if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCSETPFSYNC"); +} + +/* ARGSUSED */ +void +unsetpfsync_syncdev(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct pfsyncreq preq; + + bzero((char *)&preq, sizeof(struct pfsyncreq)); + ifr.ifr_data = (caddr_t)&preq; + + if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCGETPFSYNC"); + + bzero((char *)&preq.pfsyncr_syncdev, sizeof(preq.pfsyncr_syncdev)); + + if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCSETPFSYNC"); +} + +/* ARGSUSED */ +void +setpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct pfsyncreq preq; + struct addrinfo hints, *peerres; + int ecode; + + bzero((char *)&preq, sizeof(struct pfsyncreq)); + ifr.ifr_data = (caddr_t)&preq; + + if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCGETPFSYNC"); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + + if ((ecode = getaddrinfo(val, NULL, &hints, &peerres)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(ecode)); + + if (peerres->ai_addr->sa_family != AF_INET) + errx(1, "only IPv4 addresses supported for the syncpeer"); + + preq.pfsyncr_syncpeer.s_addr = ((struct sockaddr_in *) + peerres->ai_addr)->sin_addr.s_addr; + + if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCSETPFSYNC"); +} + +/* ARGSUSED */ +void +unsetpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct pfsyncreq preq; + + bzero((char *)&preq, sizeof(struct pfsyncreq)); + ifr.ifr_data = (caddr_t)&preq; + + if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCGETPFSYNC"); + + preq.pfsyncr_syncpeer.s_addr = 0; + + if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCSETPFSYNC"); +} + +/* ARGSUSED */ +void +setpfsync_maxupd(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct pfsyncreq preq; + int maxupdates; + + maxupdates = atoi(val); + if ((maxupdates < 0) || (maxupdates > 255)) + errx(1, "maxupd %s: out of range", val); + + memset((char *)&preq, 0, sizeof(struct pfsyncreq)); + ifr.ifr_data = (caddr_t)&preq; + + if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCGETPFSYNC"); + + preq.pfsyncr_maxupdates = maxupdates; + + if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) + err(1, "SIOCSETPFSYNC"); +} + +void +pfsync_status(int s) +{ + struct pfsyncreq preq; + + bzero((char *)&preq, sizeof(struct pfsyncreq)); + ifr.ifr_data = (caddr_t)&preq; + + if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) + return; + + if (preq.pfsyncr_syncdev[0] != '\0' || + preq.pfsyncr_syncpeer.s_addr != INADDR_PFSYNC_GROUP) + printf("\t"); + + if (preq.pfsyncr_syncdev[0] != '\0') + printf("pfsync: syncdev: %s ", preq.pfsyncr_syncdev); + if (preq.pfsyncr_syncpeer.s_addr != INADDR_PFSYNC_GROUP) + printf("syncpeer: %s ", inet_ntoa(preq.pfsyncr_syncpeer)); + + if (preq.pfsyncr_syncdev[0] != '\0' || + preq.pfsyncr_syncpeer.s_addr != INADDR_PFSYNC_GROUP) + printf("maxupd: %d\n", preq.pfsyncr_maxupdates); +} + +static struct cmd pfsync_cmds[] = { + DEF_CMD_ARG("syncdev", setpfsync_syncdev), + DEF_CMD("-syncdev", 1, unsetpfsync_syncdev), + DEF_CMD_ARG("syncif", setpfsync_syncdev), + DEF_CMD("-syncif", 1, unsetpfsync_syncdev), + DEF_CMD_ARG("syncpeer", setpfsync_syncpeer), + DEF_CMD("-syncpeer", 1, unsetpfsync_syncpeer), + DEF_CMD_ARG("maxupd", setpfsync_maxupd) +}; +static struct afswtch af_pfsync = { + .af_name = "af_pfsync", + .af_af = AF_UNSPEC, + .af_other_status = pfsync_status, +}; + +static __constructor void +pfsync_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(pfsync_cmds); i++) + cmd_register(&pfsync_cmds[i]); + af_register(&af_pfsync); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/ifvlan.c b/freebsd-userspace/commands/sbin/ifconfig/ifvlan.c new file mode 100644 index 00000000..8ec3766f --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/ifvlan.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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 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 <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#ifdef __rtems__ +#include <freebsd/net/if_var.h> +#include <freebsd/net/if_vlan_var.h> +#else +#include <net/if_var.h> +#include <net/if_vlan_var.h> +#endif +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#define NOTAG ((u_short) -1) + +static struct vlanreq params = { + .vlr_tag = NOTAG, +}; + +static int +getvlan(int s, struct ifreq *ifr, struct vlanreq *vreq) +{ + bzero((char *)vreq, sizeof(*vreq)); + ifr->ifr_data = (caddr_t)vreq; + + return ioctl(s, SIOCGETVLAN, (caddr_t)ifr); +} + +static void +vlan_status(int s) +{ + struct vlanreq vreq; + + if (getvlan(s, &ifr, &vreq) != -1) + printf("\tvlan: %d parent interface: %s\n", + vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ? + "<none>" : vreq.vlr_parent); +} + +static void +vlan_create(int s, struct ifreq *ifr) +{ + 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; + } + if (ioctl(s, SIOCIFCREATE2, ifr) < 0) + err(1, "SIOCIFCREATE2"); +} + +static void +vlan_cb(int s, void *arg) +{ + 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 +DECL_CMD_FUNC(setvlantag, val, d) +{ + struct vlanreq 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 (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); +} + +static +DECL_CMD_FUNC(setvlandev, val, d) +{ + struct vlanreq vreq; + + strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); + + if (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); +} + +static +DECL_CMD_FUNC(unsetvlandev, val, d) +{ + struct vlanreq vreq; + + bzero((char *)&vreq, sizeof(struct vlanreq)); + ifr.ifr_data = (caddr_t)&vreq; + + if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1) + err(1, "SIOCGETVLAN"); + + bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); + vreq.vlr_tag = 0; + + if (ioctl(s, 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), + /* NB: non-clone cmds */ + DEF_CMD_ARG("vlan", setvlantag), + DEF_CMD_ARG("vlandev", setvlandev), + /* 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("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), + DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap), + DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap), + DEF_CMD("-vlanhwfilter", -IFCAP_VLAN_HWFILTER, setifcap), + DEF_CMD("-vlanhwtso", -IFCAP_VLAN_HWTSO, setifcap), + DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap), +}; +static struct afswtch af_vlan = { + .af_name = "af_vlan", + .af_af = AF_UNSPEC, + .af_other_status = vlan_status, +}; + +static __constructor void +vlan_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(vlan_cmds); i++) + cmd_register(&vlan_cmds[i]); + af_register(&af_vlan); + callback_register(vlan_cb, NULL); + clone_setdefcallback("vlan", vlan_create); +#undef N +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/regdomain.c b/freebsd-userspace/commands/sbin/ifconfig/regdomain.c new file mode 100644 index 00000000..41402895 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/regdomain.c @@ -0,0 +1,705 @@ +/*- + * Copyright (c) 2008 Sam Leffler, Errno Consulting + * 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. + * + * 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. + */ +#ifndef lint +static const char rcsid[] = "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/mman.h> +#include <sys/sbuf.h> +#include <sys/stat.h> + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <err.h> +#include <unistd.h> + +#include <bsdxml.h> + +#include "regdomain.h" + +#include <net80211/_ieee80211.h> + +#define MAXLEVEL 20 + +struct mystate { + XML_Parser parser; + struct regdata *rdp; + struct regdomain *rd; /* current domain */ + struct netband *netband; /* current netband */ + struct freqband *freqband; /* current freqband */ + struct country *country; /* current country */ + netband_head *curband; /* current netband list */ + int level; + struct sbuf *sbuf[MAXLEVEL]; + int nident; +}; + +struct ident { + const void *id; + void *p; + enum { DOMAIN, COUNTRY, FREQBAND } type; +}; + +static void +start_element(void *data, const char *name, const char **attr) +{ +#define iseq(a,b) (strcasecmp(a,b) == 0) + struct mystate *mt; + const void *id, *ref, *mode; + int i; + + mt = data; + if (++mt->level == MAXLEVEL) { + /* XXX force parser to abort */ + return; + } + mt->sbuf[mt->level] = sbuf_new_auto(); + id = ref = mode = NULL; + for (i = 0; attr[i] != NULL; i += 2) { + if (iseq(attr[i], "id")) { + id = attr[i+1]; + } else if (iseq(attr[i], "ref")) { + ref = attr[i+1]; + } else if (iseq(attr[i], "mode")) { + mode = attr[i+1]; + } else + printf("%*.*s[%s = %s]\n", mt->level + 1, + mt->level + 1, "", attr[i], attr[i+1]); + } + if (iseq(name, "rd") && mt->rd == NULL) { + if (mt->country == NULL) { + mt->rd = calloc(1, sizeof(struct regdomain)); + mt->rd->name = strdup(id); + mt->nident++; + LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); + } else + mt->country->rd = (void *)strdup(ref); + return; + } + if (iseq(name, "defcc") && mt->rd != NULL) { + mt->rd->cc = (void *)strdup(ref); + return; + } + if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { + if (mode == NULL) { + warnx("no mode for netband at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + return; + } + if (iseq(mode, "11b")) + mt->curband = &mt->rd->bands_11b; + else if (iseq(mode, "11g")) + mt->curband = &mt->rd->bands_11g; + else if (iseq(mode, "11a")) + mt->curband = &mt->rd->bands_11a; + else if (iseq(mode, "11ng")) + mt->curband = &mt->rd->bands_11ng; + else if (iseq(mode, "11na")) + mt->curband = &mt->rd->bands_11na; + else + warnx("unknown mode \"%s\" at line %ld", + __DECONST(char *, mode), + XML_GetCurrentLineNumber(mt->parser)); + return; + } + if (iseq(name, "band") && mt->netband == NULL) { + if (mt->curband == NULL) { + warnx("band without enclosing netband at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + return; + } + mt->netband = calloc(1, sizeof(struct netband)); + LIST_INSERT_HEAD(mt->curband, mt->netband, next); + return; + } + if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { + /* XXX handle inlines and merge into table? */ + if (mt->netband->band != NULL) { + warnx("duplicate freqband at line %ld ignored", + XML_GetCurrentLineNumber(mt->parser)); + /* XXX complain */ + } else + mt->netband->band = (void *)strdup(ref); + return; + } + + if (iseq(name, "country") && mt->country == NULL) { + mt->country = calloc(1, sizeof(struct country)); + mt->country->isoname = strdup(id); + mt->country->code = NO_COUNTRY; + mt->nident++; + LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); + return; + } + + if (iseq(name, "freqband") && mt->freqband == NULL) { + mt->freqband = calloc(1, sizeof(struct freqband)); + mt->freqband->id = strdup(id); + mt->nident++; + LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); + return; + } +#undef iseq +} + +static int +decode_flag(struct mystate *mt, const char *p, int len) +{ +#define iseq(a,b) (strcasecmp(a,b) == 0) + static const struct { + const char *name; + int len; + uint32_t value; + } flags[] = { +#define FLAG(x) { #x, sizeof(#x)-1, x } + FLAG(IEEE80211_CHAN_A), + FLAG(IEEE80211_CHAN_B), + FLAG(IEEE80211_CHAN_G), + FLAG(IEEE80211_CHAN_HT20), + FLAG(IEEE80211_CHAN_HT40), + FLAG(IEEE80211_CHAN_ST), + FLAG(IEEE80211_CHAN_TURBO), + FLAG(IEEE80211_CHAN_PASSIVE), + FLAG(IEEE80211_CHAN_DFS), + FLAG(IEEE80211_CHAN_CCK), + FLAG(IEEE80211_CHAN_OFDM), + FLAG(IEEE80211_CHAN_2GHZ), + FLAG(IEEE80211_CHAN_5GHZ), + FLAG(IEEE80211_CHAN_DYN), + FLAG(IEEE80211_CHAN_GFSK), + FLAG(IEEE80211_CHAN_GSM), + FLAG(IEEE80211_CHAN_STURBO), + FLAG(IEEE80211_CHAN_HALF), + FLAG(IEEE80211_CHAN_QUARTER), + FLAG(IEEE80211_CHAN_HT40U), + FLAG(IEEE80211_CHAN_HT40D), + FLAG(IEEE80211_CHAN_4MSXMIT), + FLAG(IEEE80211_CHAN_NOADHOC), + FLAG(IEEE80211_CHAN_NOHOSTAP), + FLAG(IEEE80211_CHAN_11D), + FLAG(IEEE80211_CHAN_FHSS), + FLAG(IEEE80211_CHAN_PUREG), + FLAG(IEEE80211_CHAN_108A), + FLAG(IEEE80211_CHAN_108G), +#undef FLAG + { "ECM", 3, REQ_ECM }, + { "INDOOR", 6, REQ_INDOOR }, + { "OUTDOOR", 7, REQ_OUTDOOR }, + }; + int i; + + for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) + if (len == flags[i].len && iseq(p, flags[i].name)) + return flags[i].value; + warnx("unknown flag \"%.*s\" at line %ld ignored", + len, p, XML_GetCurrentLineNumber(mt->parser)); + return 0; +#undef iseq +} + +static void +end_element(void *data, const char *name) +{ +#define iseq(a,b) (strcasecmp(a,b) == 0) + struct mystate *mt; + int len; + char *p; + + mt = data; + sbuf_finish(mt->sbuf[mt->level]); + p = sbuf_data(mt->sbuf[mt->level]); + len = sbuf_len(mt->sbuf[mt->level]); + + /* <freqband>...</freqband> */ + if (iseq(name, "freqstart") && mt->freqband != NULL) { + mt->freqband->freqStart = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "freqend") && mt->freqband != NULL) { + mt->freqband->freqEnd = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "chanwidth") && mt->freqband != NULL) { + mt->freqband->chanWidth = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "chansep") && mt->freqband != NULL) { + mt->freqband->chanSep = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "flags")) { + if (mt->freqband != NULL) + mt->freqband->flags |= decode_flag(mt, p, len); + else if (mt->netband != NULL) + mt->netband->flags |= decode_flag(mt, p, len); + else { + warnx("flags without freqband or netband at line %ld ignored", + XML_GetCurrentLineNumber(mt->parser)); + } + goto done; + } + + /* <rd> ... </rd> */ + if (iseq(name, "name") && mt->rd != NULL) { + mt->rd->name = strdup(p); + goto done; + } + if (iseq(name, "sku") && mt->rd != NULL) { + mt->rd->sku = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "netband") && mt->rd != NULL) { + mt->curband = NULL; + goto done; + } + + /* <band> ... </band> */ + if (iseq(name, "freqband") && mt->netband != NULL) { + /* XXX handle inline freqbands */ + goto done; + } + if (iseq(name, "maxpower") && mt->netband != NULL) { + mt->netband->maxPower = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { + mt->netband->maxPowerDFS = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "maxantgain") && mt->netband != NULL) { + mt->netband->maxAntGain = strtoul(p, NULL, 0); + goto done; + } + + /* <country>...</country> */ + if (iseq(name, "isocc") && mt->country != NULL) { + mt->country->code = strtoul(p, NULL, 0); + goto done; + } + if (iseq(name, "name") && mt->country != NULL) { + mt->country->name = strdup(p); + goto done; + } + + if (len != 0) { + warnx("unexpected XML token \"%s\" data \"%s\" at line %ld", + name, p, XML_GetCurrentLineNumber(mt->parser)); + /* XXX goto done? */ + } + /* </freqband> */ + if (iseq(name, "freqband") && mt->freqband != NULL) { + /* XXX must have start/end frequencies */ + /* XXX must have channel width/sep */ + mt->freqband = NULL; + goto done; + } + /* </rd> */ + if (iseq(name, "rd") && mt->rd != NULL) { + mt->rd = NULL; + goto done; + } + /* </band> */ + if (iseq(name, "band") && mt->netband != NULL) { + if (mt->netband->band == NULL) { + warnx("no freqbands for band at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + } + if (mt->netband->maxPower == 0) { + warnx("no maxpower for band at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + } + /* default max power w/ DFS to max power */ + if (mt->netband->maxPowerDFS == 0) + mt->netband->maxPowerDFS = mt->netband->maxPower; + mt->netband = NULL; + goto done; + } + /* </netband> */ + if (iseq(name, "netband") && mt->netband != NULL) { + mt->curband = NULL; + goto done; + } + /* </country> */ + if (iseq(name, "country") && mt->country != NULL) { + if (mt->country->code == NO_COUNTRY) { + warnx("no ISO cc for country at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + } + if (mt->country->name == NULL) { + warnx("no name for country at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + } + if (mt->country->rd == NULL) { + warnx("no regdomain reference for country at line %ld", + XML_GetCurrentLineNumber(mt->parser)); + } + mt->country = NULL; + goto done; + } +done: + sbuf_delete(mt->sbuf[mt->level]); + mt->sbuf[mt->level--] = NULL; +#undef iseq +} + +static void +char_data(void *data, const XML_Char *s, int len) +{ + struct mystate *mt; + const char *b, *e; + + mt = data; + + b = s; + e = s + len-1; + for (; isspace(*b) && b < e; b++) + ; + for (; isspace(*e) && e > b; e++) + ; + if (e != b || (*b != '\0' && !isspace(*b))) + sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); +} + +static void * +findid(struct regdata *rdp, const void *id, int type) +{ + struct ident *ip; + + for (ip = rdp->ident; ip->id != NULL; ip++) + if (ip->type == type && strcasecmp(ip->id, id) == 0) + return ip->p; + return NULL; +} + +/* + * Parse an regdomain XML configuration and build the internal representation. + */ +int +lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) +{ + struct mystate *mt; + struct regdomain *dp; + struct country *cp; + struct freqband *fp; + struct netband *nb; + const void *id; + int i, errors; + + memset(rdp, 0, sizeof(struct regdata)); + mt = calloc(1, sizeof(struct mystate)); + if (mt == NULL) + return ENOMEM; + /* parse the XML input */ + mt->rdp = rdp; + mt->parser = XML_ParserCreate(NULL); + XML_SetUserData(mt->parser, mt); + XML_SetElementHandler(mt->parser, start_element, end_element); + XML_SetCharacterDataHandler(mt->parser, char_data); + if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) { + warnx("%s: %s at line %ld", __func__, + XML_ErrorString(XML_GetErrorCode(mt->parser)), + XML_GetCurrentLineNumber(mt->parser)); + return -1; + } + XML_ParserFree(mt->parser); + + /* setup the identifer table */ + rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); + if (rdp->ident == NULL) + return ENOMEM; + free(mt); + + errors = 0; + i = 0; + LIST_FOREACH(dp, &rdp->domains, next) { + rdp->ident[i].id = dp->name; + rdp->ident[i].p = dp; + rdp->ident[i].type = DOMAIN; + i++; + } + LIST_FOREACH(fp, &rdp->freqbands, next) { + rdp->ident[i].id = fp->id; + rdp->ident[i].p = fp; + rdp->ident[i].type = FREQBAND; + i++; + } + LIST_FOREACH(cp, &rdp->countries, next) { + rdp->ident[i].id = cp->isoname; + rdp->ident[i].p = cp; + rdp->ident[i].type = COUNTRY; + i++; + } + + /* patch references */ + LIST_FOREACH(dp, &rdp->domains, next) { + if (dp->cc != NULL) { + id = dp->cc; + dp->cc = findid(rdp, id, COUNTRY); + if (dp->cc == NULL) { + warnx("undefined country \"%s\"", + __DECONST(char *, id)); + errors++; + } + free(__DECONST(char *, id)); + } + LIST_FOREACH(nb, &dp->bands_11b, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11b band \"%s\"", + __DECONST(char *, nb->band)); + errors++; + } + nb->band = id; + } + LIST_FOREACH(nb, &dp->bands_11g, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11g band \"%s\"", + __DECONST(char *, nb->band)); + errors++; + } + nb->band = id; + } + LIST_FOREACH(nb, &dp->bands_11a, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11a band \"%s\"", + __DECONST(char *, nb->band)); + errors++; + } + nb->band = id; + } + LIST_FOREACH(nb, &dp->bands_11ng, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11ng band \"%s\"", + __DECONST(char *, nb->band)); + errors++; + } + nb->band = id; + } + LIST_FOREACH(nb, &dp->bands_11na, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11na band \"%s\"", + __DECONST(char *, nb->band)); + errors++; + } + nb->band = id; + } + } + LIST_FOREACH(cp, &rdp->countries, next) { + id = cp->rd; + cp->rd = findid(rdp, id, DOMAIN); + if (cp->rd == NULL) { + warnx("undefined country \"%s\"", + __DECONST(char *, id)); + errors++; + } + free(__DECONST(char *, id)); + } + + return errors ? EINVAL : 0; +} + +static void +cleanup_bands(netband_head *head) +{ + struct netband *nb; + + for (;;) { + nb = LIST_FIRST(head); + if (nb == NULL) + break; + free(nb); + } +} + +/* + * Cleanup state/resources for a previously parsed regdomain database. + */ +void +lib80211_regdomain_cleanup(struct regdata *rdp) +{ + + free(rdp->ident); + rdp->ident = NULL; + for (;;) { + struct regdomain *dp = LIST_FIRST(&rdp->domains); + if (dp == NULL) + break; + LIST_REMOVE(dp, next); + cleanup_bands(&dp->bands_11b); + cleanup_bands(&dp->bands_11g); + cleanup_bands(&dp->bands_11a); + cleanup_bands(&dp->bands_11ng); + cleanup_bands(&dp->bands_11na); + if (dp->name != NULL) + free(__DECONST(char *, dp->name)); + } + for (;;) { + struct country *cp = LIST_FIRST(&rdp->countries); + if (cp == NULL) + break; + LIST_REMOVE(cp, next); + if (cp->name != NULL) + free(__DECONST(char *, cp->name)); + free(cp); + } + for (;;) { + struct freqband *fp = LIST_FIRST(&rdp->freqbands); + if (fp == NULL) + break; + LIST_REMOVE(fp, next); + free(fp); + } +} + +struct regdata * +lib80211_alloc_regdata(void) +{ + struct regdata *rdp; + struct stat sb; + void *xml; + int fd; + + rdp = calloc(1, sizeof(struct regdata)); + + fd = open(_PATH_REGDOMAIN, O_RDONLY); + if (fd < 0) { +#ifdef DEBUG + warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); +#endif + free(rdp); + return NULL; + } + if (fstat(fd, &sb) < 0) { +#ifdef DEBUG + warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); +#endif + close(fd); + free(rdp); + return NULL; + } + xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (xml == MAP_FAILED) { +#ifdef DEBUG + warn("%s: mmap", __func__); +#endif + close(fd); + free(rdp); + return NULL; + } + if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { +#ifdef DEBUG + warn("%s: error reading regulatory database", __func__); +#endif + munmap(xml, sb.st_size); + close(fd); + free(rdp); + return NULL; + } + munmap(xml, sb.st_size); + close(fd); + + return rdp; +} + +void +lib80211_free_regdata(struct regdata *rdp) +{ + lib80211_regdomain_cleanup(rdp); + free(rdp); +} + +/* + * Lookup a regdomain by SKU. + */ +const struct regdomain * +lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) +{ + const struct regdomain *dp; + + LIST_FOREACH(dp, &rdp->domains, next) { + if (dp->sku == sku) + return dp; + } + return NULL; +} + +/* + * Lookup a regdomain by name. + */ +const struct regdomain * +lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) +{ + const struct regdomain *dp; + + LIST_FOREACH(dp, &rdp->domains, next) { + if (strcasecmp(dp->name, name) == 0) + return dp; + } + return NULL; +} + +/* + * Lookup a country by ISO country code. + */ +const struct country * +lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) +{ + const struct country *cp; + + LIST_FOREACH(cp, &rdp->countries, next) { + if (cp->code == cc) + return cp; + } + return NULL; +} + +/* + * Lookup a country by ISO/long name. + */ +const struct country * +lib80211_country_findbyname(const struct regdata *rdp, const char *name) +{ + const struct country *cp; + int len; + + len = strlen(name); + LIST_FOREACH(cp, &rdp->countries, next) { + if (strcasecmp(cp->isoname, name) == 0) + return cp; + } + LIST_FOREACH(cp, &rdp->countries, next) { + if (strncasecmp(cp->name, name, len) == 0) + return cp; + } + return NULL; +} diff --git a/freebsd-userspace/commands/sbin/ifconfig/regdomain.h b/freebsd-userspace/commands/sbin/ifconfig/regdomain.h new file mode 100644 index 00000000..81588752 --- /dev/null +++ b/freebsd-userspace/commands/sbin/ifconfig/regdomain.h @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * 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. + * + * 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. + * + * $FreeBSD$ + */ +#ifndef _LIB80211_H_ +#define _LIB80211_H_ + +#include <sys/cdefs.h> +#include <sys/queue.h> + +#ifdef __rtems__ +#include <freebsd/net80211/ieee80211_regdomain.h> +#else +#include <net80211/ieee80211_regdomain.h> +#endif + + +__BEGIN_DECLS + +struct freqband { + uint16_t freqStart; /* starting frequency (MHz) */ + uint16_t freqEnd; /* ending frequency (MHz) */ + uint8_t chanWidth; /* channel width (MHz) */ + uint8_t chanSep; /* channel sepaaration (MHz) */ + uint32_t flags; /* common operational constraints */ + + const void *id; + LIST_ENTRY(freqband) next; +}; + +/* private flags, don't pass to os */ +#define REQ_ECM 0x1 /* enable if ECM set */ +#define REQ_INDOOR 0x2 /* enable only for indoor operation */ +#define REQ_OUTDOOR 0x4 /* enable only for outdoor operation */ + +#define REQ_FLAGS (REQ_ECM|REQ_INDOOR|REQ_OUTDOOR) + +struct netband { + const struct freqband *band; /* channel list description */ + uint8_t maxPower; /* regulatory cap on tx power (dBm) */ + uint8_t maxPowerDFS; /* regulatory cap w/ DFS (dBm) */ + uint8_t maxAntGain; /* max allowed antenna gain (.5 dBm) */ + uint32_t flags; /* net80211 channel flags */ + + LIST_ENTRY(netband) next; +}; +typedef LIST_HEAD(, netband) netband_head; + +struct country; + +struct regdomain { + enum RegdomainCode sku; /* regdomain code/SKU */ + const char *name; /* printable name */ + const struct country *cc; /* country code for 1-1/default map */ + + netband_head bands_11b; /* 11b operation */ + netband_head bands_11g; /* 11g operation */ + netband_head bands_11a; /* 11a operation */ + netband_head bands_11ng;/* 11ng operation */ + netband_head bands_11na;/* 11na operation */ + + LIST_ENTRY(regdomain) next; +}; + +struct country { + enum ISOCountryCode code; +#define NO_COUNTRY 0xffff + const struct regdomain *rd; + const char* isoname; + const char* name; + + LIST_ENTRY(country) next; +}; + +struct ident; + +struct regdata { + LIST_HEAD(, country) countries; /* country code table */ + LIST_HEAD(, regdomain) domains; /* regulatory domains */ + LIST_HEAD(, freqband) freqbands; /* frequency band table */ + struct ident *ident; /* identifier table */ +}; + +#define _PATH_REGDOMAIN "/etc/regdomain.xml" + +struct regdata *lib80211_alloc_regdata(void); +void lib80211_free_regdata(struct regdata *); + +int lib80211_regdomain_readconfig(struct regdata *, const void *, size_t); +void lib80211_regdomain_cleanup(struct regdata *); + +const struct regdomain *lib80211_regdomain_findbysku(const struct regdata *, + enum RegdomainCode); +const struct regdomain *lib80211_regdomain_findbyname(const struct regdata *, + const char *); + +const struct country *lib80211_country_findbycc(const struct regdata *, + enum ISOCountryCode); +const struct country *lib80211_country_findbyname(const struct regdata *, + const char *); + +__END_DECLS + +#endif /* _LIB80211_H_ */ diff --git a/freebsd-userspace/from-freebsd.sh b/freebsd-userspace/from-freebsd.sh index a56a1075..e2fb85aa 100755 --- a/freebsd-userspace/from-freebsd.sh +++ b/freebsd-userspace/from-freebsd.sh @@ -260,6 +260,7 @@ netinet/udp.h sys/socket.h sys/socketvar.h sys/sysctl.h +sys/mman.h EOF diff --git a/freebsd-userspace/include/sys/mman.h b/freebsd-userspace/include/sys/mman.h new file mode 100644 index 00000000..2d90a14d --- /dev/null +++ b/freebsd-userspace/include/sys/mman.h @@ -0,0 +1,3 @@ +#include <freebsd/bsd.h> +#include <freebsd/sys/mman.h> + |