--- src/sbin/ifconfig/ifieee80211.c 2006/04/02 03:33:59 1.10 +++ src/sbin/ifconfig/ifieee80211.c 2006/05/18 13:51:45 1.11 @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sbin/ifconfig/ifieee80211.c,v 1.1.2.3 2002/02/07 15:12:37 ambrisko Exp $ + * $FreeBSD: src/sbin/ifconfig/ifieee80211.c,v 1.18.2.9 2006/03/07 17:50:23 sam Exp $ * $DragonFly$ */ @@ -75,7 +75,9 @@ #include #include #include +#include #include + #include #include #include @@ -84,10 +86,12 @@ #include #include #include +#include #include #include #include #include +#include #include "ifconfig.h" @@ -96,13 +100,19 @@ static const char *get_string(const char u_int8_t *buf, int *lenp); static void print_string(const u_int8_t *buf, int len); +static int +isanyarg(const char *arg) +{ + return (strcmp(arg, "-") == 0 || + strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0); +} + static void -set80211ssid(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211ssid(const char *val, int d, int s, const struct afswtch *rafp) { int ssid; int len; - u_int8_t data[33]; + u_int8_t data[IEEE80211_NWID_LEN]; ssid = 0; len = strlen(val); @@ -113,14 +123,14 @@ set80211ssid(const char *val, int d __un bzero(data, sizeof(data)); len = sizeof(data); - get_string(val, NULL, data, &len); + if (get_string(val, NULL, data, &len) == NULL) + exit(1); set80211(s, IEEE80211_IOC_SSID, ssid, len, data); } static void -set80211stationname(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211stationname(const char *val, int d, int s, const struct afswtch *rafp) { int len; u_int8_t data[33]; @@ -132,19 +142,50 @@ set80211stationname(const char *val, int set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data); } +/* + * Convert IEEE channel number to MHz frequency. + */ +static u_int +ieee80211_ieee2mhz(u_int chan) +{ + if (chan == 14) + return 2484; + if (chan < 14) /* 0-13 */ + return 2407 + chan*5; + if (chan < 27) /* 15-26 */ + return 2512 + ((chan-15)*20); + return 5000 + (chan*5); +} + +/* + * Convert MHz frequency to IEEE channel number. + */ +static u_int +ieee80211_mhz2ieee(u_int freq) +{ + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + if (freq < 5000) + return 15 + ((freq - 2512) / 20); + return (freq - 5000) / 5; +} + static void -set80211channel(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211channel(const char *val, int d, int s, const struct afswtch *rafp) { - if (strcmp(val, "-") == 0) + if (!isanyarg(val)) { + int v = atoi(val); + if (v > 255) /* treat as frequency */ + v = ieee80211_mhz2ieee(v); + set80211(s, IEEE80211_IOC_CHANNEL, v, 0, NULL); + } else set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL); - else - set80211(s, IEEE80211_IOC_CHANNEL, atoi(val), 0, NULL); } static void -set80211authmode(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211authmode(const char *val, int d, int s, const struct afswtch *rafp) { int mode; @@ -154,16 +195,19 @@ set80211authmode(const char *val, int d 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 { - err(1, "unknown authmode"); + errx(1, "unknown authmode"); } set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL); } static void -set80211powersavemode(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp) { int mode; @@ -178,15 +222,14 @@ set80211powersavemode(const char *val, i } else if (strcasecmp(val, "psp-cam") == 0) { mode = IEEE80211_POWERSAVE_PSP_CAM; } else { - err(1, "unknown powersavemode"); + errx(1, "unknown powersavemode"); } set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL); } static void -set80211powersave(const char *val __unused, int d, int s, - const struct afswtch *rafp __unused) +set80211powersave(const char *val, int d, int s, const struct afswtch *rafp) { if (d == 0) set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF, @@ -197,15 +240,13 @@ set80211powersave(const char *val __unus } static void -set80211powersavesleep(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +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 __unused, int s, - const struct afswtch *rafp __unused) +set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp) { int mode; @@ -216,29 +257,35 @@ set80211wepmode(const char *val, int d _ } else if (strcasecmp(val, "mixed") == 0) { mode = IEEE80211_WEP_MIXED; } else { - err(1, "unknown wep mode"); + errx(1, "unknown wep mode"); } set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL); } static void -set80211wep(const char *val __unused, int d, int s, - const struct afswtch *rafp __unused) +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 __unused, int s, - const struct afswtch *rafp __unused) +set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp) { - set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL); + 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 __unused, int s, - const struct afswtch *rafp __unused) +set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp) { int key = 0; int len; @@ -257,13 +304,12 @@ set80211wepkey(const char *val, int d __ } /* - * This function is purly a NetBSD compatability interface. The NetBSD - * iterface is too inflexable, but it's there so we'll support it since + * 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 __unused, int s, - const struct afswtch *rafp __unused) +set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp) { int txkey; int i, len; @@ -279,6 +325,8 @@ set80211nwkey(const char *val, int d __u 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); } @@ -299,15 +347,14 @@ set80211nwkey(const char *val, int d __u } static void -set80211rtsthreshold(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp) { - set80211(s, IEEE80211_IOC_RTSTHRESHOLD, atoi(val), 0, NULL); + set80211(s, IEEE80211_IOC_RTSTHRESHOLD, + isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL); } static void -set80211protmode(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211protmode(const char *val, int d, int s, const struct afswtch *rafp) { int mode; @@ -318,166 +365,1183 @@ set80211protmode(const char *val, int d } else if (strcasecmp(val, "rtscts") == 0) { mode = IEEE80211_PROTMODE_RTSCTS; } else { - err(1, "unknown protection mode"); + errx(1, "unknown protection mode"); } set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL); } static void -set80211txpower(const char *val, int d __unused, int s, - const struct afswtch *rafp __unused) +set80211txpower(const char *val, int d, int s, const struct afswtch *rafp) { set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL); } +#define IEEE80211_ROAMING_DEVICE 0 +#define IEEE80211_ROAMING_AUTO 1 +#define IEEE80211_ROAMING_MANUAL 2 + static void -ieee80211_status (int s) +set80211roaming(const char *val, int d, int s, const struct afswtch *rafp) { - int i; - int num; - struct ieee80211req ireq; - u_int8_t data[32]; - char spacer; - - memset(&ireq, 0, sizeof(ireq)); - strncpy(ireq.i_name, name, sizeof(ireq.i_name)); - ireq.i_data = &data; + int mode; - ireq.i_type = IEEE80211_IOC_SSID; - ireq.i_val = -1; - if (ioctl(s, SIOCG80211, &ireq) < 0) { - /* If we can't get the SSID, the this isn't an 802.11 device. */ - return; - } - printf("\tssid "); - print_string(data, ireq.i_len); - num = 0; - ireq.i_type = IEEE80211_IOC_NUMSSIDS; - if (ioctl(s, SIOCG80211, &ireq) >= 0) { - num = ireq.i_val; + 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"); } - ireq.i_type = IEEE80211_IOC_SSID; - for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) { - if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) { - printf(" %d:", ireq.i_val + 1); - print_string(data, ireq.i_len); + 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 +set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp) +{ + struct ieee80211req_chanlist chanlist; +#define MAXCHAN (sizeof(chanlist.ic_channels)*NBBY) + 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; + + tp = strchr(cp, ','); + if (tp != NULL) + *tp++ = '\0'; + switch (sscanf(cp, "%u-%u", &first, &last)) { + case 1: + if (first > MAXCHAN) + errx(-1, "channel %u out of range, max %zu", + first, MAXCHAN); + setbit(chanlist.ic_channels, first); + break; + case 2: + if (first > MAXCHAN) + errx(-1, "channel %u out of range, max %zu", + first, MAXCHAN); + if (last > MAXCHAN) + errx(-1, "channel %u out of range, max %zu", + last, MAXCHAN); + 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; + while (isspace(*tp)) + tp++; + if (!isdigit(*tp)) + break; + cp = tp; } - printf("\n"); + set80211(s, IEEE80211_IOC_CHANLIST, 0, + sizeof(chanlist), (uint8_t *) &chanlist); +#undef MAXCHAN +} - ireq.i_type = IEEE80211_IOC_STATIONNAME; - if (ioctl(s, SIOCG80211, &ireq) != -1) { - printf("\tstationname "); - print_string(data, ireq.i_len); - printf("\n"); +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); } +} - ireq.i_type = IEEE80211_IOC_CHANNEL; - if (ioctl(s, SIOCG80211, &ireq) < 0) { - goto end; +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), (u_int8_t *) &mlme); +} + +static +DECL_CMD_FUNC(set80211maccmd, val, d) +{ + set80211(s, IEEE80211_IOC_MACCMD, d, 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 +set80211burst(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_BURST, d, 0, NULL); +} + +static +DECL_CMD_FUNC(set80211mcastrate, val, d) +{ + set80211(s, IEEE80211_IOC_MCAST_RATE, (int) 2*atof(val), 0, NULL); +} + +static +DECL_CMD_FUNC(set80211fragthreshold, val, d) +{ + set80211(s, IEEE80211_IOC_FRAGTHRESHOLD, + isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL); +} + +static int +getmaxrate(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; } - printf("\tchannel %d", ireq.i_val); + return maxrate / 2; +} - ireq.i_type = IEEE80211_IOC_AUTHMODE; - if (ioctl(s, SIOCG80211, &ireq) != -1) { - printf(" authmode"); - switch (ireq.i_val) { - case IEEE80211_AUTH_NONE: - printf(" NONE"); - break; - case IEEE80211_AUTH_OPEN: - printf(" OPEN"); - break; - case IEEE80211_AUTH_SHARED: - printf(" SHARED"); - break; - default: - printf(" UNKNOWN"); +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 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(">"); } +} - ireq.i_type = IEEE80211_IOC_POWERSAVE; - if (ioctl(s, SIOCG80211, &ireq) != -1 && - ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) { - printf(" powersavemode"); - switch (ireq.i_val) { - case IEEE80211_POWERSAVE_OFF: - printf(" OFF"); - break; - case IEEE80211_POWERSAVE_CAM: - printf(" CAM"); - break; - case IEEE80211_POWERSAVE_PSP: - printf(" PSP"); +/* + * 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; +} + +/* 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 int __inline +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int __inline +iswmeoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); +} + +static int __inline +isatherosoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); +} + +static void +printies(const u_int8_t *vp, int ielen, int maxcols) +{ + while (ielen > 0) { + switch (vp[0]) { + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(vp)) + printie(" WPA", vp, 2+vp[1], maxcols); + else if (iswmeoui(vp)) + printie(" WME", vp, 2+vp[1], maxcols); + else if (isatherosoui(vp)) + printie(" ATH", vp, 2+vp[1], maxcols); + else + printie(" VEN", vp, 2+vp[1], maxcols); + break; + case IEEE80211_ELEMID_RSN: + printie(" RSN", vp, 2+vp[1], maxcols); + break; + default: + printie(" ???", vp, 2+vp[1], maxcols); + break; + } + ielen -= 2+vp[1]; + vp += 2+vp[1]; + } +} + +static void +list_scan(int s) +{ + uint8_t buf[24*1024]; + struct ieee80211req ireq; + char ssid[IEEE80211_NWID_LEN+1]; + uint8_t *cp; + int len, ssidmax; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_SCAN_RESULTS; + ireq.i_data = buf; + ireq.i_len = sizeof(buf); + if (ioctl(s, SIOCG80211, &ireq) < 0) + errx(1, "unable to get scan results"); + len = ireq.i_len; + if (len < sizeof(struct ieee80211req_scan_result)) + return; + + ssidmax = verbose ? IEEE80211_NWID_LEN : 14; + printf("%-*.*s %-17.17s %4s %4s %-5s %3s %4s\n" + , ssidmax, ssidmax, "SSID" + , "BSSID" + , "CHAN" + , "RATE" + , "S:N" + , "INT" + , "CAPS" + ); + cp = buf; + do { + struct ieee80211req_scan_result *sr; + uint8_t *vp; + + sr = (struct ieee80211req_scan_result *) cp; + vp = (u_int8_t *)(sr+1); + printf("%-*.*s %s %3d %3dM %2d:%-2d %3d %-4.4s" + , ssidmax + , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len) + , ssid + , ether_ntoa((const struct ether_addr *) sr->isr_bssid) + , ieee80211_mhz2ieee(sr->isr_freq) + , getmaxrate(sr->isr_rates, sr->isr_nrates) + , sr->isr_rssi, sr->isr_noise + , sr->isr_intval + , getcaps(sr->isr_capinfo) + ); + printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);; + printf("\n"); + cp += sr->isr_len, len -= sr->isr_len; + } while (len >= sizeof(struct ieee80211req_scan_result)); +} + +#include + +static void +scan_and_wait(int s) +{ + 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; + /* 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; - case IEEE80211_POWERSAVE_PSP_CAM: - printf(" PSP-CAM"); + } + 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 void +list_stations(int s) +{ + uint8_t buf[24*1024]; + struct ieee80211req ireq; + uint8_t *cp; + int len; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_STA_INFO; + ireq.i_data = buf; + ireq.i_len = sizeof(buf); + if (ioctl(s, SIOCG80211, &ireq) < 0) + errx(1, "unable to get station information"); + len = ireq.i_len; + if (len < sizeof(struct ieee80211req_sta_info)) + return; + + printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n" + , "ADDR" + , "AID" + , "CHAN" + , "RATE" + , "RSSI" + , "IDLE" + , "TXSEQ" + , "RXSEQ" + , "CAPS" + , "ERP" + ); + cp = buf; + do { + struct ieee80211req_sta_info *si; + uint8_t *vp; + + si = (struct ieee80211req_sta_info *) cp; + vp = (u_int8_t *)(si+1); + printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x" + , ether_ntoa((const struct ether_addr*) si->isi_macaddr) + , IEEE80211_AID(si->isi_associd) + , ieee80211_mhz2ieee(si->isi_freq) + , (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2 + , si->isi_rssi + , si->isi_inact + , si->isi_txseqs[0] + , si->isi_rxseqs[0] + , getcaps(si->isi_capinfo) + , si->isi_erp + ); + printies(vp, si->isi_ie_len, 24); + printf("\n"); + cp += si->isi_len, len -= si->isi_len; + } while (len >= sizeof(struct ieee80211req_sta_info)); +} + +static void +print_chaninfo(const struct ieee80211_channel *c) +{ +#define IEEE80211_IS_CHAN_PASSIVE(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE)) + char buf[14]; + + buf[0] = '\0'; + if (IEEE80211_IS_CHAN_FHSS(c)) + strlcat(buf, " FHSS", sizeof(buf)); + if (IEEE80211_IS_CHAN_A(c)) + strlcat(buf, " 11a", sizeof(buf)); + /* XXX 11g schizophrenia */ + if (IEEE80211_IS_CHAN_G(c) || + IEEE80211_IS_CHAN_PUREG(c)) + strlcat(buf, " 11g", sizeof(buf)); + else if (IEEE80211_IS_CHAN_B(c)) + strlcat(buf, " 11b", sizeof(buf)); + if (IEEE80211_IS_CHAN_T(c)) + strlcat(buf, " Turbo", sizeof(buf)); + printf("Channel %3u : %u%c Mhz%-14.14s", + ieee80211_mhz2ieee(c->ic_freq), c->ic_freq, + IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf); +#undef IEEE80211_IS_CHAN_PASSIVE +} + +static void +list_channels(int s, int allchans) +{ + struct ieee80211req ireq; + struct ieee80211req_chaninfo chans; + struct ieee80211req_chaninfo achans; + const struct ieee80211_channel *c; + int i, half; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_CHANINFO; + ireq.i_data = &chans; + ireq.i_len = sizeof(chans); + if (ioctl(s, SIOCG80211, &ireq) < 0) + errx(1, "unable to get channel information"); + if (!allchans) { + struct ieee80211req_chanlist active; + + ireq.i_type = IEEE80211_IOC_CHANLIST; + ireq.i_data = &active; + ireq.i_len = sizeof(active); + if (ioctl(s, SIOCG80211, &ireq) < 0) + errx(1, "unable to get active channel list"); + memset(&achans, 0, sizeof(achans)); + for (i = 0; i < chans.ic_nchans; i++) { + c = &chans.ic_chans[i]; + if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans) + achans.ic_chans[achans.ic_nchans++] = *c; } + } else + achans = chans; + 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]); + print_chaninfo(&achans.ic_chans[half+i]); + printf("\n"); + } + if (achans.ic_nchans % 2) { + print_chaninfo(&achans.ic_chans[i]); + printf("\n"); + } +} + +static void +list_keys(int s) +{ +} + +#define IEEE80211_C_BITS \ +"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \ +"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \ +"\31WPA2\32BURST\33WME" + +static void +list_capabilities(int s) +{ + struct ieee80211req ireq; + u_int32_t caps; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_DRIVER_CAPS; + if (ioctl(s, SIOCG80211, &ireq) < 0) + errx(1, "unable to get driver capabilities"); + caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len); + printb(name, caps, IEEE80211_C_BITS); + putchar('\n'); +} - ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP; +static void +list_wme(int s) +{ + static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" }; + struct ieee80211req ireq; + int ac; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_len = 0; + for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) { +again: + if (ireq.i_len & IEEE80211_WMEPARAM_BSS) + printf("\t%s", " "); + else + printf("\t%s", acnames[ac]); + + ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac; + + /* show WME BSS parameters */ + ireq.i_type = IEEE80211_IOC_WME_CWMIN; + if (ioctl(s, SIOCG80211, &ireq) != -1) + printf(" cwmin %2u", ireq.i_val); + ireq.i_type = IEEE80211_IOC_WME_CWMAX; + if (ioctl(s, SIOCG80211, &ireq) != -1) + printf(" cwmax %2u", ireq.i_val); + ireq.i_type = IEEE80211_IOC_WME_AIFS; + if (ioctl(s, SIOCG80211, &ireq) != -1) + printf(" aifs %2u", ireq.i_val); + ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT; + if (ioctl(s, SIOCG80211, &ireq) != -1) + printf(" txopLimit %3u", ireq.i_val); + ireq.i_type = IEEE80211_IOC_WME_ACM; if (ioctl(s, SIOCG80211, &ireq) != -1) { if (ireq.i_val) - printf(" powersavesleep %d", ireq.i_val); + printf(" acm"); + else if (verbose) + printf(" -acm"); + } + /* !BSS only */ + if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) { + ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (!ireq.i_val) + printf(" -ack"); + else if (verbose) + printf(" ack"); + } } + printf("\n"); + if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) { + ireq.i_len |= IEEE80211_WMEPARAM_BSS; + goto again; + } else + ireq.i_len &= ~IEEE80211_WMEPARAM_BSS; } +} - printf("\n"); +static void +list_mac(int s) +{ + struct ieee80211req ireq; + struct ieee80211req_maclist *acllist; + int i, nacls, policy; + 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; - spacer = '\t'; - ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD; + 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 */ + return; + + ireq.i_data = malloc(ireq.i_len); + if (ireq.i_data == NULL) + err(1, "out of memory for acl list"); + + if (ioctl(s, SIOCG80211, &ireq) < 0) + err(1, "unable to get mac acl list"); + if (policy == IEEE80211_MACCMD_POLICY_OPEN) { + if (verbose) + printf("policy: open\n"); + c = '*'; + } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) { + if (verbose) + printf("policy: allow\n"); + c = '+'; + } else if (policy == IEEE80211_MACCMD_POLICY_DENY) { + if (verbose) + printf("policy: deny\n"); + c = '-'; + } else { + printf("policy: unknown (%u)\n", policy); + c = '?'; + } + nacls = ireq.i_len / sizeof(*acllist); + acllist = (struct ieee80211req_maclist *) ireq.i_data; + for (i = 0; i < nacls; i++) + printf("%c%s\n", c, ether_ntoa( + (const struct ether_addr *) acllist[i].ml_macaddr)); +} + +static +DECL_CMD_FUNC(set80211list, arg, d) +{ +#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) + + 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")) + list_wme(s); + else if (iseq(arg, "mac")) + list_mac(s); + else + errx(1, "Don't know how to list %s for %s", arg, name); +#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) + return IEEE80211_M_IBSS; /* XXX ahdemo */ + if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + return IEEE80211_M_HOSTAP; + if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + return IEEE80211_M_MONITOR; + } + return IEEE80211_M_STA; +} + +static const struct ieee80211_channel * +getchaninfo(int s, int chan) +{ + struct ieee80211req ireq; + static struct ieee80211req_chaninfo chans; + static struct ieee80211_channel undef; + const struct ieee80211_channel *c; + int i, freq; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_CHANINFO; + ireq.i_data = &chans; + ireq.i_len = sizeof(chans); + if (ioctl(s, SIOCG80211, &ireq) < 0) + errx(1, "unable to get channel information"); + freq = ieee80211_ieee2mhz(chan); + for (i = 0; i < chans.ic_nchans; i++) { + c = &chans.ic_chans[i]; + if (c->ic_freq == freq) + return c; + } + return &undef; +} + +#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 + +#define MAXCOL 78 +static int col; +static char spacer; + +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 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 +ieee80211_status(int s) +{ + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + enum ieee80211_opmode opmode = get80211opmode(s); + int i, num, wpa, wme; + struct ieee80211req ireq; + u_int8_t data[32]; + const struct ieee80211_channel *c; + + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + ireq.i_data = &data; + + wpa = 0; /* unknown/not set */ + + ireq.i_type = IEEE80211_IOC_SSID; + ireq.i_val = -1; + if (ioctl(s, SIOCG80211, &ireq) < 0) { + /* If we can't get the SSID, this isn't an 802.11 device. */ + return; + } + num = 0; + ireq.i_type = IEEE80211_IOC_NUMSSIDS; + if (ioctl(s, SIOCG80211, &ireq) >= 0) + num = ireq.i_val; + printf("\tssid "); + if (num > 1) { + ireq.i_type = IEEE80211_IOC_SSID; + for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) { + if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) { + printf(" %d:", ireq.i_val + 1); + print_string(data, ireq.i_len); + } + } + } else + print_string(data, ireq.i_len); + + ireq.i_type = IEEE80211_IOC_CHANNEL; + if (ioctl(s, SIOCG80211, &ireq) < 0) + goto end; + c = getchaninfo(s, ireq.i_val); + if (ireq.i_val != -1) { + printf(" channel %d", ireq.i_val); + if (verbose) + printf(" (%u)", c->ic_freq); + } else if (verbose) + printf(" channel UNDEF"); + + ireq.i_type = IEEE80211_IOC_BSSID; + ireq.i_len = IEEE80211_ADDR_LEN; + if (ioctl(s, SIOCG80211, &ireq) >= 0 && + (memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0 || verbose)) + printf(" bssid %s", ether_ntoa(ireq.i_data)); + + ireq.i_type = IEEE80211_IOC_STATIONNAME; if (ioctl(s, SIOCG80211, &ireq) != -1) { - printf("%crtsthreshold %d", spacer, ireq.i_val); - spacer = ' '; + printf("\n\tstationname "); + print_string(data, ireq.i_len); } - ireq.i_type = IEEE80211_IOC_PROTMODE; + spacer = ' '; /* force first break */ + LINE_BREAK(); + + ireq.i_type = IEEE80211_IOC_AUTHMODE; if (ioctl(s, SIOCG80211, &ireq) != -1) { - printf("%cprotmode", spacer); switch (ireq.i_val) { - case IEEE80211_PROTMODE_OFF: - printf(" OFF"); + case IEEE80211_AUTH_NONE: + LINE_CHECK("authmode NONE"); break; - case IEEE80211_PROTMODE_CTS: - printf(" CTS"); + case IEEE80211_AUTH_OPEN: + LINE_CHECK("authmode OPEN"); break; - case IEEE80211_PROTMODE_RTSCTS: - printf(" RTSCTS"); + case IEEE80211_AUTH_SHARED: + LINE_CHECK("authmode SHARED"); + break; + case IEEE80211_AUTH_8021X: + LINE_CHECK("authmode 802.1x"); + break; + case IEEE80211_AUTH_WPA: + ireq.i_type = IEEE80211_IOC_WPA; + if (ioctl(s, SIOCG80211, &ireq) != -1) + wpa = ireq.i_val; + if (!wpa) + 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: - printf(" UNKNOWN"); + LINE_CHECK("authmode UNKNOWN (0x%x)", + ireq.i_val); break; } - spacer = ' '; - } - - ireq.i_type = IEEE80211_IOC_TXPOWER; - if (ioctl(s, SIOCG80211, &ireq) != -1) { - printf("%ctxpower %d", spacer, ireq.i_val); - spacer = ' '; } - if (spacer != '\t') - printf("\n"); - ireq.i_type = IEEE80211_IOC_WEP; if (ioctl(s, SIOCG80211, &ireq) != -1 && ireq.i_val != IEEE80211_WEP_NOSUP) { - printf("\twepmode"); - switch (ireq.i_val) { + int firstkey, wepmode; + + wepmode = ireq.i_val; + switch (wepmode) { case IEEE80211_WEP_OFF: - printf(" OFF"); + LINE_CHECK("privacy OFF"); break; case IEEE80211_WEP_ON: - printf(" ON"); + LINE_CHECK("privacy ON"); break; case IEEE80211_WEP_MIXED: - printf(" MIXED"); + LINE_CHECK("privacy MIXED"); break; default: - printf(" UNKNOWN"); + LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode); break; } @@ -491,7 +1555,10 @@ ieee80211_status (int s) warn("WEP support, but no tx key!"); goto end; } - printf(" weptxkey %d", ireq.i_val+1); + if (ireq.i_val != -1) + LINE_CHECK("deftxkey %d", ireq.i_val+1); + else if (wepmode != IEEE80211_WEP_OFF || verbose) + LINE_CHECK("deftxkey UNDEF"); ireq.i_type = IEEE80211_IOC_NUMWEPKEYS; if (ioctl(s, SIOCG80211, &ireq) < 0) { @@ -500,28 +1567,227 @@ ieee80211_status (int s) } num = ireq.i_val; - printf("\n"); - - ireq.i_type = IEEE80211_IOC_WEPKEY; - spacer = '\t'; + firstkey = 1; for (i = 0; i < num; i++) { - ireq.i_val = i; + struct ieee80211req_key ik; + + memset(&ik, 0, sizeof(ik)); + ik.ik_keyix = i; + ireq.i_type = IEEE80211_IOC_WPAKEY; + ireq.i_data = &ik; + ireq.i_len = sizeof(ik); if (ioctl(s, SIOCG80211, &ireq) < 0) { warn("WEP support, but can get keys!"); goto end; } - if (ireq.i_len == 0 || - ireq.i_len > IEEE80211_KEYBUF_SIZE) - continue; - printf("%cwepkey %d:%s", spacer, i+1, - ireq.i_len <= 5 ? "40-bit" : - ireq.i_len <= 13 ? "104-bit" : "128-bit"); - if (spacer == '\t') + if (ik.ik_keylen != 0) { + if (verbose) + LINE_BREAK(); + printkey(&ik); + firstkey = 0; + } + } + } + + ireq.i_type = IEEE80211_IOC_POWERSAVE; + if (ioctl(s, SIOCG80211, &ireq) != -1 && + ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) { + if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) { + switch (ireq.i_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; + } + ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP; + if (ioctl(s, SIOCG80211, &ireq) != -1) + LINE_CHECK("powersavesleep %d", ireq.i_val); + } + } + + ireq.i_type = IEEE80211_IOC_TXPOWMAX; + if (ioctl(s, SIOCG80211, &ireq) != -1) + LINE_CHECK("txpowmax %d", ireq.i_val); + + if (verbose) { + ireq.i_type = IEEE80211_IOC_TXPOWER; + if (ioctl(s, SIOCG80211, &ireq) != -1) + LINE_CHECK("txpower %d", ireq.i_val); + } + + ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val != IEEE80211_RTS_MAX || verbose) + LINE_CHECK("rtsthreshold %d", ireq.i_val); + } + + ireq.i_type = IEEE80211_IOC_MCAST_RATE; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val != 2*1 || verbose) { + if (ireq.i_val == 11) + LINE_CHECK("mcastrate 5.5"); + else + LINE_CHECK("mcastrate %d", ireq.i_val/2); + } + } + + ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val != IEEE80211_FRAG_MAX || verbose) + LINE_CHECK("fragthreshold %d", ireq.i_val); + } + + if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) { + ireq.i_type = IEEE80211_IOC_PUREG; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val) + LINE_CHECK("pureg"); + else if (verbose) + LINE_CHECK("-pureg"); + } + ireq.i_type = IEEE80211_IOC_PROTMODE; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + switch (ireq.i_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)", + ireq.i_val); + break; + } + } + } + + ireq.i_type = IEEE80211_IOC_WME; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + wme = ireq.i_val; + if (wme) + LINE_CHECK("wme"); + else if (verbose) + LINE_CHECK("-wme"); + } else + wme = 0; + + ireq.i_type = IEEE80211_IOC_BURST; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val) + LINE_CHECK("burst"); + else if (verbose) + LINE_CHECK("-burst"); + } + + if (opmode == IEEE80211_M_HOSTAP) { + ireq.i_type = IEEE80211_IOC_HIDESSID; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val) + LINE_CHECK("ssid HIDE"); + else if (verbose) + LINE_CHECK("ssid SHOW"); + } + + ireq.i_type = IEEE80211_IOC_APBRIDGE; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (!ireq.i_val) + LINE_CHECK("-apbridge"); + else if (verbose) + LINE_CHECK("apbridge"); + } + + ireq.i_type = IEEE80211_IOC_DTIM_PERIOD; + if (ioctl(s, SIOCG80211, &ireq) != -1) + LINE_CHECK("dtimperiod %u", ireq.i_val); + } else { + ireq.i_type = IEEE80211_IOC_ROAMING; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) { + switch (ireq.i_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)", + ireq.i_val); + break; + } + } + } + } + ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_val) + LINE_CHECK("bintval %u", ireq.i_val); + else if (verbose) + LINE_CHECK("bintval %u", ireq.i_val); + } + + if (wme && verbose) { + LINE_BREAK(); + list_wme(s); + } + + if (wpa) { + ireq.i_type = IEEE80211_IOC_COUNTERMEASURES; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + if (ireq.i_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) { } - if (spacer == ' ') - printf("\n"); +#endif + LINE_BREAK(); } + LINE_BREAK(); end: return; @@ -532,8 +1798,8 @@ set80211(int s, int type, int val, int l { struct ieee80211req ireq; - memset(&ireq, 0, sizeof(ireq)); - strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + (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; @@ -645,8 +1911,47 @@ static struct cmd ieee80211_cmds[] = { 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("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: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_ARG("mcastrate", set80211mcastrate), + DEF_CMD_ARG("fragthreshold", set80211fragthreshold), + DEF_CMD("burst", 1, set80211burst), + DEF_CMD("-burst", 0, set80211burst), }; - static struct afswtch af_ieee80211 = { .af_name = "af_ieee80211", .af_af = AF_UNSPEC,