From 841ab66cf8fab8cf09e8e97cb0ca0b6494140097 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Thu, 18 May 2006 13:51:46 +0000 Subject: [PATCH] Sync 802.11 support with FreeBSD6: "it includes completed 802.11g, WPA, 802.11i, 802.1x, WME/WMM, AP-side power-save, crypto plugin framework, authenticator plugin framework, and access control plugin frameowrk." MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Reoriganize the layout of netproto/802_11: put generic 802.11 layer, crypto modules, authentication module and access control module into their own directories. Header files are still in their original place. Nuke all of the mutexing in generic 802.11, reorganize ieee80211_node table scanning a little bit. Rename FreeBSD's m_append() to ieee80211_mbuf_append(), rename FreeBSD's m_unshare() to ieee80211_mbuf_clone() and put them into netproto/802_11/wlan/ieee80211_dragonly.c They are not generic enough for public using, at least for now. Pointed-out-by: hsu Expose ieee80211_add_{ssid, xrates, rates}() which are used by acx(4) Keep using opencrypto's AES implmentation for 802.11 CCMP crypto module Sync ifconfig(8)'s 802.11 support with FreeBSD6 Update acx(4) and ndis(4) for the new 802.11 support Sync iwi(4), ipw(4), wi(4) and ray(4) with FreeBSD6 For iwi(4): - Fix ieee80211_node leakage - Use a bitmap instead of FreeBSD's "unit number alloctor" to allocate IBSS node Add generic 802.11 layer and crypto modules into GENERIC and LINT, authentication module and access module are only added to LINT Unhook awi(4) from GENERIC and LINT temporarily, since as of this commit it is broken :( It will be fixed sometime later. Thank Sam Leffler and many other people for their work on 802.11 support. Thank Andrew Atrens and Adrian Michael Nida for submitting the patch. Thank all the people that helped testing 802.11 patches for this commit Based-on-Patch-Submitted-by: Andrew Atrens Adrian Michael Nida Tested-by: Thomas Schlesinger Johannes Hofmann Andrew Thompson Erik Wikström --- sbin/ifconfig/ifieee80211.c | 1629 ++++++++- sys/conf/files | 23 +- sys/config/GENERIC | 24 +- sys/config/LINT | 11 +- sys/dev/netif/acx/acx100.c | 14 +- sys/dev/netif/acx/acx111.c | 14 +- sys/dev/netif/acx/acxcmd.c | 3 +- sys/dev/netif/acx/if_acx.c | 316 +- sys/dev/netif/acx/if_acxvar.h | 4 +- sys/dev/netif/ipw/if_ipw.c | 412 +-- sys/dev/netif/ipw/if_ipwvar.h | 9 +- sys/dev/netif/iwi/if_iwi.c | 2913 ++++++++--------- sys/dev/netif/iwi/if_iwireg.h | 454 ++- sys/dev/netif/iwi/if_iwivar.h | 181 +- sys/dev/netif/ndis/if_ndis.c | 28 +- sys/dev/netif/ray/if_ray.c | 10 +- sys/dev/netif/ray/if_rayvar.h | 40 +- sys/dev/netif/wi/if_wi.c | 319 +- sys/dev/netif/wi/if_wivar.h | 12 +- sys/i386/conf/GENERIC | 24 +- sys/i386/conf/LINT | 11 +- sys/net/ethernet.h | 3 +- sys/net/route.h | 4 +- sys/net/rtsock.c | 78 +- sys/netproto/802_11/Makefile | 17 +- sys/netproto/802_11/_ieee80211.h | 191 ++ sys/netproto/802_11/ieee80211.h | 430 ++- sys/netproto/802_11/ieee80211_crypto.h | 201 +- sys/netproto/802_11/ieee80211_dragonfly.h | 167 + sys/netproto/802_11/ieee80211_ioctl.h | 417 ++- sys/netproto/802_11/ieee80211_node.h | 304 +- sys/netproto/802_11/ieee80211_proto.h | 221 +- sys/netproto/802_11/ieee80211_radiotap.h | 91 +- sys/netproto/802_11/ieee80211_var.h | 508 ++- sys/netproto/802_11/wlan/Makefile | 8 +- sys/netproto/802_11/wlan/ieee80211.c | 541 +-- sys/netproto/802_11/wlan/ieee80211_crypto.c | 753 +++-- .../802_11/wlan/ieee80211_crypto_none.c | 150 + .../802_11/wlan/ieee80211_dragonfly.c | 547 ++++ sys/netproto/802_11/wlan/ieee80211_input.c | 2809 ++++++++++++---- sys/netproto/802_11/wlan/ieee80211_ioctl.c | 2268 ++++++++++--- sys/netproto/802_11/wlan/ieee80211_node.c | 2292 +++++++++++-- sys/netproto/802_11/wlan/ieee80211_output.c | 1558 +++++++-- sys/netproto/802_11/wlan/ieee80211_proto.c | 912 +++++- sys/netproto/802_11/wlan_acl/Makefile | 7 + sys/netproto/802_11/wlan_acl/ieee80211_acl.c | 349 ++ sys/netproto/802_11/wlan_ccmp/Makefile | 9 + .../802_11/wlan_ccmp/ieee80211_crypto_ccmp.c | 665 ++++ sys/netproto/802_11/wlan_tkip/Makefile | 7 + .../802_11/wlan_tkip/ieee80211_crypto_tkip.c | 1021 ++++++ sys/netproto/802_11/wlan_wep/Makefile | 7 + .../802_11/wlan_wep/ieee80211_crypto_wep.c | 507 +++ sys/netproto/802_11/wlan_xauth/Makefile | 7 + .../802_11/wlan_xauth/ieee80211_xauth.c | 102 + usr.sbin/iwicontrol/iwicontrol.c | 8 +- 55 files changed, 18260 insertions(+), 5350 deletions(-) create mode 100644 sys/netproto/802_11/_ieee80211.h create mode 100644 sys/netproto/802_11/ieee80211_dragonfly.h create mode 100644 sys/netproto/802_11/wlan/ieee80211_crypto_none.c create mode 100644 sys/netproto/802_11/wlan/ieee80211_dragonfly.c create mode 100644 sys/netproto/802_11/wlan_acl/Makefile create mode 100644 sys/netproto/802_11/wlan_acl/ieee80211_acl.c create mode 100644 sys/netproto/802_11/wlan_ccmp/Makefile create mode 100644 sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c create mode 100644 sys/netproto/802_11/wlan_tkip/Makefile create mode 100644 sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c create mode 100644 sys/netproto/802_11/wlan_wep/Makefile create mode 100644 sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c create mode 100644 sys/netproto/802_11/wlan_xauth/Makefile create mode 100644 sys/netproto/802_11/wlan_xauth/ieee80211_xauth.c diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index 38d3d1eede..1ccffae81d 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -24,8 +24,8 @@ * 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 $ - * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.10 2006/04/02 03:33:59 sephe Exp $ + * $FreeBSD: src/sbin/ifconfig/ifieee80211.c,v 1.18.2.9 2006/03/07 17:50:23 sam Exp $ + * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.11 2006/05/18 13:51:45 sephe Exp $ */ /*- @@ -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 *val, const char *sep, 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 __unused, int s, 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 d __unused, int s, 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 __unused, int s, 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, int d __unused, int s, } 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 __unused, int d, int s, } 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 __unused, int s, } 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 __unused, int s, } /* - * 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 __unused, int s, 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 __unused, int s, } 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 __unused, int s, } 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; + 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); +} - memset(&ireq, 0, sizeof(ireq)); - strncpy(ireq.i_name, name, sizeof(ireq.i_name)); - ireq.i_data = &data; +static void +set80211wme(const char *val, int d, int s, const struct afswtch *rafp) +{ + set80211(s, IEEE80211_IOC_WME, d, 0, NULL); +} - 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; - } - 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); +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" - ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP; +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'); +} + +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_AUTH_OPEN: + LINE_CHECK("authmode OPEN"); + break; + case IEEE80211_AUTH_SHARED: + LINE_CHECK("authmode SHARED"); break; - case IEEE80211_PROTMODE_CTS: - printf(" CTS"); + case IEEE80211_AUTH_8021X: + LINE_CHECK("authmode 802.1x"); break; - case IEEE80211_PROTMODE_RTSCTS: - printf(" RTSCTS"); + 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 len, u_int8_t *data) { 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, diff --git a/sys/conf/files b/sys/conf/files index b7c081894e..5cf83d837d 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.120 2006/05/12 22:07:21 dillon Exp $ +# $DragonFly: src/sys/conf/files,v 1.121 2006/05/18 13:51:45 sephe Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -709,13 +709,20 @@ net/route.c standard net/rtsock.c standard net/zlib.c optional ppp_deflate net/zlib.c optional ipsec -netproto/802_11/ieee80211.c optional wlan -netproto/802_11/ieee80211_crypto.c optional wlan -netproto/802_11/ieee80211_input.c optional wlan -netproto/802_11/ieee80211_ioctl.c optional wlan -netproto/802_11/ieee80211_node.c optional wlan -netproto/802_11/ieee80211_output.c optional wlan -netproto/802_11/ieee80211_proto.c optional wlan +netproto/802_11/wlan/ieee80211.c optional wlan +netproto/802_11/wlan/ieee80211_crypto.c optional wlan +netproto/802_11/wlan/ieee80211_crypto_none.c optional wlan +netproto/802_11/wlan/ieee80211_dragonfly.c optional wlan +netproto/802_11/wlan/ieee80211_input.c optional wlan +netproto/802_11/wlan/ieee80211_ioctl.c optional wlan +netproto/802_11/wlan/ieee80211_node.c optional wlan +netproto/802_11/wlan/ieee80211_output.c optional wlan +netproto/802_11/wlan/ieee80211_proto.c optional wlan +netproto/802_11/wlan_acl/ieee80211_acl.c optional wlan_acl +netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c optional wlan_ccmp +netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c optional wlan_tkip +netproto/802_11/wlan_wep/ieee80211_crypto_wep.c optional wlan_wep +netproto/802_11/wlan_xauth/ieee80211_xauth.c optional wlan_xauth netproto/atalk/aarp.c optional netatalk netproto/atalk/at_control.c optional netatalk netproto/atalk/at_proto.c optional netatalk diff --git a/sys/config/GENERIC b/sys/config/GENERIC index a2d8229d4d..5366114051 100644 --- a/sys/config/GENERIC +++ b/sys/config/GENERIC @@ -4,7 +4,7 @@ # Check the LINT configuration file in sys/i386/conf, for an # exhaustive list of options. # -# $DragonFly: src/sys/config/GENERIC,v 1.33 2006/05/11 08:23:20 swildner Exp $ +# $DragonFly: src/sys/config/GENERIC,v 1.34 2006/05/18 13:51:45 sephe Exp $ machine i386 cpu I386_CPU @@ -214,20 +214,24 @@ device ep device fe0 at isa? disable port 0x300 # Xircom Ethernet device xe -# Generic 802.11 stack, used by wi -device wlan -# PRISM I IEEE 802.11b wireless NIC. -device awi -# WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really -# exists only as a PCMCIA device, so there is no ISA attachment needed -# and resources will always be dynamically assigned by the pccard code. -device wi -# Aironet 4500/4800 802.11 wireless NICs. Note: the declaration below will + +# Wireless NIC cards +device wlan # 802.11 support +device wlan_ccmp # 802.11 CCMP support +device wlan_tkip # 802.11 TKIP support +device wlan_wep # 802.11 WEP support +# Aironet 4500/4800 802.11 wireless NICs. Note: the declaration below will # work for PCMCIA and PCI cards, as well as ISA cards set to ISA PnP # mode (the factory default). If you set the switches on your ISA # card for a manually chosen I/O address and IRQ, you must specify # those parameters here. device an +#device awi # PRISM I IEEE 802.11b wireless NIC +# WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really +# exists only as a PCMCIA device, so there is no ISA attachment needed +# and resources will always be dynamically assigned by the pccard code. +device wi + # The probe order of these is presently determined by i386/isa/isa_compat.c. device ie0 at isa? disable port 0x300 irq 10 iomem 0xd0000 #device le0 at isa? disable port 0x300 irq 5 iomem 0xd0000 diff --git a/sys/config/LINT b/sys/config/LINT index 3faa1e3d83..ed153340af 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/config/LINT,v 1.75 2006/05/14 18:07:29 swildner Exp $ +# $DragonFly: src/sys/config/LINT,v 1.76 2006/05/18 13:51:45 sephe Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1411,10 +1411,15 @@ device sr0 at isa? port 0x300 irq 5 iomem 0xd0000 device sn0 at isa? port 0x300 irq 10 # Wlan support is mandatory for some wireless LAN devices. -device wlan # Wireless LAN support +device wlan # 802.11 support +device wlan_acl # 802.11 MAC-based access control for AP +device wlan_ccmp # 802.11 CCMP support +device wlan_tkip # 802.11 TKIP support +device wlan_wep # 802.11 WEP support +device wlan_xauth # 802.11 WPA or 802.1x authentication for AP options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output -device awi # AMD PCnetMobile +#device awi # AMD PCnetMobile device an # Aironet Communications 4500/4800 device ipw # Intel PRO/Wireless 2100 device iwi # Intel PRO/Wireless 2200BG/2915ABG diff --git a/sys/dev/netif/acx/acx100.c b/sys/dev/netif/acx/acx100.c index bd72442adb..a43f49ce59 100644 --- a/sys/dev/netif/acx/acx100.c +++ b/sys/dev/netif/acx/acx100.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/acx/acx100.c,v 1.1 2006/04/01 02:55:36 sephe Exp $ + * $DragonFly: src/sys/dev/netif/acx/acx100.c,v 1.2 2006/05/18 13:51:45 sephe Exp $ */ #include @@ -260,8 +260,8 @@ static void acx100_set_fw_txdesc_rate(struct acx_softc *, struct acx_txbuf *, int); static void acx100_set_bss_join_param(struct acx_softc *, void *, int); -static int acx100_set_wepkey(struct acx_softc *, - struct ieee80211_wepkey *, int); +static int acx100_set_wepkey(struct acx_softc *, struct ieee80211_key *, + int); static void acx100_proc_wep_rxbuf(struct acx_softc *, struct mbuf *, int *); @@ -696,20 +696,20 @@ acx100_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl) } static int -acx100_set_wepkey(struct acx_softc *sc, struct ieee80211_wepkey *wk, int wk_idx) +acx100_set_wepkey(struct acx_softc *sc, struct ieee80211_key *wk, int wk_idx) { struct acx100_conf_wepkey conf_wk; - if (wk->wk_len > ACX100_WEPKEY_LEN) { + if (wk->wk_keylen > ACX100_WEPKEY_LEN) { if_printf(&sc->sc_ic.ic_if, "%dth WEP key size beyond %d\n", wk_idx, ACX100_WEPKEY_LEN); return EINVAL; } conf_wk.action = ACX100_WEPKEY_ACT_ADD; - conf_wk.key_len = wk->wk_len; + conf_wk.key_len = wk->wk_keylen; conf_wk.key_idx = wk_idx; - bcopy(wk->wk_key, conf_wk.key, wk->wk_len); + bcopy(wk->wk_key, conf_wk.key, wk->wk_keylen); if (acx100_set_wepkey_conf(sc, &conf_wk) != 0) { if_printf(&sc->sc_ic.ic_if, "%s set %dth WEP key failed\n", __func__, wk_idx); diff --git a/sys/dev/netif/acx/acx111.c b/sys/dev/netif/acx/acx111.c index e381c2d3b5..84549a365f 100644 --- a/sys/dev/netif/acx/acx111.c +++ b/sys/dev/netif/acx/acx111.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/acx/acx111.c,v 1.1 2006/04/01 02:55:36 sephe Exp $ + * $DragonFly: src/sys/dev/netif/acx/acx111.c,v 1.2 2006/05/18 13:51:45 sephe Exp $ */ #include @@ -274,8 +274,8 @@ static void acx111_set_fw_txdesc_rate(struct acx_softc *, struct acx_txbuf *, int); static void acx111_set_bss_join_param(struct acx_softc *, void *, int); -static int acx111_set_wepkey(struct acx_softc *, - struct ieee80211_wepkey *, int); +static int acx111_set_wepkey(struct acx_softc *, struct ieee80211_key *, + int); void acx111_set_param(device_t dev) @@ -478,11 +478,11 @@ acx111_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl) } static int -acx111_set_wepkey(struct acx_softc *sc, struct ieee80211_wepkey *wk, int wk_idx) +acx111_set_wepkey(struct acx_softc *sc, struct ieee80211_key *wk, int wk_idx) { struct acx111_wepkey wepkey; - if (wk->wk_len > ACX111_WEPKEY_LEN) { + if (wk->wk_keylen > ACX111_WEPKEY_LEN) { if_printf(&sc->sc_ic.ic_if, "%dth WEP key size beyond %d\n", wk_idx, ACX111_WEPKEY_LEN); return EINVAL; @@ -492,9 +492,9 @@ acx111_set_wepkey(struct acx_softc *sc, struct ieee80211_wepkey *wk, int wk_idx) wepkey.action = htole16(ACX111_WEPKEY_ACT_ADD); wepkey.key_type = ACX111_WEPKEY_TYPE_DEFAULT; wepkey.index = 0; /* XXX ignored? */ - wepkey.key_len = wk->wk_len; + wepkey.key_len = wk->wk_keylen; wepkey.key_idx = wk_idx; - bcopy(wk->wk_key, wepkey.key, wk->wk_len); + bcopy(wk->wk_key, wepkey.key, wk->wk_keylen); if (acx_exec_command(sc, ACXCMD_WEP_MGMT, &wepkey, sizeof(wepkey), NULL, 0) != 0) { if_printf(&sc->sc_ic.ic_if, "%s set %dth WEP key failed\n", diff --git a/sys/dev/netif/acx/acxcmd.c b/sys/dev/netif/acx/acxcmd.c index 62824bb679..caef1a4740 100644 --- a/sys/dev/netif/acx/acxcmd.c +++ b/sys/dev/netif/acx/acxcmd.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/acx/acxcmd.c,v 1.1 2006/04/01 02:55:36 sephe Exp $ + * $DragonFly: src/sys/dev/netif/acx/acxcmd.c,v 1.2 2006/05/18 13:51:45 sephe Exp $ */ #include @@ -155,6 +155,7 @@ acx_join_bss(struct acx_softc *sc, uint8_t mode, struct ieee80211_node *node) bj->esslen = node->ni_esslen; bcopy(node->ni_essid, bj->essid, node->ni_esslen); + DPRINTF((&sc->sc_ic.ic_if, "join BSS/IBSS on channel %d\n", bj->channel)); return acx_exec_command(sc, ACXCMD_JOIN_BSS, bj, BSS_JOIN_PARAM_SIZE(bj), NULL, 0); } diff --git a/sys/dev/netif/acx/if_acx.c b/sys/dev/netif/acx/if_acx.c index 2b14dd081d..975987f946 100644 --- a/sys/dev/netif/acx/if_acx.c +++ b/sys/dev/netif/acx/if_acx.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/acx/if_acx.c,v 1.1 2006/04/01 02:55:36 sephe Exp $ + * $DragonFly: src/sys/dev/netif/acx/if_acx.c,v 1.2 2006/05/18 13:51:45 sephe Exp $ */ /* @@ -142,7 +142,9 @@ static int acx_config(struct acx_softc *); static int acx_read_config(struct acx_softc *, struct acx_config *); static int acx_write_config(struct acx_softc *, struct acx_config *); static int acx_set_wepkeys(struct acx_softc *); +#ifdef foo static void acx_begin_scan(struct acx_softc *); +#endif static void acx_next_scan(void *); static void acx_start(struct ifnet *); @@ -163,7 +165,7 @@ static int acx_init_tx_ring(struct acx_softc *); static int acx_init_rx_ring(struct acx_softc *); static int acx_newbuf(struct acx_softc *, struct acx_rxbuf *, int); static int acx_encap(struct acx_softc *, struct acx_txbuf *, - struct mbuf *, struct acx_node *, int); + struct mbuf *, struct ieee80211_node *, int); static int acx_reset(struct acx_softc *); @@ -186,16 +188,12 @@ static int acx_load_radio_firmware(struct acx_softc *, const uint8_t *, static int acx_load_base_firmware(struct acx_softc *, const uint8_t *, uint32_t); -static struct ieee80211_node *acx_node_alloc(struct ieee80211com *); -static void acx_node_free(struct ieee80211com *, struct ieee80211_node *); +static struct ieee80211_node *acx_node_alloc(struct ieee80211_node_table *); static void acx_node_init(struct acx_softc *, struct acx_node *); static void acx_node_update(struct acx_softc *, struct acx_node *, uint8_t, uint8_t); static int acx_newstate(struct ieee80211com *, enum ieee80211_state, int); -/* XXX */ -static void acx_media_status(struct ifnet *, struct ifmediareq *); - static int acx_sysctl_txrate_upd_intvl_min(SYSCTL_HANDLER_ARGS); static int acx_sysctl_txrate_upd_intvl_max(SYSCTL_HANDLER_ARGS); static int acx_sysctl_txrate_sample_thresh(SYSCTL_HANDLER_ARGS); @@ -425,17 +423,16 @@ acx_attach(device_t dev) &ic->ic_myaddr[i]); } - ieee80211_ifattach(ifp); + ieee80211_ifattach(ic); - /* Override alloc/free */ + /* Override node alloc */ ic->ic_node_alloc = acx_node_alloc; - ic->ic_node_free = acx_node_free; /* Override newstate */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = acx_newstate; - ieee80211_media_init(ifp, ieee80211_media_change, acx_media_status); + ieee80211_media_init(ic, ieee80211_media_change, ieee80211_media_status); sc->sc_txrate_upd_intvl_min = 10; /* 10 seconds */ sc->sc_txrate_upd_intvl_max = 300; /* 5 minutes */ @@ -503,9 +500,12 @@ acx_attach(device_t dev) goto fail1; } + if (bootverbose) + ieee80211_announce(ic); + return 0; fail1: - ieee80211_ifdetach(ifp); + ieee80211_ifdetach(ic); fail: acx_detach(dev); return error; @@ -517,7 +517,8 @@ acx_detach(device_t dev) struct acx_softc *sc = device_get_softc(dev); if (device_is_attached(dev)) { - struct ifnet *ifp = &sc->sc_ic.ic_if; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; lwkt_serialize_enter(ifp->if_serializer); @@ -527,7 +528,7 @@ acx_detach(device_t dev) lwkt_serialize_exit(ifp->if_serializer); - ieee80211_ifdetach(ifp); + ieee80211_ifdetach(ic); } if (sc->sc_sysctl_tree != NULL) @@ -629,10 +630,11 @@ acx_init(void *arg) goto back; /* Setup WEP */ - if (sc->sc_ic.ic_flags & IEEE80211_WEP_ON) { + if (sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY) { error = acx_set_wepkeys(sc); if (error) goto back; + sc->sc_ic.ic_flags &= ~IEEE80211_F_DROPUNENC; } /* Turn on power led */ @@ -644,7 +646,12 @@ acx_init(void *arg) ifp->if_flags &= ~IFF_OACTIVE; /* Begin background scanning */ +#ifdef foo acx_begin_scan(sc); +#else + ieee80211_new_state(&sc->sc_ic, IEEE80211_S_SCAN, -1); +#endif + back: if (error) acx_stop(sc); @@ -662,21 +669,28 @@ acx_set_wepkeys(struct acx_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct acx_conf_wep_txkey wep_txkey; - int i, error; + int i, error, got_wk = 0; for (i = 0; i < IEEE80211_WEP_NKID; ++i) { - struct ieee80211_wepkey *wk = &ic->ic_nw_keys[i]; + struct ieee80211_key *wk = &ic->ic_nw_keys[i]; - if (wk->wk_len == 0) + if (wk->wk_keylen == 0) continue; error = sc->chip_set_wepkey(sc, wk, i); if (error) return error; + + if (sc->sc_softwep && (wk->wk_flags & IEEE80211_KEY_XMIT)) + wk->wk_flags |= IEEE80211_KEY_SWCRYPT; + got_wk = 1; } + if (!got_wk || ic->ic_def_txkey == IEEE80211_KEYIX_NONE) + return 0; + /* Set current WEP key index */ - wep_txkey.wep_txkey = ic->ic_wep_txkey; + wep_txkey.wep_txkey = ic->ic_def_txkey; if (acx_set_wep_txkey_conf(sc, &wep_txkey) != 0) { if_printf(&ic->ic_if, "set WEP txkey failed\n"); return ENXIO; @@ -684,13 +698,14 @@ acx_set_wepkeys(struct acx_softc *sc) return 0; } +#ifdef foo static void acx_begin_scan(struct acx_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint8_t chan; - ieee80211_begin_scan(&ic->ic_if); + ieee80211_begin_scan(ic, 1); chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); @@ -701,6 +716,7 @@ acx_begin_scan(struct acx_softc *sc) callout_reset(&sc->sc_chanscan_timer, hz / acx_chanscan_rate, acx_next_scan, sc); } +#endif static void acx_next_scan(void *arg) @@ -712,10 +728,13 @@ acx_next_scan(void *arg) lwkt_serialize_enter(ifp->if_serializer); if (ic->ic_state == IEEE80211_S_SCAN) { +#if 0 uint8_t chan; +#endif - ieee80211_next_scan(ifp); + ieee80211_next_scan(ic); +#if 0 chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); ACX_ENABLE_TXCHAN(sc, chan); @@ -723,6 +742,7 @@ acx_next_scan(void *arg) callout_reset(&sc->sc_chanscan_timer, hz / acx_chanscan_rate, acx_next_scan, sc); +#endif } lwkt_serialize_exit(ifp->if_serializer); @@ -768,13 +788,13 @@ acx_stop(struct acx_softc *sc) if (buf->tb_mbuf != NULL) { bus_dmamap_unload(bd->mbuf_dma_tag, buf->tb_mbuf_dmamap); - m_free(buf->tb_mbuf); + m_freem(buf->tb_mbuf); buf->tb_mbuf = NULL; } ni = (struct ieee80211_node *)buf->tb_node; - if (ni != NULL && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); + if (ni != NULL) + ieee80211_free_node(ni); buf->tb_node = NULL; } @@ -786,7 +806,7 @@ acx_stop(struct acx_softc *sc) if (bd->rx_buf[i].rb_mbuf != NULL) { bus_dmamap_unload(bd->mbuf_dma_tag, bd->rx_buf[i].rb_mbuf_dmamap); - m_free(bd->rx_buf[i].rb_mbuf); + m_freem(bd->rx_buf[i].rb_mbuf); bd->rx_buf[i].rb_mbuf = NULL; } } @@ -1021,14 +1041,14 @@ acx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) switch (cmd) { case SIOCSLOADFW: - error = suser(curthread); + error = suser_cred(cr, NULL_CRED_OKAY); if (error) break; error = acx_copyin_firmware(sc, req); break; case SIOCSKILLFW: - error = suser(curthread); + error = suser_cred(cr, NULL_CRED_OKAY); if (error) break; acx_free_firmware(sc); @@ -1063,7 +1083,7 @@ acx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) /* TODO */ break; default: - error = ieee80211_ioctl(ifp, cmd, data, cr); + error = ieee80211_ioctl(&sc->sc_ic, cmd, data, cr); break; } @@ -1076,18 +1096,6 @@ acx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) return error; } -static __inline struct mbuf * -acx_softwep(struct ieee80211com *ic, struct mbuf *m, struct acx_node *node) -{ - m = ieee80211_wep_crypt(&ic->ic_if, m, 1); - if (m != NULL) - return m; - - if (node != NULL && (struct ieee80211_node *)node != ic->ic_bss) - ieee80211_free_node(ic, (struct ieee80211_node *)node); - return NULL; -} - static void acx_start(struct ifnet *ifp) { @@ -1114,70 +1122,70 @@ acx_start(struct ifnet *ifp) trans = 0; for (buf = &bd->tx_buf[idx]; buf->tb_mbuf == NULL; buf = &bd->tx_buf[idx]) { - struct acx_node *node; + struct ieee80211_frame *f; + struct ieee80211_node *ni = NULL; struct mbuf *m; int rate; - node = NULL; if (!IF_QEMPTY(&ic->ic_mgtq)) { - struct ieee80211_node *ni; - IF_DEQUEUE(&ic->ic_mgtq, m); ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; +#if 0 /* * Since mgmt data are transmitted at fixed rate * they will not be used to do rate control. */ - if (ni && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); - + if (ni != NULL) + ieee80211_free_node(ni); +#endif rate = 4; /* XXX 2Mb/s for mgmt packet */ } else if (!ifq_is_empty(&ifp->if_snd)) { - struct ieee80211_frame *f; + struct ether_header *eh; + struct acx_node *node; - /* XXX */ -#if 0 if (ic->ic_state != IEEE80211_S_RUN) { if_printf(ifp, "data packet dropped due to " "not RUN. Current state %d\n", ic->ic_state); break; } -#endif m = ifq_dequeue(&ifp->if_snd, NULL); if (m == NULL) break; - m = ieee80211_encap(ifp, m, - (struct ieee80211_node **)&node); - if (m == NULL) { + if (m->m_len < sizeof(struct ether_header)) { + m = m_pullup(m, sizeof(struct ether_header)); + if (m == NULL) { + ifp->if_oerrors++; + continue; + } + } + eh = mtod(m, struct ether_header *); + + ni = ieee80211_find_txnode(ic, eh->ether_dhost); + if (ni == NULL) { + m_freem(m); ifp->if_oerrors++; continue; } - f = mtod(m, struct ieee80211_frame *); - if (ic->ic_flags & IEEE80211_F_WEPON) { - f->i_fc[1] |= IEEE80211_FC1_WEP; - if (sc->sc_softwep) { - m = acx_softwep(ic, m, node); - if (m == NULL) { - /* - * axc_softwep() will free - * `node' for us if it fails - */ - ifp->if_oerrors++; - node = NULL; - continue; - } - } + /* TODO power save */ + + m = ieee80211_encap(ic, m, ni); + if (m == NULL) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + continue; } + node = (struct acx_node *)ni; if (node->nd_txrate < 0) { acx_node_init(sc, node); +#if 0 if (ic->ic_opmode == IEEE80211_M_IBSS) { /* XXX * Add extra reference here, @@ -1186,9 +1194,9 @@ acx_start(struct ifnet *ifp) * they are allocated, which * make TX rate control impossible */ - ieee80211_ref_node( - (struct ieee80211_node *)node); + ieee80211_ref_node(ni); } +#endif } rate = node->nd_rates.rs_rates[node->nd_txrate]; @@ -1198,16 +1206,27 @@ acx_start(struct ifnet *ifp) break; } + f = mtod(m, struct ieee80211_frame *); + if ((f->i_fc[1] & IEEE80211_FC1_WEP) && sc->sc_softwep) { + KASSERT(ni != NULL, ("TX node is NULL (WEP)\n")); + if (ieee80211_crypto_encap(ic, ni, m) == NULL) { + ieee80211_free_node(ni); + m_freem(m); + ifp->if_oerrors++; + continue; + } + } + if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m); - if (acx_encap(sc, buf, m, node, rate) != 0) { - struct ieee80211_node *ni; - - ni = (struct ieee80211_node *)node; - if (ni != NULL && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); - + if (acx_encap(sc, buf, m, ni, rate) != 0) { + /* + * NOTE: `m' will be freed in acx_encap() + * if we reach here. + */ + if (ni != NULL) + ieee80211_free_node(ni); ifp->if_oerrors++; continue; } @@ -1310,7 +1329,7 @@ acx_txeof(struct acx_softc *sc) break; bus_dmamap_unload(bd->mbuf_dma_tag, buf->tb_mbuf_dmamap); - m_free(buf->tb_mbuf); + m_freem(buf->tb_mbuf); buf->tb_mbuf = NULL; error = FW_TXDESC_GETFIELD_1(sc, buf, f_tx_error); @@ -1329,8 +1348,7 @@ acx_txeof(struct acx_softc *sc) ni = (struct ieee80211_node *)buf->tb_node; acx_node_update(sc, buf->tb_node, buf->tb_rate, error); - if (ni != ic->ic_bss) - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); buf->tb_node = NULL; } @@ -1497,37 +1515,30 @@ acx_rxeof(struct acx_softc *sc) sc->chip_rxbuf_exhdr); f = mtod(m, struct ieee80211_frame *); - if (ic->ic_opmode == IEEE80211_M_STA) { - ni = ieee80211_ref_node(ic->ic_bss); - } else { - ni = ieee80211_find_node(ic, f->i_addr2); - if (ni == NULL) - ni = ieee80211_ref_node(ic->ic_bss); - } - if (f->i_fc[1] & IEEE80211_FC1_WEP) { /* Short circuit software WEP */ f->i_fc[1] &= ~IEEE80211_FC1_WEP; /* Do chip specific RX buffer processing */ - if (sc->chip_proc_wep_rxbuf != NULL) + if (sc->chip_proc_wep_rxbuf != NULL) { sc->chip_proc_wep_rxbuf(sc, m, &len); + f = mtod(m, struct ieee80211_frame *); + } } + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)f); + m->m_len = m->m_pkthdr.len = len; m->m_pkthdr.rcvif = &ic->ic_if; - ieee80211_input(&ic->ic_if, m, ni, head->rbh_level, + ieee80211_input(ic, m, ni, head->rbh_level, le32toh(head->rbh_time)); - if (ni == ic->ic_bss) - ieee80211_unref_node(&ni); - else - ieee80211_free_node(ic, ni); - + ieee80211_free_node(ni); ifp->if_ipackets++; } else { - m_free(m); + m_freem(m); ifp->if_ierrors++; } @@ -1842,15 +1853,12 @@ acx_load_firmware(struct acx_softc *sc, uint32_t offset, const uint8_t *data, return 0; } -MALLOC_DECLARE(ACX_NODE); -MALLOC_DEFINE(ACX_NODE, "acx_node", "acx(4) wrapper for ieee80211_node"); - static struct ieee80211_node * -acx_node_alloc(struct ieee80211com *ic) +acx_node_alloc(struct ieee80211_node_table *nt __unused) { struct acx_node *node; - node = malloc(sizeof(struct acx_node), ACX_NODE, M_NOWAIT | M_ZERO); + node = malloc(sizeof(struct acx_node), M_80211_NODE, M_NOWAIT | M_ZERO); node->nd_txrate = -1; return (struct ieee80211_node *)node; } @@ -2008,12 +2016,6 @@ acx_node_update(struct acx_softc *sc, struct acx_node *node, uint8_t rate, #undef IEEERATE } -static void -acx_node_free(struct ieee80211com *ic, struct ieee80211_node *n) -{ - free(n, ACX_NODE); -} - static int acx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { @@ -2023,6 +2025,19 @@ acx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) ASSERT_SERIALIZED(ic->ic_if.if_serializer); switch (nstate) { + case IEEE80211_S_SCAN: + if (ic->ic_state != IEEE80211_S_INIT) { + uint8_t chan; + + chan = ieee80211_chan2ieee(ic, ic->ic_curchan); + ACX_ENABLE_TXCHAN(sc, chan); + ACX_ENABLE_RXCHAN(sc, chan); + + callout_reset(&sc->sc_chanscan_timer, + hz / acx_chanscan_rate, + acx_next_scan, sc); + } + break; case IEEE80211_S_AUTH: if (ic->ic_opmode == IEEE80211_M_STA) { struct ieee80211_node *ni; @@ -2301,7 +2316,7 @@ acx_dma_free(struct acx_softc *sc) if (bd->rx_buf[i].rb_mbuf != NULL) { bus_dmamap_unload(bd->mbuf_dma_tag, bd->rx_buf[i].rb_mbuf_dmamap); - m_free(bd->rx_buf[i].rb_mbuf); + m_freem(bd->rx_buf[i].rb_mbuf); } bus_dmamap_destroy(bd->mbuf_dma_tag, bd->rx_buf[i].rb_mbuf_dmamap); @@ -2313,7 +2328,7 @@ acx_dma_free(struct acx_softc *sc) if (bd->tx_buf[i].tb_mbuf != NULL) { bus_dmamap_unload(bd->mbuf_dma_tag, bd->tx_buf[i].tb_mbuf_dmamap); - m_free(bd->tx_buf[i].tb_mbuf); + m_freem(bd->tx_buf[i].tb_mbuf); } bus_dmamap_destroy(bd->mbuf_dma_tag, bd->tx_buf[i].tb_mbuf_dmamap); @@ -2425,7 +2440,7 @@ acx_newbuf(struct acx_softc *sc, struct acx_rxbuf *rb, int wait) m, acx_buf_dma_addr, &paddr, wait ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT); if (error) { - m_free(m); + m_freem(m); if_printf(&sc->sc_ic.ic_if, "can't map rx mbuf %d\n", error); return error; } @@ -2449,10 +2464,11 @@ acx_newbuf(struct acx_softc *sc, struct acx_rxbuf *rb, int wait) static int acx_encap(struct acx_softc *sc, struct acx_txbuf *txbuf, struct mbuf *m, - struct acx_node *node, int rate) + struct ieee80211_node *ni, int rate) { struct acx_buf_data *bd = &sc->sc_buf_data; struct acx_ring_data *rd = &sc->sc_ring_data; + struct acx_node *node = (struct acx_node *)ni; uint32_t paddr; uint8_t ctrl; int error; @@ -2567,20 +2583,10 @@ acx_encap(struct acx_softc *sc, struct acx_txbuf *txbuf, struct mbuf *m, CSR_WRITE_2(sc, ACXREG_INTR_TRIG, ACXRV_TRIG_TX_FINI); back: if (error) - m_free(m); + m_freem(m); return error; } -/* XXX C&P of ieee80211_add_ssid() */ -static uint8_t * -my_ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) -{ - *frm++ = IEEE80211_ELEMID_SSID; - *frm++ = len; - memcpy(frm, ssid, len); - return frm + len; -} - static int acx_set_null_tmplt(struct acx_softc *sc) { @@ -2615,7 +2621,7 @@ acx_set_probe_req_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len) IEEE80211_ADDR_COPY(f->i_addr3, etherbroadcastaddr); v = req.data.u_data.var; - v = my_ieee80211_add_ssid(v, ssid, ssid_len); + v = ieee80211_add_ssid(v, ssid, ssid_len); v = ieee80211_add_rates(v, &sc->sc_ic.ic_sup_rates[sc->chip_phymode]); v = ieee80211_add_xrates(v, &sc->sc_ic.ic_sup_rates[sc->chip_phymode]); vlen = v - req.data.u_data.var; @@ -2648,7 +2654,7 @@ acx_set_probe_resp_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len, resp.data.u_data.cap = htole16(IEEE80211_CAPINFO_IBSS); v = resp.data.u_data.var; - v = my_ieee80211_add_ssid(v, ssid, ssid_len); + v = ieee80211_add_ssid(v, ssid, ssid_len); v = ieee80211_add_rates(v, &ic->ic_sup_rates[sc->chip_phymode]); *v++ = IEEE80211_ELEMID_DSPARMS; @@ -2694,7 +2700,7 @@ acx_set_beacon_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len, beacon.data.u_data.cap = htole16(IEEE80211_CAPINFO_IBSS); v = beacon.data.u_data.var; - v = my_ieee80211_add_ssid(v, ssid, ssid_len); + v = ieee80211_add_ssid(v, ssid, ssid_len); v = ieee80211_add_rates(v, &ic->ic_sup_rates[sc->chip_phymode]); *v++ = IEEE80211_ELEMID_DSPARMS; @@ -2714,64 +2720,6 @@ acx_set_beacon_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len, return _acx_set_beacon_tmplt(sc, &beacon, ACX_TMPLT_BEACON_SIZ(vlen)); } -/* - * XXX - * C&P of ieee80211_media_status(), only - * imr->ifm_status |= IFM_ACTIVE; is added - */ -static void -acx_media_status(struct ifnet *ifp, struct ifmediareq *imr) -{ - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni = NULL; - - imr->ifm_status = IFM_AVALID; - imr->ifm_active = IFM_IEEE80211; - - if (ic->ic_state == IEEE80211_S_RUN) - imr->ifm_status |= IFM_ACTIVE; - - imr->ifm_active |= IFM_AUTO; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ni = ic->ic_bss; - /* calculate rate subtype */ - imr->ifm_active |= ieee80211_rate2media(ic, - ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); - break; - case IEEE80211_M_IBSS: - imr->ifm_active |= IFM_IEEE80211_ADHOC; - break; - case IEEE80211_M_AHDEMO: - /* should not come here */ - break; - case IEEE80211_M_HOSTAP: - imr->ifm_active |= IFM_IEEE80211_HOSTAP; - break; - case IEEE80211_M_MONITOR: - imr->ifm_active |= IFM_IEEE80211_MONITOR; - break; - } - switch (ic->ic_curmode) { - case IEEE80211_MODE_11A: - imr->ifm_active |= IFM_IEEE80211_11A; - break; - case IEEE80211_MODE_11B: - imr->ifm_active |= IFM_IEEE80211_11B; - break; - case IEEE80211_MODE_11G: - imr->ifm_active |= IFM_IEEE80211_11G; - break; - case IEEE80211_MODE_FH: - imr->ifm_active |= IFM_IEEE80211_FH; - break; - case IEEE80211_MODE_TURBO: - imr->ifm_active |= IFM_IEEE80211_11A - | IFM_IEEE80211_TURBO; - break; - } -} - static int acx_sysctl_txrate_upd_intvl_min(SYSCTL_HANDLER_ARGS) { diff --git a/sys/dev/netif/acx/if_acxvar.h b/sys/dev/netif/acx/if_acxvar.h index f1a1110bfd..e6900a27f0 100644 --- a/sys/dev/netif/acx/if_acxvar.h +++ b/sys/dev/netif/acx/if_acxvar.h @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/acx/if_acxvar.h,v 1.1 2006/04/01 02:55:36 sephe Exp $ + * $DragonFly: src/sys/dev/netif/acx/if_acxvar.h,v 1.2 2006/05/18 13:51:45 sephe Exp $ */ #ifndef _IF_ACXVAR_H @@ -406,7 +406,7 @@ struct acx_softc { int (*chip_set_wepkey) /* non-NULL */ (struct acx_softc *, - struct ieee80211_wepkey *, int); + struct ieee80211_key *, int); int (*chip_read_config) (struct acx_softc *, struct acx_config *); diff --git a/sys/dev/netif/ipw/if_ipw.c b/sys/dev/netif/ipw/if_ipw.c index 1b0e723fb8..5c95a8be2e 100644 --- a/sys/dev/netif/ipw/if_ipw.c +++ b/sys/dev/netif/ipw/if_ipw.c @@ -26,7 +26,8 @@ * * * $Id: if_ipw.c,v 1.7.2.1 2005/01/13 20:01:03 damien Exp $ - * $DragonFly: src/sys/dev/netif/ipw/Attic/if_ipw.c,v 1.13 2005/12/31 14:07:59 sephe Exp $ + * $FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.7.2.4 2006/01/29 15:13:01 damien Exp $ + * $DragonFly: src/sys/dev/netif/ipw/Attic/if_ipw.c,v 1.14 2006/05/18 13:51:45 sephe Exp $ */ /*- @@ -57,6 +58,7 @@ #include #include +#include #include #include @@ -78,8 +80,8 @@ #include #include -#include "if_ipwreg.h" -#include "if_ipwvar.h" +#include +#include #ifdef IPW_DEBUG #define DPRINTF(x) if (ipw_debug > 0) printf x @@ -101,7 +103,8 @@ struct ipw_ident { }; static const struct ipw_ident ipw_ident_table[] = { - { 0x8086, 0x1043, "Intel(R) PRO/Wireless 2100 MiniPCI" }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_2100, + "Intel(R) PRO/Wireless 2100 MiniPCI" }, { 0, 0, NULL } }; @@ -223,7 +226,7 @@ ipw_attach(device_t dev) struct ifnet *ifp = &ic->ic_if; struct sysctl_oid *sysctl_tree; u_int16_t val; - int error, rid, i; + int error, i; if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " @@ -237,8 +240,9 @@ ipw_attach(device_t dev) pci_enable_busmaster(dev); /* map the register window */ - rid = IPW_PCI_BAR0; - sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + sc->mem_rid = IPW_PCI_BAR0; + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, + RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); goto fail; @@ -247,9 +251,9 @@ ipw_attach(device_t dev) sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); - rid = 0; - sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | - RF_SHAREABLE); + sc->irq_rid = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, + RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); goto fail; @@ -314,9 +318,6 @@ ipw_attach(device_t dev) if (!(ipw_read_prom_word(sc, IPW_EEPROM_RADIO) & 8)) sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH; - /* default to authmode OPEN */ - sc->authmode = IEEE80211_AUTH_OPEN; - /* IBSS channel undefined for now */ ic->ic_ibss_chan = &ic->ic_channels[0]; @@ -329,11 +330,11 @@ ipw_attach(device_t dev) ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); ifq_set_ready(&ifp->if_snd); - ieee80211_ifattach(ifp); + ieee80211_ifattach(ic); /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = ipw_newstate; - ieee80211_media_init(ifp, ipw_media_change, ipw_media_status); + ieee80211_media_init(ic, ipw_media_change, ipw_media_status); bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf); @@ -346,6 +347,8 @@ ipw_attach(device_t dev) sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(IPW_TX_RADIOTAP_PRESENT); + sc->dwelltime = 100; + SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "radio", CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I", @@ -356,6 +359,11 @@ ipw_attach(device_t dev) CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S", "Statistics"); + SYSCTL_ADD_INT(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "dwell", + CTLFLAG_RW, &sc->dwelltime, 0, + "Channel dwell time (ms) for AP/station scanning"); + /* * Hook our interrupt after all initialization is complete */ @@ -364,10 +372,13 @@ ipw_attach(device_t dev) if (error != 0) { device_printf(dev, "could not set up interrupt\n"); bpfdetach(ifp); - ieee80211_ifdetach(ifp); + ieee80211_ifdetach(ic); goto fail; } + if (bootverbose) + ieee80211_announce(ic); + return 0; fail: @@ -389,18 +400,16 @@ ipw_detach(device_t dev) lwkt_serialize_exit(ifp->if_serializer); bpfdetach(ifp); - ieee80211_ifdetach(ifp); + ieee80211_ifdetach(&sc->sc_ic); } ipw_release(sc); if (sc->irq != NULL) - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); - if (sc->mem != NULL) { - bus_release_resource(dev, SYS_RES_MEMORY, IPW_PCI_BAR0, - sc->mem); - } + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); sysctl_ctx_free(&sc->sysctl_ctx); @@ -808,25 +817,27 @@ ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) static int ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg __unused) { - struct ipw_softc *sc = ic->ic_softc; - struct ieee80211_node *ni = ic->ic_bss; - u_int32_t len; - u_int8_t val; + struct ipw_softc *sc = ic->ic_if.if_softc; + struct ieee80211_node *ni; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint32_t len; switch (nstate) { case IEEE80211_S_RUN: - len = IEEE80211_NWID_LEN; - ipw_read_table2(sc, IPW_INFO_CURRENT_SSID, ni->ni_essid, &len); - ni->ni_esslen = len; + DELAY(200); /* firmware needs a short delay here */ - val = ipw_read_table1(sc, IPW_INFO_CURRENT_CHANNEL); - ni->ni_chan = &ic->ic_channels[val]; + len = IEEE80211_ADDR_LEN; + ipw_read_table2(sc, IPW_INFO_CURRENT_BSSID, macaddr, &len); + ni = ieee80211_find_node(&ic->ic_scan, macaddr); + if (ni == NULL) + break; - DELAY(100); /* firmware needs a short delay here */ + ieee80211_ref_node(ni); + ieee80211_sta_join(ic, ni); + ieee80211_node_authorize(ni); - len = IEEE80211_ADDR_LEN; - ipw_read_table2(sc, IPW_INFO_CURRENT_BSSID, ni->ni_bssid, &len); - IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid); + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_notify_node_join(ic, ni, 1); break; case IEEE80211_S_INIT: @@ -896,62 +907,13 @@ ipw_read_prom_word(struct ipw_softc *sc, u_int8_t addr) return le16toh(val); } -static void -ipw_scan_result(struct ipw_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni; - u_int32_t i, cnt, off; - struct ipw_node ap; - - /* flush previously seen access points */ - ieee80211_free_allnodes(ic); - - cnt = ipw_read_table1(sc, IPW_INFO_APS_CNT); - off = ipw_read_table1(sc, IPW_INFO_APS_BASE); - - DPRINTF(("Found %u APs\n", cnt)); - - for (i = 0; i < cnt; i++) { - ipw_read_mem_1(sc, off, (u_int8_t *)&ap, sizeof ap); - off += sizeof ap; - -#ifdef IPW_DEBUG - if (ipw_debug >= 2) { - u_char *p = (u_char *)≈ - int j; - - printf("AP%u\n", i); - for (j = 0; j < sizeof ap; j++) - printf("%02x", *p++); - printf("\n"); - } -#endif - - ni = ieee80211_lookup_node(ic, ap.bssid, - &ic->ic_channels[ap.chan]); - if (ni != NULL) - continue; - - ni = ieee80211_alloc_node(ic, ap.bssid); - if (ni == NULL) - return; - - IEEE80211_ADDR_COPY(ni->ni_bssid, ap.bssid); - ni->ni_rssi = ap.rssi; - ni->ni_intval = le16toh(ap.intval); - ni->ni_capinfo = le16toh(ap.capinfo); - ni->ni_chan = &ic->ic_channels[ap.chan]; - ni->ni_esslen = ap.esslen; - bcopy(ap.essid, ni->ni_essid, IEEE80211_NWID_LEN); - } -} - static void ipw_command_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) { struct ipw_cmd *cmd; + bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); + cmd = mtod(sbuf->m, struct ipw_cmd *); DPRINTFN(2, ("RX!CMD!%u!%u!%u!%u!%u\n", @@ -967,6 +929,8 @@ ipw_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) struct ieee80211com *ic = &sc->sc_ic; u_int32_t state; + bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); + state = le32toh(*mtod(sbuf->m, u_int32_t *)); DPRINTFN(2, ("RX!NEWSTATE!%u\n", state)); @@ -980,10 +944,13 @@ ipw_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) /* don't leave run state on background scan */ if (ic->ic_state != IEEE80211_S_RUN) ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + + ic->ic_flags |= IEEE80211_F_SCAN; break; case IPW_STATE_SCAN_COMPLETE: - ipw_scan_result(sc); + ieee80211_notify_scan_done(ic); + ic->ic_flags &= ~IEEE80211_F_SCAN; break; case IPW_STATE_ASSOCIATION_LOST: @@ -991,19 +958,56 @@ ipw_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) break; case IPW_STATE_RADIO_DISABLED: - sc->sc_ic.ic_if.if_flags &= ~IFF_UP; + ic->ic_if.if_flags &= ~IFF_UP; ipw_stop(sc); break; } } +/* + * XXX: Hack to set the current channel to the value advertised in beacons or + * probe responses. Only used during AP detection. + */ +static void +ipw_fix_channel(struct ieee80211com *ic, struct mbuf *m) +{ + struct ieee80211_frame *wh; + uint8_t subtype; + uint8_t *frm, *efrm; + + wh = mtod(m, struct ieee80211_frame *); + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return; + + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && + subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) + return; + + frm = (uint8_t *)(wh + 1); + efrm = mtod(m, uint8_t *) + m->m_len; + + frm += 12; /* skip tstamp, bintval and capinfo fields */ + while (frm < efrm) { + if (*frm == IEEE80211_ELEMID_DSPARMS) +#if IEEE80211_CHAN_MAX < 255 + if (frm[2] <= IEEE80211_CHAN_MAX) +#endif + ic->ic_curchan = &ic->ic_channels[frm[2]]; + + frm += frm[1] + 2; + } +} + static void ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; - struct mbuf *m; + struct mbuf *mnew, *m; struct ieee80211_frame *wh; struct ieee80211_node *ni; bus_addr_t physaddr; @@ -1017,10 +1021,45 @@ ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, return; } + /* + * Try to allocate a new mbuf for this ring element and load it before + * processing the current mbuf. If the ring element cannot be loaded, + * drop the received packet and reuse the old mbuf. In the unlikely + * case that the old mbuf can't be reloaded either, explicitly panic. + */ + mnew = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); + if (mnew == NULL) { + ifp->if_ierrors++; + return; + } + + bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_dmat, sbuf->map); - /* Finalize mbuf */ + error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, mtod(mnew, void *), + MCLBYTES, ipw_dma_map_addr, &physaddr, 0); + if (error != 0) { + m_freem(mnew); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, + mtod(sbuf->m, void *), MCLBYTES, ipw_dma_map_addr, + &physaddr, 0); + if (error != 0) /* XXX very unlikely that it will fail... */ + panic("%s: could not load old rx mbuf", ifp->if_xname); + ifp->if_ierrors++; + return; + } + + /* + * New mbuf successfully loaded, update Rx ring and continue + * processing. + */ m = sbuf->m; + sbuf->m = mnew; + sbd->bd->physaddr = htole32(physaddr); + + /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = le32toh(status->len); @@ -1029,47 +1068,25 @@ ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, tap->wr_flags = 0; tap->wr_antsignal = status->rssi; - tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); - tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); + tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); bpf_ptap(sc->sc_drvbpf, m, tap, sc->sc_rxtap_len); } - wh = mtod(m, struct ieee80211_frame *); - - if (ic->ic_opmode != IEEE80211_M_STA) { - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) - ni = ieee80211_ref_node(ic->ic_bss); - } else - ni = ieee80211_ref_node(ic->ic_bss); + if (ic->ic_state == IEEE80211_S_SCAN) + ipw_fix_channel(ic, m); - /* Send the frame to the upper layer */ - ieee80211_input(ifp, m, ni, status->rssi, 0); - - if (ni == ic->ic_bss) - ieee80211_unref_node(&ni); - else - ieee80211_free_node(ic, ni); + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); - m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) { - if_printf(ifp, "could not allocate rx mbuf\n"); - sbuf->m = NULL; - return; - } + /* send the frame to the 802.11 layer */ + ieee80211_input(ic, m, ni, status->rssi, 0); - error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, mtod(m, void *), - MCLBYTES, ipw_dma_map_addr, &physaddr, 0); - if (error != 0) { - if_printf(ifp, "could not map rx DMA memory\n"); - m_freem(m); - sbuf->m = NULL; - return; - } + /* node is no longer needed */ + ieee80211_free_node(ni); - sbuf->m = m; - sbd->bd->physaddr = htole32(physaddr); + bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); } static void @@ -1099,9 +1116,6 @@ ipw_rx_intr(struct ipw_softc *sc) sbd = &sc->srbd_list[i]; sbuf = sbd->priv; - bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, - BUS_DMASYNC_POSTREAD); - switch (le16toh(status->code) & 0xf) { case IPW_STATUS_CODE_COMMAND: ipw_command_intr(sc, sbuf); @@ -1142,7 +1156,6 @@ ipw_rx_intr(struct ipw_softc *sc) static void ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd) { - struct ieee80211com *ic = &sc->sc_ic; struct ipw_soft_hdr *shdr; struct ipw_soft_buf *sbuf; @@ -1168,9 +1181,7 @@ ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd) SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); m_freem(sbuf->m); - - if (sbuf->ni != NULL && sbuf->ni != ic->ic_bss) - ieee80211_free_node(ic, sbuf->ni); + ieee80211_free_node(sbuf->ni); /* kill watchdog timer */ sc->sc_tx_timer = 0; @@ -1191,7 +1202,13 @@ ipw_tx_intr(struct ipw_softc *sc) r = CSR_READ_4(sc, IPW_CSR_TX_READ_INDEX); for (i = (sc->txold + 1) % IPW_NTBD; i != r; i = (i + 1) % IPW_NTBD) { - ipw_release_sbd(sc, &sc->stbd_list[i]); + struct ipw_soft_bd *sbd; + + sbd = &sc->stbd_list[i]; + if (sbd->type == IPW_SBD_TYPE_DATA) + ifp->if_opackets++; + + ipw_release_sbd(sc, sbd); sc->txfree++; } @@ -1303,8 +1320,10 @@ ipw_cmd(struct ipw_softc *sc, u_int32_t type, void *data, u_int32_t len) bus_dmamap_sync(sc->cmd_dmat, sc->cmd_map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->tbd_dmat, sc->tbd_map, BUS_DMASYNC_PREWRITE); + /* kick firmware */ sc->txcur = (sc->txcur + 1) % IPW_NTBD; sc->txfree--; + CSR_WRITE_4(sc, IPW_CSR_TX_WRITE_INDEX, sc->txcur); /* * This is kinda messy. Since we may be MP, a combination of @@ -1336,24 +1355,27 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) bus_addr_t physaddr; int error, i; - if (ic->ic_flags & IEEE80211_F_WEPON) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + wh = mtod(m0, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + if (ieee80211_crypto_encap(ic, ni, m0) == NULL) { + m_freem(m0); return ENOBUFS; - } + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } if (sc->sc_drvbpf != NULL) { struct ipw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; - tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); - tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); + tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len); } - wh = mtod(m0, struct ieee80211_frame *); - shdr = SLIST_FIRST(&sc->free_shdr); sbuf = SLIST_FIRST(&sc->free_sbuf); KASSERT(shdr != NULL && sbuf != NULL, ("empty sw hdr/buf pool")); @@ -1474,6 +1496,8 @@ ipw_start(struct ifnet *ifp) } for (;;) { + struct ether_header *eh; + m0 = ifq_poll(&ifp->if_snd); if (m0 == NULL) break; @@ -1483,18 +1507,31 @@ ipw_start(struct ifnet *ifp) } ifq_dequeue(&ifp->if_snd, m0); + if (m0->m_len < sizeof (struct ether_header) && + (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) + continue; + + eh = mtod(m0, struct ether_header *); + ni = ieee80211_find_txnode(ic, eh->ether_dhost); + if (ni == NULL) { + m_freem(m0); + continue; + } + BPF_MTAP(ifp, m0); - m0 = ieee80211_encap(ifp, m0, &ni); - if (m0 == NULL) + m0 = ieee80211_encap(ic, m0, ni); + if (m0 == NULL) { + ieee80211_free_node(ni); continue; + } if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0); if (ipw_tx_start(ifp, m0, ni) != 0) { - if (ni != NULL && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); + ifp->if_oerrors++; break; } @@ -1514,6 +1551,7 @@ ipw_watchdog(struct ifnet *ifp) if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); + ifp->if_oerrors++; ifp->if_flags &= ~IFF_UP; ipw_stop(sc); return; @@ -1521,7 +1559,7 @@ ipw_watchdog(struct ifnet *ifp) ifp->if_timer = 1; } - ieee80211_watchdog(ifp); + ieee80211_watchdog(&sc->sc_ic); } static int @@ -1530,7 +1568,6 @@ ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) struct ipw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct ifreq *ifr; - struct ieee80211req *ireq; int error = 0; switch (cmd) { @@ -1563,41 +1600,8 @@ ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) ipw_free_firmware(sc); break; - case SIOCG80211: - ireq = (struct ieee80211req *)data; - switch (ireq->i_type) { - case IEEE80211_IOC_AUTHMODE: - ireq->i_val = sc->authmode; - break; - - case IEEE80211_IOC_TXPOWER: - ireq->i_val = (CSR_READ_4(sc, IPW_CSR_IO) & - IPW_IO_RADIO_DISABLED) ? 0 : ic->ic_txpower; - break; - - default: - error = ieee80211_ioctl(ifp, cmd, data, cr); - } - break; - - case SIOCS80211: - /* only super-user can do that! */ - if ((error = suser(curthread)) != 0) - break; - - ireq = (struct ieee80211req *)data; - switch (ireq->i_type) { - case IEEE80211_IOC_AUTHMODE: - sc->authmode = ireq->i_val; - break; - - default: - error = ieee80211_ioctl(ifp, cmd, data, cr); - } - break; - default: - error = ieee80211_ioctl(ifp, cmd, data, cr); + error = ieee80211_ioctl(ic, cmd, data, cr); } if (error == ENETRESET) { @@ -1619,12 +1623,12 @@ ipw_stop_master(struct ipw_softc *sc) CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0); CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_STOP_MASTER); - for (ntries = 0; ntries < 5; ntries++) { + for (ntries = 0; ntries < 50; ntries++) { if (CSR_READ_4(sc, IPW_CSR_RST) & IPW_RST_MASTER_DISABLED) break; DELAY(10); } - if (ntries == 5) + if (ntries == 50) if_printf(&sc->sc_ic.ic_if, "timeout waiting for master\n"); CSR_WRITE_4(sc, IPW_CSR_RST, CSR_READ_4(sc, IPW_CSR_RST) | @@ -1854,7 +1858,7 @@ ipw_config(struct ipw_softc *sc) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; struct ipw_security security; - struct ieee80211_wepkey *k; + struct ieee80211_key *k; struct ipw_wep_key wepkey; struct ipw_scan_options options; struct ipw_configuration config; @@ -1983,7 +1987,7 @@ ipw_config(struct ipw_softc *sc) } bzero(&security, sizeof security); - security.authmode = (sc->authmode == IEEE80211_AUTH_SHARED) ? + security.authmode = (ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED) ? IPW_AUTH_SHARED : IPW_AUTH_OPEN; security.ciphers = htole32(IPW_CIPHER_NONE); DPRINTF(("Setting authmode to %u\n", security.authmode)); @@ -1992,16 +1996,16 @@ ipw_config(struct ipw_softc *sc) if (error != 0) return error; - if (ic->ic_flags & IEEE80211_F_WEPON) { - k = ic->ic_nw_keys; + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + k = ic->ic_crypto.cs_nw_keys; for (i = 0; i < IEEE80211_WEP_NKID; i++, k++) { - if (k->wk_len == 0) + if (k->wk_keylen == 0) continue; wepkey.idx = i; - wepkey.len = k->wk_len; + wepkey.len = k->wk_keylen; bzero(wepkey.key, sizeof wepkey.key); - bcopy(k->wk_key, wepkey.key, k->wk_len); + bcopy(k->wk_key, wepkey.key, k->wk_keylen); DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, wepkey.len)); error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY, &wepkey, @@ -2010,7 +2014,7 @@ ipw_config(struct ipw_softc *sc) return error; } - data = htole32(ic->ic_wep_txkey); + data = htole32(ic->ic_crypto.cs_def_txkey); DPRINTF(("Setting wep tx key index to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data, sizeof data); @@ -2018,15 +2022,26 @@ ipw_config(struct ipw_softc *sc) return error; } - data = htole32((ic->ic_flags & IEEE80211_F_WEPON) ? IPW_WEPON : 0); + data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0); DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data); if (error != 0) return error; +#if 0 + struct ipw_wpa_ie ie; + + memset(&ie, 0, sizeof ie); + ie.len = htole32(sizeof (struct ieee80211_ie_wpa)); + DPRINTF(("Setting wpa ie\n")); + error = ipw_cmd(sc, IPW_CMD_SET_WPA_IE, &ie, sizeof ie); + if (error != 0) + return error; +#endif + if (ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_HOSTAP) { - data = htole32(ic->ic_lintval); + data = htole32(ic->ic_bintval); DPRINTF(("Setting beacon interval to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_BEACON_INTERVAL, &data, sizeof data); @@ -2056,6 +2071,9 @@ ipw_init(void *priv) /* exit immediately if firmware has not been ioctl'd */ if (!(sc->flags & IPW_FLAG_FW_CACHED)) { + if (!(sc->flags & IPW_FLAG_FW_WARNED)) + if_printf(ifp, "Please load firmware\n"); + sc->flags |= IPW_FLAG_FW_WARNED; ifp->if_flags &= ~IFF_UP; return; } @@ -2137,6 +2155,7 @@ ipw_stop(void *priv) for (i = 0; i < IPW_NTBD; i++) ipw_release_sbd(sc, &sc->stbd_list[i]); + sc->sc_tx_timer = 0; ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); @@ -2147,14 +2166,17 @@ static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct ipw_softc *sc = arg1; + struct ifnet *ifp = &sc->sc_ic.ic_if; u_int32_t i, size, buf[256]; (void)arg2; /* silence WARNS == 6 */ (void)oidp; /* silence WARNS == 6 */ + lwkt_serialize_enter(ifp->if_serializer); + if (!(sc->flags & IPW_FLAG_FW_INITED)) { bzero(buf, sizeof buf); - return SYSCTL_OUT(req, buf, sizeof buf); + goto back; } CSR_WRITE_4(sc, IPW_CSR_AUTOINC_ADDR, sc->table1_base); @@ -2162,7 +2184,8 @@ ipw_sysctl_stats(SYSCTL_HANDLER_ARGS) size = min(CSR_READ_4(sc, IPW_CSR_AUTOINC_DATA), 256); for (i = 1; i < size; i++) buf[i] = MEM_READ_4(sc, CSR_READ_4(sc, IPW_CSR_AUTOINC_DATA)); - +back: + lwkt_serialize_exit(ifp->if_serializer); return SYSCTL_OUT(req, buf, sizeof buf); } @@ -2170,13 +2193,16 @@ static int ipw_sysctl_radio(SYSCTL_HANDLER_ARGS) { struct ipw_softc *sc = arg1; + struct ifnet *ifp = &sc->sc_ic.ic_if; int val; (void)arg2; /* silence WARNS == 6 */ (void)oidp; /* silence WARNS == 6 */ + lwkt_serialize_enter(ifp->if_serializer); val = !((sc->flags & IPW_FLAG_HAS_RADIO_SWITCH) && (CSR_READ_4(sc, IPW_CSR_IO) & IPW_IO_RADIO_DISABLED)); + lwkt_serialize_exit(ifp->if_serializer); return SYSCTL_OUT(req, &val, sizeof val); } diff --git a/sys/dev/netif/ipw/if_ipwvar.h b/sys/dev/netif/ipw/if_ipwvar.h index 18cf0e29f9..7281ba02e8 100644 --- a/sys/dev/netif/ipw/if_ipwvar.h +++ b/sys/dev/netif/ipw/if_ipwvar.h @@ -25,7 +25,8 @@ * SUCH DAMAGE. * * $Id: if_ipwvar.h,v 1.2.2.1 2005/01/13 20:01:04 damien Exp $ - * $DragonFly: src/sys/dev/netif/ipw/Attic/if_ipwvar.h,v 1.5 2005/11/29 17:15:56 dillon Exp $ + * $FreeBSD: src/sys/dev/ipw/if_ipwvar.h,v 1.3 2005/06/10 16:49:11 brooks Exp $ + * $DragonFly: src/sys/dev/netif/ipw/Attic/if_ipwvar.h,v 1.6 2006/05/18 13:51:45 sephe Exp $ */ struct ipw_firmware { @@ -98,14 +99,16 @@ struct ipw_softc { #define IPW_FLAG_FW_CACHED (1 << 0) #define IPW_FLAG_FW_INITED (1 << 1) #define IPW_FLAG_HAS_RADIO_SWITCH (1 << 2) +#define IPW_FLAG_FW_WARNED (1 << 3) + int irq_rid; + int mem_rid; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; - int authmode; int sc_tx_timer; bus_dma_tag_t tbd_dmat; @@ -147,6 +150,8 @@ struct ipw_softc { int txfree; u_int32_t rxcur; + int dwelltime; + struct bpf_if *sc_drvbpf; union { diff --git a/sys/dev/netif/iwi/if_iwi.c b/sys/dev/netif/iwi/if_iwi.c index 7ad6a43b25..e9c695fa30 100644 --- a/sys/dev/netif/iwi/if_iwi.c +++ b/sys/dev/netif/iwi/if_iwi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2005 + * Copyright (c) 2004-2006 * Damien Bergamini . * Copyright (c) 2004, 2005 * Andrew Atrens . @@ -28,15 +28,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/iwi/if_iwi.c,v 1.10 2005/12/31 14:07:59 sephe Exp $ + * $FreeBSD: src/sys/dev/iwi/if_iwi.c,v 1.8.2.6 2006/02/23 02:06:46 sam Exp $ + * $DragonFly: src/sys/dev/netif/iwi/if_iwi.c,v 1.11 2006/05/18 13:51:45 sephe Exp $ */ -#include "opt_inet.h" - -#include - /*- - * Intel(R) PRO/Wireless 2200BG/2915ABG driver + * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm */ @@ -54,11 +51,10 @@ #include #include #include -#include +#include #include #include -#include #include #include @@ -67,142 +63,109 @@ #include #include #include -#include #include #include #include #include #include +#include +#include + #include #include #include #include #include -#ifdef IPX -#include -#include -#endif - -#include -#include -#include -#include - -#include "if_iwireg.h" -#include "if_iwivar.h" +#include +#include #ifdef IWI_DEBUG -#define DPRINTF(x) if (sc->debug_level > 0) printf x -#define DPRINTFN(n, x) if (sc->debug_level >= (n)) printf x - +#define DPRINTF(x) do { if (iwi_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0) +int iwi_debug = 0; +SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level"); #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif -MODULE_DEPEND(iwi, pci, 1, 1, 1); -MODULE_DEPEND(iwi, wlan, 1, 1, 1); - -struct iwi_dump_buffer { - u_int32_t buf[128]; -}; - struct iwi_ident { - u_int16_t vendor; - u_int16_t device; + uint16_t vendor; + uint16_t device; const char *name; }; static const struct iwi_ident iwi_ident_table[] = { - { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG MiniPCI" }, - { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG MiniPCI" }, - { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG MiniPCI" }, + { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" }, + { 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" }, + { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" }, + { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" }, { 0, 0, NULL } }; -static const struct ieee80211_rateset iwi_rateset_11a = - { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; - -static const struct ieee80211_rateset iwi_rateset_11b = - { 4, { 2, 4, 11, 22 } }; - -static const struct ieee80211_rateset iwi_rateset_11g = - { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; - -static int iwi_dma_alloc(struct iwi_softc *); -static void iwi_release(struct iwi_softc *); -static int iwi_media_change(struct ifnet *); -static void iwi_media_status(struct ifnet *, struct ifmediareq *); -static u_int16_t iwi_read_prom_word(struct iwi_softc *, u_int8_t); -static int iwi_newstate(struct ieee80211com *, - enum ieee80211_state, int); -static void iwi_fix_channel(struct iwi_softc *, struct mbuf *); -static void iwi_frame_intr(struct iwi_softc *, - struct iwi_rx_buf *, int, struct iwi_frame *); -static void iwi_notification_intr(struct iwi_softc *, - struct iwi_notif *); -static void iwi_rx_intr(struct iwi_softc *); -static void iwi_tx_intr(struct iwi_softc *); -static void iwi_intr(void *); -static void iwi_dma_map_buf(void *, bus_dma_segment_t *, int, - bus_size_t, int); -static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); -static int iwi_cmd(struct iwi_softc *, u_int8_t, void *, u_int8_t, - int); -static int iwi_tx_start(struct ifnet *, struct mbuf *, - struct ieee80211_node *); -static void iwi_start(struct ifnet *); -static void iwi_watchdog(struct ifnet *); -static int iwi_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *cr); -static void iwi_stop_master(struct iwi_softc *); -static int iwi_reset(struct iwi_softc *); -static int iwi_load_ucode(struct iwi_softc *, void *, int); -static int iwi_load_firmware(struct iwi_softc *, void *, int); -static int iwi_cache_firmware(struct iwi_softc *, void *, int); -static void iwi_free_firmware(struct iwi_softc *); -static int iwi_config(struct iwi_softc *); -static int iwi_scan(struct iwi_softc *); -static int iwi_auth_and_assoc(struct iwi_softc *); -static void iwi_init(void *); -static void iwi_stop(void *); -static void iwi_dump_fw_event_log(struct iwi_softc *sc); -static void iwi_dump_fw_error_log(struct iwi_softc *sc); -static u_int8_t iwi_find_station(struct iwi_softc *sc, u_int8_t *mac); -static int8_t iwi_cache_station(struct iwi_softc *sc, u_int8_t *mac); -static int iwi_adapter_config(struct iwi_softc *sc, int is_a, int cmd_wait); - -static int iwi_sysctl_bt_coexist(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_bg_autodetect(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_cts_to_self(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_antenna_diversity(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_dump_logs(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_neg_best_rates_first(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_disable_unicast_decryption(SYSCTL_HANDLER_ARGS); -static int iwi_sysctl_disable_multicast_decryption(SYSCTL_HANDLER_ARGS); - -static __inline u_int8_t MEM_READ_1(struct iwi_softc *sc, u_int32_t addr) -{ - CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); - return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); -} - -static __inline u_int32_t MEM_READ_4(struct iwi_softc *sc, u_int32_t addr) -{ - CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); - return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); -} - -static int iwi_probe(device_t); -static int iwi_attach(device_t); -static int iwi_detach(device_t); -static int iwi_shutdown(device_t); -static int iwi_suspend(device_t); -static int iwi_resume(device_t); +static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); +static void iwi_dma_map_mbuf(void *, bus_dma_segment_t *, int, bus_size_t, + int); +static int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *, + int); +static void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); +static void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); +static int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *, + int, bus_addr_t, bus_addr_t); +static void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); +static void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); +static int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *, + int); +static void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); +static void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); +static struct ieee80211_node *iwi_node_alloc(struct ieee80211_node_table *); +static void iwi_node_free(struct ieee80211_node *); +static int iwi_media_change(struct ifnet *); +static void iwi_media_status(struct ifnet *, struct ifmediareq *); +static int iwi_newstate(struct ieee80211com *, enum ieee80211_state, int); +static int iwi_wme_update(struct ieee80211com *); +static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); +static void iwi_fix_channel(struct ieee80211com *, struct mbuf *); +static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, + struct iwi_frame *); +static void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *); +static void iwi_rx_intr(struct iwi_softc *); +static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); +static void iwi_intr(void *); +static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t, int); +static void iwi_write_ibssnode(struct iwi_softc *, const struct iwi_node *); +static int iwi_tx_start(struct ifnet *, struct mbuf *, + struct ieee80211_node *, int); +static void iwi_start(struct ifnet *); +static void iwi_watchdog(struct ifnet *); +static int iwi_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); +static void iwi_stop_master(struct iwi_softc *); +static int iwi_reset(struct iwi_softc *); +static int iwi_load_ucode(struct iwi_softc *, void *, int); +static int iwi_load_firmware(struct iwi_softc *, void *, int); +static int iwi_cache_firmware(struct iwi_softc *, void *); +static void iwi_free_firmware(struct iwi_softc *); +static int iwi_config(struct iwi_softc *); +static int iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *); +static int iwi_scan(struct iwi_softc *); +static int iwi_auth_and_assoc(struct iwi_softc *); +static void iwi_init(void *); +static void iwi_stop(void *); +static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS); + +static int iwi_probe(device_t); +static int iwi_attach(device_t); +static int iwi_detach(device_t); +static int iwi_shutdown(device_t); +static int iwi_suspend(device_t); +static int iwi_resume(device_t); + +static int iwi_alloc_ibss_node(struct iwi_softc *); +static void iwi_free_ibss_node(struct iwi_softc *, int); static device_method_t iwi_methods[] = { /* Device interface */ @@ -219,24 +182,38 @@ static device_method_t iwi_methods[] = { static driver_t iwi_driver = { "iwi", iwi_methods, - sizeof (struct iwi_softc), - 0, /* baseclasses */ - 0, /* refs */ - 0 /* ops */ + sizeof (struct iwi_softc) }; static devclass_t iwi_devclass; DRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0); +MODULE_DEPEND(iwi, pci, 1, 1, 1); +MODULE_DEPEND(iwi, wlan, 1, 1, 1); + +/* + * Supported rates for 802.11a/b/g modes (in 500Kbps unit). + */ +static const struct ieee80211_rateset iwi_rateset_11a = + { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; + +static const struct ieee80211_rateset iwi_rateset_11b = + { 4, { 2, 4, 11, 22 } }; + +static const struct ieee80211_rateset iwi_rateset_11g = + { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; + static int iwi_probe(device_t dev) { const struct iwi_ident *ident; + uint16_t vid, did; + vid = pci_get_vendor(dev); + did = pci_get_device(dev); for (ident = iwi_ident_table; ident->name != NULL; ident++) { - if (pci_get_vendor(dev) == ident->vendor && - pci_get_device(dev) == ident->device) { + if (vid == ident->vendor && did == ident->device) { device_set_desc(dev, ident->name); return 0; } @@ -244,59 +221,6 @@ iwi_probe(device_t dev) return ENXIO; } -static void -iwi_fw_monitor(void *arg) -{ - struct iwi_softc *sc = (struct iwi_softc *)arg; - struct ifnet *ifp = &sc->sc_ic.ic_if; - int error, boff; - - for ( ;; ) { - crit_enter(); - tsleep_interlock(IWI_FW_WAKE_MONITOR(sc)); - error = tsleep(IWI_FW_WAKE_MONITOR(sc), 0, "iwifwm", 0 ); - crit_exit(); - - if ( error == 0 ) { - if ( sc->flags & IWI_FLAG_EXIT ) { - sc->flags &= ~( IWI_FLAG_EXIT ); - break; - } else if ( sc->flags & IWI_FLAG_RESET ) { - device_printf(sc->sc_dev, "firmware reset\n"); - for ( boff = 1; sc->flags & IWI_FLAG_RESET ; boff++ ) { - if ( sc->debug_level > 0 ) - iwi_dump_fw_error_log(sc); - lwkt_serialize_enter(ifp->if_serializer); - iwi_init(sc); - lwkt_serialize_exit(ifp->if_serializer); - if ((sc->flags & IWI_FLAG_FW_INITED)) - sc->flags &= ~( IWI_FLAG_RESET ); - crit_enter(); - tsleep_interlock(IWI_FW_CMD_ACKED(sc)); - error = tsleep(IWI_FW_CMD_ACKED(sc), 0, - "iwirun", boff * hz ); - crit_exit(); - } - } - } - } - wakeup(IWI_FW_MON_EXIT(sc)); - kthread_exit(); -} - -static int -iwi_start_fw_monitor_thread( struct iwi_softc *sc ) -{ - if (kthread_create(iwi_fw_monitor, sc, &sc->event_thread, - "%s%d:fw-monitor", device_get_name(sc->sc_dev), - device_get_unit(sc->sc_dev))) { - device_printf (sc->sc_dev, - "unable to create firmware monitor thread.\n"); - return -1; - } - return 0; -} - /* Base Address Register */ #define IWI_PCI_BAR0 0x10 @@ -306,10 +230,11 @@ iwi_attach(device_t dev) struct iwi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; - u_int16_t val; - int error, rid, i; + uint16_t val; + int error, i; sc->sc_dev = dev; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " @@ -322,68 +247,122 @@ iwi_attach(device_t dev) /* enable bus-mastering */ pci_enable_busmaster(dev); - sc->num_stations = 0; - - /* map the register window */ - rid = IWI_PCI_BAR0; - sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + sc->mem_rid = IWI_PCI_BAR0; + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, + RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); - error = ENXIO; - goto fail; + return ENXIO; } - sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); - rid = 0; - sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | - RF_SHAREABLE); + sc->irq_rid = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, + RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); - error = ENXIO; goto fail; } - error = iwi_reset(sc); - if (error != 0) { + if (iwi_reset(sc) != 0) { device_printf(dev, "could not reset adapter\n"); goto fail; } - if (iwi_start_fw_monitor_thread(sc)) { - device_printf(dev, "could not start f/w reset thread\n"); - error = ENXIO; + /* + * Allocate rings. + */ + error = iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT); + if (error != 0) { + device_printf(dev, "could not allocate Cmd ring\n"); + goto fail; + } + + error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT, + IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX); + if (error != 0) { + device_printf(dev, "could not allocate Tx ring 1\n"); + goto fail; + } + + error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT, + IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX); + if (error != 0) { + device_printf(dev, "could not allocate Tx ring 2\n"); goto fail; } - error = iwi_dma_alloc(sc); + error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT, + IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX); if (error != 0) { - device_printf(dev, "could not allocate DMA resources\n"); + device_printf(dev, "could not allocate Tx ring 3\n"); goto fail; } - ic->ic_phytype = IEEE80211_T_OFDM; - ic->ic_opmode = IEEE80211_M_STA; - ic->ic_state = IEEE80211_S_INIT; + error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT, + IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX); + if (error != 0) { + device_printf(dev, "could not allocate Tx ring 4\n"); + goto fail; + } + + error = iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT); + if (error != 0) { + device_printf(dev, "could not allocate Rx ring\n"); + goto fail; + } + + sysctl_ctx_init(&sc->sysctl_ctx); + sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), + OID_AUTO, + device_get_nameunit(dev), + CTLFLAG_RD, + 0, ""); + if (sc->sysctl_tree == NULL) { + device_printf(dev, "sysctl add node failed\n"); + error = EIO; + goto fail; + } + + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = iwi_init; + ifp->if_ioctl = iwi_ioctl; + ifp->if_start = iwi_start; + ifp->if_watchdog = iwi_watchdog; + ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); + ifq_set_ready(&ifp->if_snd); + + ic->ic_wme.wme_update = iwi_wme_update; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; /* set device capabilities */ - ic->ic_caps = IEEE80211_C_IBSS | IEEE80211_C_PMGT | IEEE80211_C_WEP | - IEEE80211_C_TXPMGT | IEEE80211_C_SHPREAMBLE; + ic->ic_caps = + IEEE80211_C_IBSS | /* IBSS mode supported */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_WEP | /* WEP */ + IEEE80211_C_WPA | /* 802.11i */ + IEEE80211_C_WME; /* 802.11e */ /* read MAC address from EEPROM */ val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); - ic->ic_myaddr[0] = val >> 8; - ic->ic_myaddr[1] = val & 0xff; + ic->ic_myaddr[0] = val & 0xff; + ic->ic_myaddr[1] = val >> 8; val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); - ic->ic_myaddr[2] = val >> 8; - ic->ic_myaddr[3] = val & 0xff; + ic->ic_myaddr[2] = val & 0xff; + ic->ic_myaddr[3] = val >> 8; val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); - ic->ic_myaddr[4] = val >> 8; - ic->ic_myaddr[5] = val & 0xff; + ic->ic_myaddr[4] = val & 0xff; + ic->ic_myaddr[5] = val >> 8; - if (pci_get_device(dev) != 0x4220) { - /* set supported .11a rates */ + if (pci_get_device(dev) >= 0x4223) { + /* set supported .11a rates (2915ABG only) */ ic->ic_sup_rates[IEEE80211_MODE_11A] = iwi_rateset_11a; /* set supported .11a channels */ @@ -412,27 +391,15 @@ iwi_attach(device_t dev) IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; } - /* default to authmode OPEN */ - sc->authmode = IEEE80211_AUTH_OPEN; - - /* IBSS channel undefined for now */ - ic->ic_ibss_chan = &ic->ic_channels[0]; - - ifp->if_softc = sc; - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_init = iwi_init; - ifp->if_ioctl = iwi_ioctl; - ifp->if_start = iwi_start; - ifp->if_watchdog = iwi_watchdog; - ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); - ifq_set_ready(&ifp->if_snd); - - ieee80211_ifattach(ifp); + ieee80211_ifattach(ic); + /* override default methods */ + ic->ic_node_alloc = iwi_node_alloc; + sc->sc_node_free = ic->ic_node_free; + ic->ic_node_free = iwi_node_free; /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = iwi_newstate; - ieee80211_media_init(ifp, iwi_media_change, iwi_media_status); + ieee80211_media_init(ic, iwi_media_change, iwi_media_status); bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf); @@ -446,361 +413,407 @@ iwi_attach(device_t dev) sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT); /* - * Add sysctl knobs - * - * use -1 to indicate 'default / not set' + * Add a few sysctl knobs. */ - - sc->enable_bg_autodetect = -1; - sc->enable_bt_coexist = -1; - sc->enable_cts_to_self = -1; - sc->antenna_diversity = -1; - sc->enable_neg_best_first = -1; - sc->disable_unicast_decryption = -1; - sc->disable_multicast_decryption = -1; - - sysctl_ctx_init(&sc->sysctl_ctx); - sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, - SYSCTL_STATIC_CHILDREN(_hw), - OID_AUTO, - device_get_nameunit(dev), - CTLFLAG_RD, - 0, ""); - - if (sc->sysctl_tree == NULL) { - device_printf(dev, "sysctl add node failed\n"); - error = EIO; - goto fail2; - } - - SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), - OID_AUTO, "debug", CTLFLAG_RW, &sc->debug_level, 0, - "Set driver debug level (0 = off)"); - - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), - OID_AUTO, "cts_to_self", CTLTYPE_INT|CTLFLAG_RW, - (void *)sc, 0, iwi_sysctl_cts_to_self, "I", - "Enable cts to self [0 = Off] [1 = On] [-1 = Auto]" ); - - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), - OID_AUTO, "antenna_diversity", CTLTYPE_INT|CTLFLAG_RW, - (void *)sc, 0, iwi_sysctl_antenna_diversity, - "I", "Set antenna diversity [0 = Both] " - "[1 = Antenna A] [3 = Antenna B] [-1 = Auto]" ); + sc->dwelltime = 100; + sc->bluetooth = 1; + sc->antenna = 0; SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), - OID_AUTO, "bluetooth_coexist", CTLTYPE_INT|CTLFLAG_RW, - (void *)sc, 0, iwi_sysctl_bt_coexist, - "I", "Enable bluetooth coexistence heuristics " - "[0 = Off] [1 = On] [-1 = Auto]" ); + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "radio", + CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", + "radio transmitter switch state (0=off, 1=on)"); SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), - OID_AUTO, "bg_autodetect", CTLTYPE_INT|CTLFLAG_RW, - (void *)sc, 0, iwi_sysctl_bg_autodetect, - "I", "Set b/g autodetect [0 = Off] [1 = On] [-1 = Auto]" ); + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "stats", + CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S", + "statistics"); + SYSCTL_ADD_INT(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "dwell", + CTLFLAG_RW, &sc->dwelltime, 0, + "channel dwell time (ms) for AP/station scanning"); - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "radio", - CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", - "Radio transmitter switch"); + SYSCTL_ADD_INT(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "bluetooth", + CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence"); - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "stats", - CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, - "S,iwi_dump_buffer", "statistics"); - - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), - OID_AUTO, "firmware_logs", CTLTYPE_INT|CTLFLAG_RW, - (void *)sc, 0, iwi_sysctl_dump_logs, "I", "Dump firmware logs"); - - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, - "neg_best_rates_first", - CTLTYPE_INT | CTLFLAG_RW, sc, 0, - iwi_sysctl_neg_best_rates_first, "I", - "Negotiate highest rates first."); - - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, - "disable_unicast_decrypt", - CTLTYPE_INT | CTLFLAG_RW, sc, 0, - iwi_sysctl_disable_unicast_decryption, "I", - "Disable unicast decryption."); - - SYSCTL_ADD_PROC(&sc->sysctl_ctx, - SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, - "disable_multicast_decrypt", - CTLTYPE_INT | CTLFLAG_RW, sc, 0, - iwi_sysctl_disable_multicast_decryption, "I", - "Disable multicast decryption."); + SYSCTL_ADD_INT(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "antenna", + CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)"); /* - * Hook our interrupt after all initialization is complete + * Hook our interrupt after all initialization is complete. */ - error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE, - iwi_intr, sc, &sc->sc_ih, ifp->if_serializer); + error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE, iwi_intr, sc, + &sc->sc_ih, ifp->if_serializer); if (error != 0) { device_printf(dev, "could not set up interrupt\n"); - goto fail2; + + bpfdetach(ifp); + ieee80211_ifdetach(ic); + goto fail; } - return 0; + if (bootverbose) + ieee80211_announce(ic); -fail2: - bpfdetach(ifp); - ieee80211_ifdetach(ifp); + return 0; fail: iwi_detach(dev); - return error; + return ENXIO; } static int iwi_detach(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); - struct ifnet *ifp = &sc->sc_ic.ic_if; - - sc->flags |= IWI_FLAG_EXIT; - wakeup(IWI_FW_WAKE_MONITOR(sc)); /* Stop firmware monitor. */ - - tsleep(IWI_FW_MON_EXIT(sc), 0, "iwiexi", 10 * hz); + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = ic->ic_ifp; if (device_is_attached(dev)) { lwkt_serialize_enter(ifp->if_serializer); + iwi_stop(sc); - iwi_free_firmware(sc); bus_teardown_intr(dev, sc->irq, sc->sc_ih); + iwi_free_firmware(sc); + lwkt_serialize_exit(ifp->if_serializer); bpfdetach(ifp); - ieee80211_ifdetach(ifp); + ieee80211_ifdetach(ic); } - if (sc->sysctl_tree) { - sysctl_ctx_free(&sc->sysctl_ctx); - sc->sysctl_tree = 0; - } + iwi_free_cmd_ring(sc, &sc->cmdq); + iwi_free_tx_ring(sc, &sc->txq[0]); + iwi_free_tx_ring(sc, &sc->txq[1]); + iwi_free_tx_ring(sc, &sc->txq[2]); + iwi_free_tx_ring(sc, &sc->txq[3]); + iwi_free_rx_ring(sc, &sc->rxq); if (sc->irq != NULL) - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); - if (sc->mem != NULL) { - bus_release_resource(dev, SYS_RES_MEMORY, IWI_PCI_BAR0, - sc->mem); - } + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); - iwi_release(sc); + if (sc->sysctl_tree != NULL) + sysctl_ctx_free(&sc->sysctl_ctx); return 0; } +static void +iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + if (error != 0) + return; + + KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + + *(bus_addr_t *)arg = segs[0].ds_addr; +} + static int -iwi_dma_alloc(struct iwi_softc *sc) +iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) { - int i, error; + int error; - error = bus_dma_tag_create(NULL, /* parent */ - 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, - NULL, NULL, - MAXBSIZE, 128, - BUS_SPACE_MAXSIZE_32BIT, - BUS_DMA_ALLOCNOW, - &sc->iwi_parent_tag ); + ring->count = count; + ring->queued = 0; + ring->cur = ring->next = 0; + + error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_CMD_DESC_SIZE, 1, + count * IWI_CMD_DESC_SIZE, 0, &ring->desc_dmat); if (error != 0) { - device_printf(sc->sc_dev, "could not create parent tag\n"); + device_printf(sc->sc_dev, "could not create desc DMA tag\n"); goto fail; } - /* - * Allocate and map Tx ring - */ - error = bus_dma_tag_create(sc->iwi_parent_tag, 4, 0, BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, - sizeof (struct iwi_tx_desc) * IWI_TX_RING_SIZE, 1, - sizeof (struct iwi_tx_desc) * IWI_TX_RING_SIZE, - BUS_DMA_ALLOCNOW, &sc->tx_ring_dmat); + + error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &ring->desc_map); if (error != 0) { - device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); + device_printf(sc->sc_dev, "could not allocate DMA memory\n"); goto fail; } - error = bus_dmamem_alloc(sc->tx_ring_dmat,(void **) &sc->tx_desc, - BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx_ring_map); + error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, + count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate tx ring DMA memory\n"); - goto fail; + device_printf(sc->sc_dev, "could not load desc DMA map\n"); + bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); + ring->desc = NULL; + return error; } - error = bus_dmamap_load(sc->tx_ring_dmat, sc->tx_ring_map, - sc->tx_desc, sizeof (struct iwi_tx_desc) * IWI_TX_RING_SIZE, - iwi_dma_map_addr, &sc->tx_ring_pa, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not load tx ring DMA map\n"); - goto fail; + return 0; + +fail: iwi_free_cmd_ring(sc, ring); + return error; +} + +static void +iwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) +{ + ring->queued = 0; + ring->cur = ring->next = 0; +} + +static void +iwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) +{ + if (ring->desc != NULL) { + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->desc_dmat, ring->desc_map); + bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); + ring->desc = NULL; } - /* - * Allocate and map command ring - */ - error = bus_dma_tag_create(sc->iwi_parent_tag, 4, 0, BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, - sizeof (struct iwi_cmd_desc) * IWI_CMD_RING_SIZE, 1, - sizeof (struct iwi_cmd_desc) * IWI_CMD_RING_SIZE, - BUS_DMA_ALLOCNOW, - &sc->cmd_ring_dmat); + if (ring->desc_dmat != NULL) { + bus_dma_tag_destroy(ring->desc_dmat); + ring->desc_dmat = NULL; + } +} + +static int +iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, + bus_addr_t csr_ridx, bus_addr_t csr_widx) +{ + int i, error; + + ring->count = count; + ring->queued = 0; + ring->cur = ring->next = 0; + ring->csr_ridx = csr_ridx; + ring->csr_widx = csr_widx; + + error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_TX_DESC_SIZE, 1, + count * IWI_TX_DESC_SIZE, 0, &ring->desc_dmat); if (error != 0) { - device_printf(sc->sc_dev, - "could not create command ring DMA tag\n"); + device_printf(sc->sc_dev, "could not create desc DMA tag\n"); goto fail; } - error = bus_dmamem_alloc(sc->cmd_ring_dmat, (void **)&sc->cmd_desc, - BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->cmd_ring_map); + error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &ring->desc_map); if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate command ring DMA memory\n"); + device_printf(sc->sc_dev, "could not allocate DMA memory\n"); goto fail; } - error = bus_dmamap_load(sc->cmd_ring_dmat, sc->cmd_ring_map, - sc->cmd_desc, sizeof (struct iwi_cmd_desc) * IWI_CMD_RING_SIZE, - iwi_dma_map_addr, &sc->cmd_ring_pa, 0); + error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, + count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); if (error != 0) { - device_printf(sc->sc_dev, - "could not load command ring DMA map\n"); + device_printf(sc->sc_dev, "could not load desc DMA map\n"); + + bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); + ring->desc = NULL; goto fail; } - /* - * Allocate Tx buffers DMA maps - */ - error = bus_dma_tag_create(sc->iwi_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWI_MAX_NSEG, MCLBYTES, - BUS_DMA_ALLOCNOW, &sc->tx_buf_dmat); + ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF, + M_WAITOK | M_ZERO); + + error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWI_MAX_NSEG - 2, + MCLBYTES, 0, &ring->data_dmat); if (error != 0) { - device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); + device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } - for (i = 0; i < IWI_TX_RING_SIZE; i++) { - error = bus_dmamap_create(sc->tx_buf_dmat, 0, - &sc->tx_buf[i].map); + for (i = 0; i < count; i++) { + error = bus_dmamap_create(ring->data_dmat, 0, + &ring->data[i].map); if (error != 0) { - device_printf(sc->sc_dev, - "could not create tx buf DMA map"); + device_printf(sc->sc_dev, "could not create DMA map\n"); goto fail; } } - /* - * Allocate and map Rx buffers - */ - error = bus_dma_tag_create(sc->iwi_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, - BUS_DMA_ALLOCNOW, &sc->rx_buf_dmat); + return 0; + +fail: iwi_free_tx_ring(sc, ring); + return error; +} + +static void +iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) +{ + struct iwi_tx_data *data; + int i; + + for (i = 0; i < ring->count; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } + + ring->queued = 0; + ring->cur = ring->next = 0; +} + +static void +iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) +{ + struct iwi_tx_data *data; + int i; + + if (ring->desc != NULL) { + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->desc_dmat, ring->desc_map); + bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); + ring->desc = NULL; + } + + if (ring->desc_dmat != NULL) { + bus_dma_tag_destroy(ring->desc_dmat); + ring->desc_dmat = NULL; + } + + if (ring->data != NULL) { + for (i = 0; i < ring->count; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + + if (data->map != NULL) { + bus_dmamap_destroy(ring->data_dmat, data->map); + data->map = NULL; + } + } + + free(ring->data, M_DEVBUF); + ring->data = NULL; + } + + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } +} + +static int +iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count) +{ + struct iwi_rx_data *data; + int i, error; + + ring->count = count; + ring->cur = 0; + + ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF, + M_WAITOK | M_ZERO); + + error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, + &ring->data_dmat); if (error != 0) { - device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); + device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } - for (i = 0; i < IWI_RX_RING_SIZE; i++) { + for (i = 0; i < count; i++) { + data = &ring->data[i]; - error = bus_dmamap_create(sc->rx_buf_dmat, 0, - &sc->rx_buf[i].map); + error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { - device_printf(sc->sc_dev, - "could not create rx buf DMA map"); + device_printf(sc->sc_dev, "could not create DMA map\n"); goto fail; } - sc->rx_buf[i].m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); - if (sc->rx_buf[i].m == NULL) { + data->m = m_getcl(MB_WAIT, MT_DATA, M_PKTHDR); + if (data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } - error = bus_dmamap_load(sc->rx_buf_dmat, sc->rx_buf[i].map, - mtod(sc->rx_buf[i].m, void *), MCLBYTES, iwi_dma_map_addr, - &sc->rx_buf[i].physaddr, 0); + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, + &data->physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load rx buf DMA map"); + + m_freem(data->m); + data->m = NULL; goto fail; } + + data->reg = IWI_CSR_RX_BASE + i * 4; } return 0; -fail: iwi_release(sc); +fail: iwi_free_rx_ring(sc, ring); return error; } static void -iwi_release(struct iwi_softc *sc) +iwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) { - int i; + ring->cur = 0; +} - if (sc->tx_ring_dmat != NULL) { - if (sc->tx_desc != NULL) { - bus_dmamap_sync(sc->tx_ring_dmat, sc->tx_ring_map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->tx_ring_dmat, sc->tx_ring_map); - bus_dmamem_free(sc->tx_ring_dmat, sc->tx_desc, - sc->tx_ring_map); - } - bus_dma_tag_destroy(sc->tx_ring_dmat); - } +static void +iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) +{ + struct iwi_rx_data *data; + int i; - if (sc->cmd_ring_dmat != NULL) { - if (sc->cmd_desc != NULL) { - bus_dmamap_sync(sc->cmd_ring_dmat, sc->cmd_ring_map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->cmd_ring_dmat, sc->cmd_ring_map); - bus_dmamem_free(sc->cmd_ring_dmat, sc->cmd_desc, - sc->cmd_ring_map); - } - bus_dma_tag_destroy(sc->cmd_ring_dmat); - } + if (ring->data != NULL) { + for (i = 0; i < ring->count; i++) { + data = &ring->data[i]; - if (sc->tx_buf_dmat != NULL) { - for (i = 0; i < IWI_TX_RING_SIZE; i++) { - if (sc->tx_buf[i].m != NULL) { - bus_dmamap_sync(sc->tx_buf_dmat, - sc->tx_buf[i].map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->tx_buf_dmat, - sc->tx_buf[i].map); - m_freem(sc->tx_buf[i].m); + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; } - bus_dmamap_destroy(sc->tx_buf_dmat, sc->tx_buf[i].map); - } - bus_dma_tag_destroy(sc->tx_buf_dmat); - } - if (sc->rx_buf_dmat != NULL) { - for (i = 0; i < IWI_RX_RING_SIZE; i++) { - if (sc->rx_buf[i].m != NULL) { - bus_dmamap_sync(sc->rx_buf_dmat, - sc->rx_buf[i].map, BUS_DMASYNC_POSTREAD); - bus_dmamap_unload(sc->rx_buf_dmat, - sc->rx_buf[i].map); - m_freem(sc->rx_buf[i].m); + if (data->map != NULL) { + bus_dmamap_destroy(ring->data_dmat, data->map); + data->map = NULL; } - bus_dmamap_destroy(sc->rx_buf_dmat, sc->rx_buf[i].map); } - bus_dma_tag_destroy(sc->rx_buf_dmat); + + free(ring->data, M_DEVBUF); + ring->data = NULL; } - if ( sc->iwi_parent_tag != NULL ) { - bus_dma_tag_destroy(sc->iwi_parent_tag); + + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; } } @@ -834,9 +847,10 @@ static int iwi_resume(device_t dev) { struct iwi_softc *sc = device_get_softc(dev); - struct ifnet *ifp = &sc->sc_ic.ic_if; + struct ifnet *ifp = sc->sc_ic.ic_ifp; lwkt_serialize_enter(ifp->if_serializer); + pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) { @@ -844,29 +858,60 @@ iwi_resume(device_t dev) if (ifp->if_flags & IFF_RUNNING) ifp->if_start(ifp); } + lwkt_serialize_exit(ifp->if_serializer); + return 0; } +static struct ieee80211_node * +iwi_node_alloc(struct ieee80211_node_table *nt) +{ + struct iwi_node *in; + + in = malloc(sizeof (struct iwi_node), M_80211_NODE, M_NOWAIT | M_ZERO); + if (in == NULL) + return NULL; + + in->in_station = -1; + + return &in->in_node; +} + +static void +iwi_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct iwi_softc *sc = ic->ic_ifp->if_softc; + struct iwi_node *in = (struct iwi_node *)ni; + + if (in->in_station != -1) + iwi_free_ibss_node(sc, in->in_station); + + sc->sc_node_free(ni); +} + static int iwi_media_change(struct ifnet *ifp) { struct iwi_softc *sc = ifp->if_softc; - int error = 0; + int error; + + ASSERT_SERIALIZED(ifp->if_serializer); error = ieee80211_media_change(ifp); - if (error != ENETRESET) { + if (error != ENETRESET) return error; - } - error = 0; /* clear ENETRESET */ - if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)){ + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) iwi_init(sc); - error = tsleep( IWI_FW_CMD_ACKED(sc), 0, "iwirun", hz ); - } - return error; + return 0; } +/* + * The firmware automatically adapts the transmit speed. We report its current + * value here. + */ static void iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) { @@ -874,7 +919,7 @@ iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) struct ieee80211com *ic = &sc->sc_ic; #define N(a) (sizeof (a) / sizeof (a[0])) static const struct { - u_int32_t val; + uint32_t val; int rate; } rates[] = { { IWI_RATE_DS1, 2 }, @@ -890,8 +935,8 @@ iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) { IWI_RATE_OFDM48, 96 }, { IWI_RATE_OFDM54, 108 }, }; - u_int32_t val, i; - int rate; + uint32_t val; + int rate, i; imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; @@ -902,7 +947,7 @@ iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) val = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); /* convert rate to 802.11 rate */ - for (i = 0; i < N(rates) && rates[i].val != val ; i++); + for (i = 0; i < N(rates) && rates[i].val != val; i++); rate = (i < N(rates)) ? rates[i].rate : 0; imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode); @@ -927,95 +972,139 @@ iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) } static int -iwi_disassociate( struct iwi_softc *sc ) -{ - sc->assoc.type = 2; /* DISASSOCIATE */ - return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &sc->assoc, sizeof sc->assoc, 0); -} - - -static int -iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg __unused) +iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { - struct iwi_softc *sc = ic->ic_softc; - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = ic->ic_ifp; + struct iwi_softc *sc = ifp->if_softc; + enum ieee80211_state ostate; + uint32_t tmp; - ASSERT_SERIALIZED(ifp->if_serializer); + ostate = ic->ic_state; switch (nstate) { case IEEE80211_S_SCAN: - if (sc->flags & IWI_FLAG_ASSOCIATED) { - sc->flags &= ~( IWI_FLAG_ASSOCIATED ); - iwi_disassociate(sc); - - crit_enter(); - tsleep_interlock(IWI_FW_DEASSOCIATED(sc)); - lwkt_serialize_exit(ifp->if_serializer); - tsleep(IWI_FW_DEASSOCIATED(sc), 0, "iwisca", hz ); - crit_exit(); - lwkt_serialize_enter(ifp->if_serializer); - } - if ( !(sc->flags & IWI_FLAG_SCANNING) && - !(sc->flags & IWI_FLAG_RF_DISABLED) ) { - iwi_scan(sc); - } + if (sc->flags & IWI_FLAG_SCANNING) + break; + + ieee80211_node_table_reset(&ic->ic_scan); + ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; + sc->flags |= IWI_FLAG_SCANNING; + iwi_scan(sc); break; case IEEE80211_S_AUTH: - if ( sc->flags & IWI_FLAG_ASSOCIATED ) { - sc->flags &= ~( IWI_FLAG_ASSOCIATED ); - iwi_disassociate(sc); - - crit_enter(); - tsleep_interlock(IWI_FW_DEASSOCIATED(sc)); - lwkt_serialize_exit(ifp->if_serializer); - tsleep(IWI_FW_DEASSOCIATED(sc), 0, "iwiaut", hz ); - crit_exit(); - lwkt_serialize_enter(ifp->if_serializer); - } - if ( iwi_auth_and_assoc(sc) != 0 ) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + iwi_auth_and_assoc(sc); break; case IEEE80211_S_RUN: - if (sc->flags & IWI_FLAG_SCAN_COMPLETE) { - sc->flags &= ~(IWI_FLAG_SCAN_COMPLETE); - if (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_MONITOR ) { - /* - * In IBSS mode, following an end_scan - * the ieee80211 stack state machine transitions - * straight to 'run' state. This is out of - * step with the firmware which requires - * an association first. Flip our state from - * RUN back to AUTH to allow us to tell the - * firmware to associate. - */ - ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); - } - } - break; + if (ic->ic_opmode == IEEE80211_M_IBSS) + iwi_auth_and_assoc(sc); + else if (ic->ic_opmode == IEEE80211_M_MONITOR) + iwi_set_chan(sc, ic->ic_ibss_chan); + + /* assoc led on */ + tmp = MEM_READ_4(sc, IWI_MEM_EVENT_CTL) & IWI_LED_MASK; + MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, tmp | IWI_LED_ASSOC); + + return sc->sc_newstate(ic, nstate, + IEEE80211_FC0_SUBTYPE_ASSOC_RESP); case IEEE80211_S_ASSOC: break; + case IEEE80211_S_INIT: - sc->flags &= ~( IWI_FLAG_SCANNING | IWI_FLAG_ASSOCIATED ); + sc->flags &= ~IWI_FLAG_SCANNING; + + if (ostate != IEEE80211_S_RUN) + break; + + /* assoc led off */ + tmp = MEM_READ_4(sc, IWI_MEM_EVENT_CTL) & IWI_LED_MASK; + MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, tmp & ~IWI_LED_ASSOC); break; } ic->ic_state = nstate; + return 0; } +/* + * WME parameters coming from IEEE 802.11e specification. These values are + * already declared in ieee80211_proto.c, but they are static so they can't + * be reused here. + */ +static const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = { + { 0, 3, 5, 7, 0 }, /* WME_AC_BE */ + { 0, 3, 5, 10, 0 }, /* WME_AC_BK */ + { 0, 2, 4, 5, 188 }, /* WME_AC_VI */ + { 0, 2, 3, 4, 102 } /* WME_AC_VO */ +}; + +static const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = { + { 0, 3, 4, 6, 0 }, /* WME_AC_BE */ + { 0, 3, 4, 10, 0 }, /* WME_AC_BK */ + { 0, 2, 3, 4, 94 }, /* WME_AC_VI */ + { 0, 2, 2, 3, 47 } /* WME_AC_VO */ +}; + +static int +iwi_wme_update(struct ieee80211com *ic) +{ +#define IWI_EXP2(v) htole16((1 << (v)) - 1) +#define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) + struct iwi_softc *sc = ic->ic_ifp->if_softc; + struct iwi_wme_params wme[3]; + const struct wmeParams *wmep; + int ac; + + /* + * We shall not override firmware default WME values if WME is not + * actually enabled. + */ + if (!(ic->ic_flags & IEEE80211_F_WME)) + return 0; + + for (ac = 0; ac < WME_NUM_AC; ac++) { + /* set WME values for current operating mode */ + wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; + wme[0].aifsn[ac] = wmep->wmep_aifsn; + wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); + wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); + wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); + wme[0].acm[ac] = wmep->wmep_acm; + + /* set WME values for CCK modulation */ + wmep = &iwi_wme_cck_params[ac]; + wme[1].aifsn[ac] = wmep->wmep_aifsn; + wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); + wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); + wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); + wme[1].acm[ac] = wmep->wmep_acm; + + /* set WME values for OFDM modulation */ + wmep = &iwi_wme_ofdm_params[ac]; + wme[2].aifsn[ac] = wmep->wmep_aifsn; + wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); + wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); + wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); + wme[2].acm[ac] = wmep->wmep_acm; + } + + DPRINTF(("Setting WME parameters\n")); + return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, wme, sizeof wme, 1); +#undef IWI_USEC +#undef IWI_EXP2 +} + /* * Read 16 bits at address 'addr' from the serial EEPROM. - * DON'T PLAY WITH THIS CODE UNLESS YOU KNOW *EXACTLY* WHAT YOU'RE DOING! */ -static u_int16_t -iwi_read_prom_word(struct iwi_softc *sc, u_int8_t addr) +static uint16_t +iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) { - u_int32_t tmp; - u_int16_t val; + uint32_t tmp; + uint16_t val; int n; /* Clock C once before the first command */ @@ -1060,7 +1149,7 @@ iwi_read_prom_word(struct iwi_softc *sc, u_int8_t addr) IWI_EEPROM_CTL(sc, 0); IWI_EEPROM_CTL(sc, IWI_EEPROM_C); - return be16toh(val); + return val; } /* @@ -1068,12 +1157,11 @@ iwi_read_prom_word(struct iwi_softc *sc, u_int8_t addr) * probe responses. Only used during AP detection. */ static void -iwi_fix_channel(struct iwi_softc *sc, struct mbuf *m) +iwi_fix_channel(struct ieee80211com *ic, struct mbuf *m) { - struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; - u_int8_t subtype; - u_int8_t *frm, *efrm; + uint8_t subtype; + uint8_t *frm, *efrm; wh = mtod(m, struct ieee80211_frame *); @@ -1083,88 +1171,91 @@ iwi_fix_channel(struct iwi_softc *sc, struct mbuf *m) subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && - subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) - return; - - /* - * Cache station entries from beacons and probes. - */ - if ( iwi_find_station(sc, wh->i_addr2) == 0xff ) - iwi_cache_station(sc, wh->i_addr2); + subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) + return; - frm = (u_int8_t *)(wh + 1); - efrm = mtod(m, u_int8_t *) + m->m_len; + frm = (uint8_t *)(wh + 1); + efrm = mtod(m, uint8_t *) + m->m_len; frm += 12; /* skip tstamp, bintval and capinfo fields */ -#if 0 - { /* XXX - debugging code */ - u_int8_t *ptr; - u_int32_t cnt; - printf("Frame -->"); - for ( ptr = frm, cnt = 0 ; ptr < efrm ; ptr++, cnt++ ) { - if ( cnt % 8 == 0 ) - printf("\n"); - printf("0x%-2.2x ", *ptr); - } - printf("<-- End Frame\n"); - } -#endif - while (frm < efrm) { if (*frm == IEEE80211_ELEMID_DSPARMS) #if IEEE80211_CHAN_MAX < 255 if (frm[2] <= IEEE80211_CHAN_MAX) #endif - ic->ic_bss->ni_chan = &ic->ic_channels[frm[2]]; + ic->ic_curchan = &ic->ic_channels[frm[2]]; - frm += frm[1] + 2; /* advance to the next tag */ + frm += frm[1] + 2; } } static void -iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_buf *buf, int i, +iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, struct iwi_frame *frame) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct mbuf *m; + struct ifnet *ifp = ic->ic_ifp; + struct mbuf *mnew, *m; struct ieee80211_frame *wh; struct ieee80211_node *ni; int error; - DPRINTFN(5, ("RX!DATA!%u!%u!%u\n", le16toh(frame->len), frame->chan, - frame->rssi_dbm)); + DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u\n", + le16toh(frame->len), frame->chan, frame->rssi_dbm)); + + if (le16toh(frame->len) < sizeof (struct ieee80211_frame)) + return; + + /* + * Try to allocate a new mbuf for this ring element and load it before + * processing the current mbuf. If the ring element cannot be loaded, + * drop the received packet and reuse the old mbuf. In the unlikely + * case that the old mbuf can't be reloaded either, explicitly panic. + */ + mnew = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); + if (mnew == NULL) { + ifp->if_ierrors++; + return; + } + + bus_dmamap_unload(sc->rxq.data_dmat, data->map); - if (le16toh(frame->len) < sizeof (struct ieee80211_frame_min) || - le16toh(frame->len) > MCLBYTES) { - device_printf(sc->sc_dev, "bad frame length\n"); + error = bus_dmamap_load(sc->rxq.data_dmat, data->map, + mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, + 0); + if (error != 0) { + m_freem(mnew); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(sc->rxq.data_dmat, data->map, + mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, + &data->physaddr, 0); + if (error != 0) { + /* very unlikely that it will fail... */ + panic("%s: could not load old rx mbuf", + device_get_name(sc->sc_dev)); + } + ifp->if_ierrors++; return; } - bus_dmamap_unload(sc->rx_buf_dmat, buf->map); + /* + * New mbuf successfully loaded, update Rx ring and continue + * processing. + */ + m = data->m; + data->m = mnew; + CSR_WRITE_4(sc, data->reg, data->physaddr); /* Finalize mbuf */ - m = buf->m; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + sizeof (struct iwi_frame) + le16toh(frame->len); m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); - wh = mtod(m, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - /* - * Hardware decrypts the frame itself but leaves the WEP bit - * set in the 802.11 header and don't remove the iv and crc - * fields - */ - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - bcopy(wh, (char *)wh + IEEE80211_WEP_IVLEN + - IEEE80211_WEP_KIDLEN, sizeof (struct ieee80211_frame)); - m_adj(m, IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN); - m_adj(m, -IEEE80211_WEP_CRCLEN); - wh = mtod(m, struct ieee80211_frame *); - } + if (ic->ic_state == IEEE80211_S_SCAN) + iwi_fix_channel(ic, m); if (sc->sc_drvbpf != NULL) { struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; @@ -1176,53 +1267,25 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_buf *buf, int i, tap->wr_chan_flags = htole16(ic->ic_channels[frame->chan].ic_flags); tap->wr_antsignal = frame->signal; - tap->wr_antnoise = frame->noise; tap->wr_antenna = frame->antenna; bpf_ptap(sc->sc_drvbpf, m, tap, sc->sc_rxtap_len); } - if (ic->ic_state == IEEE80211_S_SCAN) - iwi_fix_channel(sc, m); - - if (ic->ic_opmode != IEEE80211_M_STA) { - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) - ni = ieee80211_ref_node(ic->ic_bss); - } else - ni = ieee80211_ref_node(ic->ic_bss); - - /* Send the frame to the upper layer */ - ieee80211_input(ifp, m, ni, IWI_RSSIDBM2RAW(frame->rssi_dbm), 0); - - if (ni == ic->ic_bss) - ieee80211_unref_node(&ni); - else - ieee80211_free_node(ic, ni); - - buf->m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); - if (buf->m == NULL) { - device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); - return; - } + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); - error = bus_dmamap_load(sc->rx_buf_dmat, buf->map, mtod(buf->m, void *), - MCLBYTES, iwi_dma_map_addr, &buf->physaddr, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not load rx buf DMA map\n"); - m_freem(buf->m); - buf->m = NULL; - return; - } + /* send the frame to the 802.11 layer */ + ieee80211_input(ic, m, ni, frame->rssi_dbm, 0); - CSR_WRITE_4(sc, IWI_CSR_RX_BASE + i * 4, buf->physaddr); + /* node is no longer needed */ + ieee80211_free_node(ni); } static void iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct iwi_notif_scan_channel *chan; struct iwi_notif_scan_complete *scan; struct iwi_notif_authentication *auth; @@ -1232,7 +1295,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) case IWI_NOTIF_TYPE_SCAN_CHANNEL: chan = (struct iwi_notif_scan_channel *)(notif + 1); - DPRINTFN(2, ("Scan channel (%u)\n", chan->nchan)); + DPRINTFN(2, ("Scanning channel (%u)\n", chan->nchan)); break; case IWI_NOTIF_TYPE_SCAN_COMPLETE: @@ -1241,14 +1304,12 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, scan->status)); - sc->flags &= ~(IWI_FLAG_SCANNING); - sc->flags |= IWI_FLAG_SCAN_COMPLETE; - - if ( sc->flags & IWI_FLAG_SCAN_ABORT ) - sc->flags &= ~(IWI_FLAG_SCAN_ABORT); - else - ieee80211_end_scan(ifp); - wakeup(IWI_FW_SCAN_COMPLETED(sc)); + /* monitor mode uses scan to set the channel ... */ + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + sc->flags &= ~IWI_FLAG_SCANNING; + ieee80211_end_scan(ic); + } else + iwi_set_chan(sc, ic->ic_ibss_chan); break; case IWI_NOTIF_TYPE_AUTHENTICATION: @@ -1258,11 +1319,11 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) switch (auth->state) { case IWI_AUTHENTICATED: + ieee80211_node_authorize(ic->ic_bss); ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); break; case IWI_DEAUTHENTICATED: - ieee80211_begin_scan(ifp);/* not necessary */ break; default: @@ -1278,15 +1339,16 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) assoc->status)); switch (assoc->state) { + case IWI_AUTHENTICATED: + /* re-association, do nothing */ + break; + case IWI_ASSOCIATED: - sc->flags |= IWI_FLAG_ASSOCIATED; ieee80211_new_state(ic, IEEE80211_S_RUN, -1); break; case IWI_DEASSOCIATED: - sc->flags &= ~(IWI_FLAG_ASSOCIATED); - wakeup(IWI_FW_DEASSOCIATED(sc)); - ieee80211_begin_scan(ifp);/* probably not necessary */ + ieee80211_begin_scan(ic, 1); break; default: @@ -1295,44 +1357,31 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) } break; - case IWI_NOTIF_TYPE_CALIBRATION: - DPRINTFN(5, ("Notification calib (%u)\n", notif->type)); - break; - case IWI_NOTIF_TYPE_BEACON: - DPRINTFN(5, ("Notification beacon (%u)\n", notif->type)); - break; - case IWI_NOTIF_TYPE_NOISE: - DPRINTFN(5, ("Notification noise (%u)\n", notif->type)); - break; - default: - device_printf(sc->sc_dev, "unknown notification type %u\n", - notif->type); + DPRINTFN(5, ("Notification (%u)\n", notif->type)); } } static void iwi_rx_intr(struct iwi_softc *sc) { - struct iwi_rx_buf *buf; + struct iwi_rx_data *data; struct iwi_hdr *hdr; - u_int32_t r, i; - - r = CSR_READ_4(sc, IWI_CSR_RX_READ_INDEX); + uint32_t hw; - for (i = (sc->rx_cur + 1) % IWI_RX_RING_SIZE; i != r; - i = (i + 1) % IWI_RX_RING_SIZE) { + hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX); - buf = &sc->rx_buf[i]; + for (; sc->rxq.cur != hw;) { + data = &sc->rxq.data[sc->rxq.cur]; - bus_dmamap_sync(sc->rx_buf_dmat, buf->map, + bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); - hdr = mtod(buf->m, struct iwi_hdr *); + hdr = mtod(data->m, struct iwi_hdr *); switch (hdr->type) { case IWI_HDR_TYPE_FRAME: - iwi_frame_intr(sc, buf, i, + iwi_frame_intr(sc, data, sc->rxq.cur, (struct iwi_frame *)(hdr + 1)); break; @@ -1345,82 +1394,67 @@ iwi_rx_intr(struct iwi_softc *sc) device_printf(sc->sc_dev, "unknown hdr type %u\n", hdr->type); } + + DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); + + sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; } /* Tell the firmware what we have processed */ - sc->rx_cur = (r == 0) ? IWI_RX_RING_SIZE - 1 : r - 1; - CSR_WRITE_4(sc, IWI_CSR_RX_WRITE_INDEX, sc->rx_cur); + hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1; + CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw); } static void -iwi_tx_intr(struct iwi_softc *sc) +iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct iwi_tx_buf *buf; - u_int32_t r, i; - - r = CSR_READ_4(sc, IWI_CSR_TX1_READ_INDEX); -#if notyet - bus_dmamap_sync(sc->tx_ring_dmat, sc->tx_ring_map, BUS_DMASYNC_POSTWRITE); -#endif + struct ifnet *ifp = ic->ic_ifp; + struct iwi_tx_data *data; + uint32_t hw; - for (i = (sc->tx_old + 1) % IWI_TX_RING_SIZE; i != r; - i = (i + 1) % IWI_TX_RING_SIZE) { + hw = CSR_READ_4(sc, txq->csr_ridx); - buf = &sc->tx_buf[i]; + for (; txq->next != hw;) { + data = &txq->data[txq->next]; - bus_dmamap_sync(sc->tx_buf_dmat, buf->map, + bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->tx_buf_dmat, buf->map); - m_freem(buf->m); - buf->m = NULL; - if (buf->ni != ic->ic_bss) - ieee80211_free_node(ic, buf->ni); - buf->ni = NULL; + bus_dmamap_unload(txq->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + ieee80211_free_node(data->ni); + data->ni = NULL; - sc->tx_queued--; + DPRINTFN(15, ("tx done idx=%u\n", txq->next)); - /* kill watchdog timer */ - sc->sc_tx_timer = 0; - } + ifp->if_opackets++; - /* Remember what the firmware has processed */ - sc->tx_old = (r == 0) ? IWI_TX_RING_SIZE - 1 : r - 1; + txq->queued--; + txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; + } - /* Call start() since some buffer descriptors have been released */ + sc->sc_tx_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; - (*ifp->if_start)(ifp); + iwi_start(ifp); } static void iwi_intr(void *arg) { struct iwi_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - u_int32_t r; + uint32_t r; - if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) + r = CSR_READ_4(sc, IWI_CSR_INTR); + if (r == 0 || r == 0xffffffff) return; - /* Disable interrupts */ + /* disable interrupts */ CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); - DPRINTFN(8, ("INTR!0x%08x\n", r)); - - sc->flags &= ~(IWI_FLAG_RF_DISABLED); - - if (r & IWI_INTR_FATAL_ERROR) { - if (!(sc->flags & (IWI_FLAG_RESET | IWI_FLAG_EXIT))) { - sc->flags |= IWI_FLAG_RESET; - wakeup(IWI_FW_WAKE_MONITOR(sc)); - } - } - - if (r & IWI_INTR_PARITY_ERROR) { + if (r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)) { device_printf(sc->sc_dev, "fatal error\n"); - sc->sc_ic.ic_if.if_flags &= ~IFF_UP; + sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP; iwi_stop(sc); } @@ -1430,149 +1464,184 @@ iwi_intr(void *arg) } if (r & IWI_INTR_RADIO_OFF) { - DPRINTF(("radio transmitter off\n")); - sc->sc_ic.ic_if.if_flags &= ~IFF_UP; + DPRINTF(("radio transmitter turned off\n")); + sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP; iwi_stop(sc); - sc->flags |= IWI_FLAG_RF_DISABLED; } - if (r & IWI_INTR_RX_TRANSFER) - iwi_rx_intr(sc); - - if (r & IWI_INTR_CMD_TRANSFER) + if (r & IWI_INTR_CMD_DONE) wakeup(IWI_FW_CMD_ACKED(sc)); - if (r & IWI_INTR_TX1_TRANSFER) - iwi_tx_intr(sc); - - if (r & ~(IWI_HANDLED_INTR_MASK)) { - device_printf(sc->sc_dev, - "unhandled interrupt(s) INTR!0x%08x\n", - r & ~(IWI_HANDLED_INTR_MASK)); - } - - /* Acknowledge interrupts */ - CSR_WRITE_4(sc, IWI_CSR_INTR, r); - - /* Re-enable interrupts */ - CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); - - if ((ifp->if_flags & IFF_RUNNING) && !ifq_is_empty(&ifp->if_snd)) - iwi_start(ifp); -} - -struct iwi_dma_mapping { - bus_dma_segment_t segs[IWI_MAX_NSEG]; - int nseg; - bus_size_t mapsize; -}; - -static void -iwi_dma_map_buf(void *arg, bus_dma_segment_t *segs, int nseg, - bus_size_t mapsize, int error) -{ - struct iwi_dma_mapping *map = arg; + if (r & IWI_INTR_TX1_DONE) + iwi_tx_intr(sc, &sc->txq[0]); - if (error != 0) - return; + if (r & IWI_INTR_TX2_DONE) + iwi_tx_intr(sc, &sc->txq[1]); - KASSERT(nseg <= IWI_MAX_NSEG, ("too many DMA segments %d", nseg)); + if (r & IWI_INTR_TX3_DONE) + iwi_tx_intr(sc, &sc->txq[2]); - bcopy(segs, map->segs, nseg * sizeof (bus_dma_segment_t)); - map->nseg = nseg; - map->mapsize = mapsize; -} + if (r & IWI_INTR_TX4_DONE) + iwi_tx_intr(sc, &sc->txq[3]); -static void -iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg __unused, int error) -{ - if (error != 0) { - printf("iwi: fatal DMA mapping error !!!\n"); - return; - } + if (r & IWI_INTR_RX_DONE) + iwi_rx_intr(sc); - KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + /* acknowledge interrupts */ + CSR_WRITE_4(sc, IWI_CSR_INTR, r); - *(bus_addr_t *)arg = segs[0].ds_addr; + /* re-enable interrupts */ + CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); } static int -iwi_cmd(struct iwi_softc *sc, u_int8_t type, void *data, u_int8_t len, - int async) +iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len, int async) { struct iwi_cmd_desc *desc; + struct ifnet *ifp = &sc->sc_ic.ic_if; int ret; - DPRINTFN(2, ("TX!CMD!%u!%u\n", type, len)); + desc = &sc->cmdq.desc[sc->cmdq.cur]; - desc = &sc->cmd_desc[sc->cmd_cur]; desc->hdr.type = IWI_HDR_TYPE_COMMAND; desc->hdr.flags = IWI_HDR_FLAG_IRQ; desc->type = type; desc->len = len; - bcopy(data, desc->data, len); + memcpy(desc->data, data, len); - bus_dmamap_sync(sc->cmd_ring_dmat, sc->cmd_ring_map, + bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map, BUS_DMASYNC_PREWRITE); - sc->cmd_cur = (sc->cmd_cur + 1) % IWI_CMD_RING_SIZE; + DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur, + type, len)); + + sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT; + CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); + if (!async) { - struct ifnet *ifp = &sc->sc_ic.ic_if; + ASSERT_SERIALIZED(ifp->if_serializer); crit_enter(); - CSR_WRITE_4(sc, IWI_CSR_CMD_WRITE_INDEX, sc->cmd_cur); tsleep_interlock(IWI_FW_CMD_ACKED(sc)); lwkt_serialize_exit(ifp->if_serializer); ret = tsleep(IWI_FW_CMD_ACKED(sc), 0, "iwicmd", hz); crit_exit(); lwkt_serialize_enter(ifp->if_serializer); } else { - CSR_WRITE_4(sc, IWI_CSR_CMD_WRITE_INDEX, sc->cmd_cur); ret = 0; } - return ret; + return ret; +} + +static void +iwi_write_ibssnode(struct iwi_softc *sc, const struct iwi_node *in) +{ + struct iwi_ibssnode node; + + /* write node information into NIC memory */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.bssid, in->in_node.ni_macaddr); + + CSR_WRITE_REGION_1(sc, + IWI_CSR_NODE_BASE + in->in_station * sizeof node, + (uint8_t *)&node, sizeof node); +} + +struct iwi_dma_mapping { + bus_dma_segment_t segs[IWI_MAX_NSEG]; + int nseg; + bus_size_t mapsize; +}; + +static void +iwi_dma_map_mbuf(void *arg, bus_dma_segment_t *segs, int nseg, + bus_size_t mapsize, int error) +{ + struct iwi_dma_mapping *map = arg; + + if (error != 0) + return; + + KASSERT(nseg <= IWI_MAX_NSEG, ("too many DMA segments %d", nseg)); + + bcopy(segs, map->segs, nseg * sizeof(bus_dma_segment_t)); + map->nseg = nseg; + map->mapsize = mapsize; } static int -iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) +iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, + int ac) { struct iwi_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; + struct iwi_node *in = (struct iwi_node *)ni; struct ieee80211_frame *wh; - struct iwi_tx_buf *buf; + struct ieee80211_key *k; + const struct chanAccParams *cap; + struct iwi_tx_ring *txq = &sc->txq[ac]; + struct iwi_tx_data *data; struct iwi_tx_desc *desc; - struct iwi_dma_mapping map; struct mbuf *mnew; - u_int32_t id = 0; - int error, i; + struct iwi_dma_mapping map; + int error, hdrlen, i, noack = 0; + + wh = mtod(m0, struct ieee80211_frame *); + + if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) { + hdrlen = sizeof (struct ieee80211_qosframe); + cap = &ic->ic_wme.wme_chanParams; + noack = cap->cap_wmeParams[ac].wmep_noackPolicy; + } else + hdrlen = sizeof (struct ieee80211_frame); + + /* + * This is only used in IBSS mode where the firmware expect an index + * in a h/w table instead of a destination address. + */ + if (ic->ic_opmode == IEEE80211_M_IBSS && in->in_station == -1) { + in->in_station = iwi_alloc_ibss_node(sc); + if (in->in_station == -1) { /* h/w table is full */ + m_freem(m0); + ieee80211_free_node(ni); + ifp->if_oerrors++; + if_printf(ifp, "ibss table is full\n"); + return 0; + } + iwi_write_ibssnode(sc, in); + } + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ic, ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } if (sc->sc_drvbpf != NULL) { struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; - tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); - tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); + tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len); } - buf = &sc->tx_buf[sc->tx_cur]; - desc = &sc->tx_desc[sc->tx_cur]; - - wh = mtod(m0, struct ieee80211_frame *); - - if ( (id = iwi_find_station( sc, wh->i_addr1 ) ) == 0xff ) - id = iwi_cache_station( sc, wh->i_addr1 ); - - bzero( desc, sizeof (struct iwi_tx_desc) ); - desc->station_number = id; + data = &txq->data[txq->cur]; + desc = &txq->desc[txq->cur]; - /* trim IEEE802.11 header */ - m_adj(m0, sizeof (struct ieee80211_frame)); + /* save and trim IEEE802.11 header */ + m_copydata(m0, 0, hdrlen, (caddr_t)&desc->wh); + m_adj(m0, hdrlen); - error = bus_dmamap_load_mbuf(sc->tx_buf_dmat, buf->map, m0, - iwi_dma_map_buf, &map, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf(txq->data_dmat, data->map, m0, + iwi_dma_map_mbuf, &map, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); @@ -1589,8 +1658,9 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) } m0 = mnew; - error = bus_dmamap_load_mbuf(sc->tx_buf_dmat, buf->map, m0, - iwi_dma_map_buf, &map, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf(txq->data_dmat, data->map, m0, + iwi_dma_map_mbuf, &map, + BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); @@ -1599,46 +1669,50 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) } } - buf->m = m0; - buf->ni = ni; + data->m = m0; + data->ni = ni; desc->hdr.type = IWI_HDR_TYPE_DATA; desc->hdr.flags = IWI_HDR_FLAG_IRQ; + desc->station = + (ic->ic_opmode == IEEE80211_M_IBSS) ? in->in_station : 0; desc->cmd = IWI_DATA_CMD_TX; desc->len = htole16(m0->m_pkthdr.len); desc->flags = 0; - if (ic->ic_opmode == IEEE80211_M_IBSS) { - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) - desc->flags |= IWI_DATA_FLAG_NEED_ACK; - } else if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) + desc->xflags = 0; + + if (!noack && !IEEE80211_IS_MULTICAST(desc->wh.i_addr1)) desc->flags |= IWI_DATA_FLAG_NEED_ACK; - if (ic->ic_flags & IEEE80211_F_WEPON) { - wh->i_fc[1] |= IEEE80211_FC1_WEP; - desc->wep_txkey = ic->ic_wep_txkey; +#if 0 + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + desc->wh.i_fc[1] |= IEEE80211_FC1_WEP; + desc->weptxkey = ic->ic_crypto.cs_def_txkey; } else +#endif desc->flags |= IWI_DATA_FLAG_NO_WEP; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) desc->flags |= IWI_DATA_FLAG_SHPREAMBLE; - bcopy(wh, &desc->wh, sizeof (struct ieee80211_frame)); + if (desc->wh.i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) + desc->xflags |= IWI_DATA_XFLAG_QOS; + desc->nseg = htole32(map.nseg); for (i = 0; i < map.nseg; i++) { desc->seg_addr[i] = htole32(map.segs[i].ds_addr); - desc->seg_len[i] = htole32(map.segs[i].ds_len); + desc->seg_len[i] = htole16(map.segs[i].ds_len); } - bus_dmamap_sync(sc->tx_buf_dmat, buf->map, BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(sc->tx_ring_dmat, sc->tx_ring_map, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE); - DPRINTFN(5, ("TX!DATA!%u!%u\n", desc->len, desc->nseg)); + DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n", + ac, txq->cur, le16toh(desc->len), nsegs)); - /* Inform firmware about this new packet */ - sc->tx_queued++; - sc->tx_cur = (sc->tx_cur + 1) % IWI_TX_RING_SIZE; - CSR_WRITE_4(sc, IWI_CSR_TX1_WRITE_INDEX, sc->tx_cur); + txq->queued++; + txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT; + CSR_WRITE_4(sc, txq->csr_widx, txq->cur); return 0; } @@ -1649,185 +1723,129 @@ iwi_start(struct ifnet *ifp) struct iwi_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m0; + struct ether_header *eh; struct ieee80211_node *ni; + int ac; - if (ic->ic_state != IEEE80211_S_RUN) { + if (ic->ic_state != IEEE80211_S_RUN) return; - } for (;;) { - m0 = ifq_poll(&ifp->if_snd); + m0 = ifq_dequeue(&ifp->if_snd, NULL); if (m0 == NULL) break; - if (sc->tx_queued >= IWI_TX_RING_SIZE - 4) { + if (m0->m_len < sizeof (struct ether_header) && + (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) { + ifp->if_oerrors++; + continue; + } + eh = mtod(m0, struct ether_header *); + ni = ieee80211_find_txnode(ic, eh->ether_dhost); + if (ni == NULL) { + m_freem(m0); + ifp->if_oerrors++; + continue; + } + + /* classify mbuf so we can find which tx ring to use */ + if (ieee80211_classify(ic, m0, ni) != 0) { + m_freem(m0); + ieee80211_free_node(ni); + ifp->if_oerrors++; + continue; + } + + /* no QoS encapsulation for EAPOL frames */ + ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? + M_WME_GETAC(m0) : WME_AC_BE; + + if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { + /* there is no place left in this ring */ + m_freem(m0); + ieee80211_free_node(ni); ifp->if_flags |= IFF_OACTIVE; break; } - - ifq_dequeue(&ifp->if_snd, m0); BPF_MTAP(ifp, m0); - m0 = ieee80211_encap(ifp, m0, &ni); - if (m0 == NULL) + m0 = ieee80211_encap(ic, m0, ni); + if (m0 == NULL) { + ieee80211_free_node(ni); + ifp->if_oerrors++; continue; + } if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0); - if (iwi_tx_start(ifp, m0, ni) != 0) { - if (ni != NULL && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); + if (iwi_tx_start(ifp, m0, ni, ac) != 0) { + ieee80211_free_node(ni); + ifp->if_oerrors++; break; } - /* start watchdog timer */ sc->sc_tx_timer = 5; ifp->if_timer = 1; } - } static void iwi_watchdog(struct ifnet *ifp) { struct iwi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; ifp->if_timer = 0; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); - wakeup(IWI_FW_WAKE_MONITOR(sc)); + ifp->if_oerrors++; + ifp->if_flags &= ~IFF_UP; + iwi_stop(sc); return; } ifp->if_timer = 1; } - ieee80211_watchdog(ifp); -} - - -static int -iwi_wi_ioctl_get(struct ifnet *ifp, caddr_t data) -{ - struct wi_req wreq; - struct ifreq *ifr; - struct iwi_softc *sc; - int error; - - sc = ifp->if_softc; - ifr = (struct ifreq *)data; - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return (error); - - switch (wreq.wi_type) { - case WI_RID_READ_APS: - ieee80211_begin_scan(ifp); - - crit_enter(); - tsleep_interlock(IWI_FW_SCAN_COMPLETED(sc)); - lwkt_serialize_exit(ifp->if_serializer); - tsleep(IWI_FW_SCAN_COMPLETED(sc), PCATCH, "ssidscan", hz * 2); - crit_exit(); - lwkt_serialize_enter(ifp->if_serializer); - - ieee80211_end_scan(ifp); - break; - default: - error = ENOTTY; - break; - } - return (error); + ieee80211_watchdog(ic); } - - - static int iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) { struct iwi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; struct ifreq *ifr; - struct ieee80211req *ireq; - struct ifaddr *ifa; int error = 0; switch (cmd) { - case SIOCSIFADDR: - /* - * Handle this here instead of in net80211_ioctl.c - * so that we can lock the call to iwi_init(). - */ - ifa = (struct ifaddr *) data; - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - if ((ifp->if_flags & IFF_UP) == 0) { - ifp->if_flags |= IFF_UP; - ifp->if_init(ifp->if_softc); - } - arp_ifinit(ifp, ifa); - break; -#endif -#ifdef IPX -#warning "IPX support has not been tested" - /* - * XXX - This code is probably wrong, - * but has been copied many times. - */ - case AF_IPX: { - struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); - struct arpcom *ac = (struct arpcom *)ifp; - - if (ipx_nullhost(*ina)) - ina->x_host = *(union ipx_host *) ac->ac_enaddr; - else - bcopy((caddr_t) ina->x_host.c_host, - (caddr_t) ac->ac_enaddr, - sizeof(ac->ac_enaddr)); - /* fall thru... */ - } -#endif - default: - if ((ifp->if_flags & IFF_UP) == 0) { - ifp->if_flags |= IFF_UP; - ifp->if_init(ifp->if_softc); - } - break; - } - break; - case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_flags & IFF_RUNNING)) { + if (!(ifp->if_flags & IFF_RUNNING)) iwi_init(sc); - error = tsleep(IWI_FW_CMD_ACKED(sc), 0, - "iwirun", hz); - } } else { - if (ifp->if_flags & IFF_RUNNING) { + if (ifp->if_flags & IFF_RUNNING) iwi_stop(sc); - } } break; case SIOCSLOADFW: - case SIOCSLOADIBSSFW: /* only super-user can do that! */ - if ((error = suser(curthread)) != 0) + error = suser_cred(cr, NULL_CRED_OKAY); + if (error != 0) break; ifr = (struct ifreq *)data; - error = iwi_cache_firmware(sc, ifr->ifr_data, - (cmd == SIOCSLOADIBSSFW) ? 1 : 0); + error = iwi_cache_firmware(sc, ifr->ifr_data); break; case SIOCSKILLFW: /* only super-user can do that! */ - if ((error = suser(curthread)) != 0) + error = suser_cred(cr, NULL_CRED_OKAY); + if (error != 0) break; ifp->if_flags &= ~IFF_UP; @@ -1835,95 +1853,28 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) iwi_free_firmware(sc); break; - case SIOCG80211: - ireq = (struct ieee80211req *)data; - switch (ireq->i_type) { - case IEEE80211_IOC_AUTHMODE: - ireq->i_val = sc->authmode; - break; - - default: - error = ieee80211_ioctl(ifp, cmd, data, cr); - } - break; - - case SIOCS80211: - /* only super-user can do that! */ - if ((error = suser(curthread)) != 0) - break; - - ireq = (struct ieee80211req *)data; - switch (ireq->i_type) { - case IEEE80211_IOC_AUTHMODE: - sc->authmode = ireq->i_val; - break; - - default: - error = ieee80211_ioctl(ifp, cmd, data, cr); - } - break; - case SIOCGIFGENERIC: - if (sc->flags & IWI_FLAG_FW_INITED) { - error = iwi_wi_ioctl_get(ifp, data); - if (! error) - error = ieee80211_ioctl(ifp, cmd, data, cr); - } else - error = ENOTTY; - if (error != ENOTTY) - break; - default: - error = ieee80211_ioctl(ifp, cmd, data, cr); + error = ieee80211_ioctl(ic, cmd, data, cr); } if (error == ENETRESET) { - error = 0; if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == - (IFF_UP | IFF_RUNNING)) { + (IFF_UP | IFF_RUNNING) && + (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)) iwi_init(sc); - error = tsleep(IWI_FW_CMD_ACKED(sc), 0, "iwirun", hz); - } + error = 0; } - return error; -} -static int -iwi_abort_scan( struct iwi_softc *sc ) -{ - sc->flags |= IWI_FLAG_SCAN_ABORT; - return iwi_cmd(sc, IWI_CMD_SCAN_ABORT, NULL, 0, 1); + return error; } static void iwi_stop_master(struct iwi_softc *sc) { + uint32_t tmp; int ntries; - /* - * If the master is busy scanning, we will occasionally - * timeout waiting for it (the master) to stop. Make the - * 'stopping' process more robust by ceasing all scans - * prior to asking for the stop. - */ - if ( ( sc->flags & IWI_FLAG_SCANNING ) && - !( sc->flags & IWI_FLAG_RF_DISABLED ) ) { - struct ifnet *ifp = &sc->sc_ic.ic_if; - - ASSERT_SERIALIZED(ifp->if_serializer); - - iwi_abort_scan(sc); - if (( sc->flags & IWI_FLAG_SCAN_ABORT ) && - !( sc->flags & IWI_FLAG_RF_DISABLED )) { - crit_enter(); - tsleep_interlock(IWI_FW_SCAN_COMPLETED(sc)); - lwkt_serialize_exit(ifp->if_serializer); - tsleep(IWI_FW_SCAN_COMPLETED(sc), 0, "iwiabr", hz); - crit_exit(); - lwkt_serialize_enter(ifp->if_serializer); - } - } - /* Disable interrupts */ - + /* disable interrupts */ CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); @@ -1932,11 +1883,11 @@ iwi_stop_master(struct iwi_softc *sc) break; DELAY(10); } - if (ntries == 5 && sc->debug_level > 0) - device_printf(sc->sc_dev, "timeout waiting for master\n"); + if (ntries == 5) + if_printf(&sc->sc_ic.ic_if, "timeout waiting for master\n"); - CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | - IWI_RST_PRINCETON_RESET); + tmp = CSR_READ_4(sc, IWI_CSR_RST); + CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET); sc->flags &= ~IWI_FLAG_FW_INITED; } @@ -1944,15 +1895,14 @@ iwi_stop_master(struct iwi_softc *sc) static int iwi_reset(struct iwi_softc *sc) { + uint32_t tmp; int i, ntries; iwi_stop_master(sc); - /* Move adapter to D0 state */ - CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) | - IWI_CTL_INIT); + tmp = CSR_READ_4(sc, IWI_CSR_CTL); + CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); - /* Initialize Phase-Locked Level (PLL) */ CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); /* Wait for clock stabilization */ @@ -1962,33 +1912,32 @@ iwi_reset(struct iwi_softc *sc) DELAY(200); } if (ntries == 1000) { + if_printf(&sc->sc_ic.ic_if, + "timeout waiting for clock stabilization\n"); return EIO; } - CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | - IWI_RST_SW_RESET); + tmp = CSR_READ_4(sc, IWI_CSR_RST); + CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET); DELAY(10); - CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) | - IWI_CTL_INIT); - + tmp = CSR_READ_4(sc, IWI_CSR_CTL); + CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); /* Clear NIC memory */ CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); - - for (i = 0; i < 0xc000; i++) { + for (i = 0; i < 0xc000; i++) CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); - } - sc->num_stations = 0; return 0; } static int iwi_load_ucode(struct iwi_softc *sc, void *uc, int size) { - u_int16_t *w; + uint32_t tmp; + uint16_t *w; int ntries, i; CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | @@ -2005,27 +1954,30 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size) MEM_WRITE_4(sc, 0x3000e0, 0x80000000); DELAY(5000); - CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) & - ~IWI_RST_PRINCETON_RESET); + + tmp = CSR_READ_4(sc, IWI_CSR_RST); + tmp &= ~IWI_RST_PRINCETON_RESET; + CSR_WRITE_4(sc, IWI_CSR_RST, tmp); + DELAY(5000); MEM_WRITE_4(sc, 0x3000e0, 0); DELAY(1000); - MEM_WRITE_4(sc, 0x300004, 1); + MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, 1); DELAY(1000); - MEM_WRITE_4(sc, 0x300004, 0); + MEM_WRITE_4(sc, IWI_MEM_EVENT_CTL, 0); DELAY(1000); MEM_WRITE_1(sc, 0x200000, 0x00); MEM_WRITE_1(sc, 0x200000, 0x40); DELAY(1000); - /* Adapter is buggy, we must set the address for each word */ + /* write microcode into adapter memory */ for (w = uc; size > 0; w++, size -= 2) - MEM_WRITE_2(sc, 0x200010, *w); + MEM_WRITE_2(sc, 0x200010, htole16(*w)); MEM_WRITE_1(sc, 0x200000, 0x00); MEM_WRITE_1(sc, 0x200000, 0x80); - /* Wait until we get a response in the uc queue */ + /* wait until we get an answer */ for (ntries = 0; ntries < 100; ntries++) { if (MEM_READ_1(sc, 0x200000) & 1) break; @@ -2037,7 +1989,7 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size) return EIO; } - /* Empty the uc queue or the firmware will not initialize properly */ + /* read the answer or the firmware will not initialize properly */ for (i = 0; i < 7; i++) MEM_READ_4(sc, 0x200004); @@ -2048,6 +2000,7 @@ iwi_load_ucode(struct iwi_softc *sc, void *uc, int size) /* macro to handle unaligned little endian data in firmware image */ #define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) + static int iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) { @@ -2056,25 +2009,21 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) bus_addr_t physaddr; void *virtaddr; u_char *p, *end; - u_int32_t sentinel, ctl, src, dst, sum, len, mlen; + uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; int ntries, error = 0; struct ifnet *ifp = &sc->sc_ic.ic_if; - sc->flags &= ~(IWI_FLAG_FW_INITED); + ASSERT_SERIALIZED(ifp->if_serializer); - /* Allocate DMA memory for storing firmware image */ - error = bus_dma_tag_create(sc->iwi_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, BUS_DMA_ALLOCNOW, &dmat); + /* Allocate DMA memory for mapping firmware image */ + error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, &dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create firmware DMA tag\n"); goto fail1; } - - /* - * We cannot map fw directly because of some hardware constraints on - * the mapping address. - */ + error = bus_dmamem_alloc(dmat, &virtaddr, BUS_DMA_WAITOK, &map); if (error != 0) { device_printf(sc->sc_dev, @@ -2090,7 +2039,7 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) } /* Copy firmware image to DMA memory */ - bcopy(fw, virtaddr, size); + memcpy(virtaddr, fw, size); /* Make sure the adapter will get up-to-date values */ bus_dmamap_sync(dmat, map, BUS_DMASYNC_PREWRITE); @@ -2135,13 +2084,14 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); - CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) & - ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER)); + tmp = CSR_READ_4(sc, IWI_CSR_RST); + tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER); + CSR_WRITE_4(sc, IWI_CSR_RST, tmp); /* Tell the adapter to start processing command blocks */ MEM_WRITE_4(sc, 0x3000a4, 0x540100); - /* Wait until the adapter has processed all command blocks */ + /* Wait until the adapter reaches the sentinel */ for (ntries = 0; ntries < 400; ntries++) { if (MEM_READ_4(sc, 0x3000d0) >= sentinel) break; @@ -2154,22 +2104,23 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) goto fail4; } - /* We're done with command blocks processing */ MEM_WRITE_4(sc, 0x3000a4, 0x540c00); - /* Allow interrupts so we know when the firmware is inited */ + /* Allow interrupts so we know when the firmware is ready */ CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); /* Tell the adapter to initialize the firmware */ - crit_enter(); CSR_WRITE_4(sc, IWI_CSR_RST, 0); - CSR_WRITE_4(sc, IWI_CSR_CTL, - CSR_READ_4(sc, IWI_CSR_CTL) | IWI_CTL_ALLOW_STANDBY); + tmp = CSR_READ_4(sc, IWI_CSR_CTL); + CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY); + + /* wait at most one second for firmware initialization to complete */ + crit_enter(); tsleep_interlock(IWI_FW_INITIALIZED(sc)); lwkt_serialize_exit(ifp->if_serializer); - error = tsleep(IWI_FW_INITIALIZED(sc), 0, "iwiini", hz); + error = tsleep(IWI_FW_INITIALIZED(sc), 0, "iwiinit", hz); crit_exit(); lwkt_serialize_enter(ifp->if_serializer); if (error != 0) { @@ -2178,7 +2129,7 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) goto fail4; } -fail4: bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE); +fail4: bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dmat, map); fail3: bus_dmamem_free(dmat, virtaddr, map); fail2: bus_dma_tag_destroy(dmat); @@ -2191,7 +2142,7 @@ fail1: * e.g when the adapter wakes up from suspend mode. */ static int -iwi_cache_firmware(struct iwi_softc *sc, void *data, int is_ibss) +iwi_cache_firmware(struct iwi_softc *sc, void *data) { struct iwi_firmware *kfw = &sc->fw; struct iwi_firmware ufw; @@ -2199,57 +2150,37 @@ iwi_cache_firmware(struct iwi_softc *sc, void *data, int is_ibss) iwi_free_firmware(sc); - /* - * mutex(9): no mutexes should be held across functions which access - * memory in userspace, such as copyin(9) [...] - */ - if ((error = copyin(data, &ufw, sizeof ufw)) != 0) - goto fail1; + return error; kfw->boot_size = ufw.boot_size; kfw->ucode_size = ufw.ucode_size; kfw->main_size = ufw.main_size; kfw->boot = malloc(kfw->boot_size, M_DEVBUF, M_WAITOK); - if (kfw->boot == NULL) { - error = ENOMEM; - goto fail1; - } - kfw->ucode = malloc(kfw->ucode_size, M_DEVBUF, M_WAITOK); - if (kfw->ucode == NULL) { - error = ENOMEM; - goto fail2; - } - kfw->main = malloc(kfw->main_size, M_DEVBUF, M_WAITOK); - if (kfw->main == NULL) { - error = ENOMEM; - goto fail3; - } if ((error = copyin(ufw.boot, kfw->boot, kfw->boot_size)) != 0) - goto fail4; + goto fail; if ((error = copyin(ufw.ucode, kfw->ucode, kfw->ucode_size)) != 0) - goto fail4; + goto fail; if ((error = copyin(ufw.main, kfw->main, kfw->main_size)) != 0) - goto fail4; + goto fail; DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n", kfw->boot_size, kfw->ucode_size, kfw->main_size)); - sc->flags |= IWI_FLAG_FW_CACHED; - sc->flags |= is_ibss ? IWI_FLAG_FW_IBSS : 0; + return 0; -fail4: free(kfw->boot, M_DEVBUF); -fail3: free(kfw->ucode, M_DEVBUF); -fail2: free(kfw->main, M_DEVBUF); -fail1: +fail: + free(kfw->boot, M_DEVBUF); + free(kfw->ucode, M_DEVBUF); + free(kfw->main, M_DEVBUF); return error; } @@ -2264,73 +2195,20 @@ iwi_free_firmware(struct iwi_softc *sc) free(sc->fw.ucode, M_DEVBUF); free(sc->fw.main, M_DEVBUF); - sc->flags &= ~( IWI_FLAG_FW_CACHED | IWI_FLAG_FW_IBSS ); -} - -static int -iwi_adapter_config(struct iwi_softc *sc, int is_a, int cmd_wait) -{ - struct iwi_configuration config; - - bzero(&config, sizeof config); - config.enable_multicast = 1; - config.noise_reported = 1; - - config.bg_autodetect = - ( !(is_a) && - ( sc->enable_bg_autodetect != 0 ) ) ? 1 : 0; /* default: on */ - - config.bluetooth_coexistence = - ( sc->enable_bt_coexist != 0 ) ? 1 : 0; /* default: on */ - - config.enable_cts_to_self = - ( sc->enable_cts_to_self > 0 ) ? 1 : 0; /* default: off */ - - if (sc->antenna_diversity > 0 ) { /* default: BOTH */ - switch( sc->antenna_diversity ) { - case 1: case 3: - config.antenna_diversity = sc->antenna_diversity; - } - } - - config.disable_unicast_decryption = - ( sc->disable_unicast_decryption != 0 ) ? 1 : 0; /* default: on */ - - config.disable_multicast_decryption = - ( sc->disable_multicast_decryption != 0 ) ? 1 : 0;/* default: on */ - - - if ( sc->debug_level > 0 ) { - printf("config.bluetooth_coexistence = %d\n", - config.bluetooth_coexistence ); - printf("config.bg_autodetect = %d\n", - config.bg_autodetect ); - printf("config.enable_cts_to_self = %d\n", - config.enable_cts_to_self ); - printf("config.antenna_diversity = %d\n", - config.antenna_diversity ); - printf("config.disable_unicast_decryption = %d\n", - config.disable_unicast_decryption ); - printf("config.disable_multicast_decryption = %d\n", - config.disable_multicast_decryption ); - printf("config.neg_best_rates_first = %d\n", - sc->enable_neg_best_first ); - } - - return iwi_cmd(sc, IWI_CMD_SET_CONFIGURATION, &config, - sizeof config, cmd_wait ); + sc->flags &= ~IWI_FLAG_FW_CACHED; } static int iwi_config(struct iwi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = ic->ic_ifp; + struct iwi_configuration config; struct iwi_rateset rs; struct iwi_txpower power; - struct ieee80211_wepkey *k; + struct ieee80211_key *wk; struct iwi_wep_key wepkey; - u_int32_t data; + uint32_t data; int error, i; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); @@ -2340,8 +2218,16 @@ iwi_config(struct iwi_softc *sc) if (error != 0) return error; + memset(&config, 0, sizeof config); + config.bluetooth_coexistence = sc->bluetooth; + config.antenna = sc->antenna; + config.multicast_enabled = 1; + config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; + config.disable_unicast_decryption = 1; + config.disable_multicast_decryption = 1; DPRINTF(("Configuring adapter\n")); - if ((error = iwi_adapter_config(sc, 1, 0)) != 0) + error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config, 0); + if (error != 0) return error; data = htole32(IWI_POWER_MODE_CAM); @@ -2356,6 +2242,12 @@ iwi_config(struct iwi_softc *sc) if (error != 0) return error; + data = htole32(ic->ic_fragthreshold); + DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data))); + error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data, 0); + if (error != 0) + return error; + if (ic->ic_opmode == IEEE80211_M_IBSS) { power.mode = IWI_MODE_11B; power.nchan = 11; @@ -2380,7 +2272,7 @@ iwi_config(struct iwi_softc *sc) rs.mode = IWI_MODE_11G; rs.type = IWI_RATESET_TYPE_SUPPORTED; rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; - bcopy(ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, rs.rates, + memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, rs.nrates); DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0); @@ -2390,34 +2282,49 @@ iwi_config(struct iwi_softc *sc) rs.mode = IWI_MODE_11A; rs.type = IWI_RATESET_TYPE_SUPPORTED; rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; - bcopy(ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, rs.rates, + memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, rs.nrates); DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0); if (error != 0) return error; + /* if we have a desired ESSID, set it now */ + if (ic->ic_des_esslen != 0) { +#ifdef IWI_DEBUG + if (iwi_debug > 0) { + printf("Setting desired ESSID to "); + ieee80211_print_essid(ic->ic_des_essid, + ic->ic_des_esslen); + printf("\n"); + } +#endif + error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_essid, + ic->ic_des_esslen, 0); + if (error != 0) + return error; + } + data = htole32(arc4random()); DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0); if (error != 0) return error; - if (ic->ic_flags & IEEE80211_F_WEPON) { - k = ic->ic_nw_keys; - for (i = 0; i < IEEE80211_WEP_NKID; i++, k++) { - wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; - wepkey.idx = i; - wepkey.len = k->wk_len; - bzero(wepkey.key, sizeof wepkey.key); - bcopy(k->wk_key, wepkey.key, k->wk_len); - DPRINTF(("Setting wep key index %u len %u\n", - wepkey.idx, wepkey.len)); - error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, - sizeof wepkey, 0); - if (error != 0) - return error; - } + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + wk = &ic->ic_crypto.cs_nw_keys[i]; + + wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; + wepkey.idx = i; + wepkey.len = wk->wk_keylen; + memset(wepkey.key, 0, sizeof wepkey.key); + memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); + DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, + wepkey.len)); + error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, + sizeof wepkey, 0); + if (error != 0) + return error; } /* Enable adapter */ @@ -2426,68 +2333,63 @@ iwi_config(struct iwi_softc *sc) } static int -iwi_scan(struct iwi_softc *sc) +iwi_set_chan(struct iwi_softc *sc, struct ieee80211_channel *chan) { struct ieee80211com *ic = &sc->sc_ic; struct iwi_scan scan; - u_int8_t *p; - int i, count; - int do_5ghz_scan = 0; - sc->scan_counter++; /* track the number of scans started */ + memset(&scan, 0, sizeof scan); + memset(scan.type, IWI_SCAN_TYPE_PASSIVE, sizeof scan.type); + scan.passive = htole16(2000); + scan.channels[0] = 1 | + (IEEE80211_IS_CHAN_5GHZ(chan) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ); + scan.channels[1] = ieee80211_chan2ieee(ic, chan); + + DPRINTF(("Setting channel to %u\n", ieee80211_chan2ieee(ic, chan))); + return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1); +} - sc->flags |= IWI_FLAG_SCANNING; +static int +iwi_scan(struct iwi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct iwi_scan scan; + uint8_t *p; + int i, count; - bzero(&scan, sizeof scan); + memset(&scan, 0, sizeof scan); - /* - * Alternate two broadcast scans with - * two broadcast/direct scans. - */ - if ( sc->scan_counter & 2 ) { - scan.type = IWI_SCAN_TYPE_BROADCAST_AND_DIRECT; - scan.intval = htole16(100); + if (ic->ic_des_esslen != 0) { + scan.bdirected = htole16(sc->dwelltime); + memset(scan.type, IWI_SCAN_TYPE_BDIRECTED, sizeof scan.type); } else { - scan.type = IWI_SCAN_TYPE_BROADCAST; - scan.intval = htole16(40); + scan.broadcast = htole16(sc->dwelltime); + memset(scan.type, IWI_SCAN_TYPE_BROADCAST, sizeof scan.type); } p = scan.channels; - - /* - * If we have .11a capable adapter, and - * - we are in .11a mode, or - * - we are in auto mode and this is an odd numbered scan - * then do a 5GHz scan, otherwise do a 2GHz scan. - */ - if ( ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates > 0 ) { - if (( ic->ic_curmode == IEEE80211_MODE_11A ) || - (( ic->ic_curmode == IEEE80211_MODE_AUTO ) && - ( sc->scan_counter & 1))) - do_5ghz_scan = 1; - } count = 0; - if ( do_5ghz_scan ) { - DPRINTF(("Scanning 5GHz band\n")); - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) && - isset(ic->ic_chan_active, i)) { - *++p = i; - count++; - } + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) && + isset(ic->ic_chan_active, i)) { + *++p = i; + count++; } - *(p - count) = IWI_CHAN_5GHZ | count; - } else { - DPRINTF(("Scanning 2GHz band\n")); - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) && - isset(ic->ic_chan_active, i)) { - *++p = i; - count++; - } + } + *(p - count) = IWI_CHAN_5GHZ | count; + + p = (count > 0) ? p + 1 : scan.channels; + count = 0; + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) && + isset(ic->ic_chan_active, i)) { + *++p = i; + count++; } - *(p - count) = IWI_CHAN_2GHZ | count; } + *(p - count) = IWI_CHAN_2GHZ | count; + + DPRINTF(("Start scanning\n")); return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1); } @@ -2495,24 +2397,35 @@ static int iwi_auth_and_assoc(struct iwi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_node *ni = ic->ic_bss; + struct ieee80211_wme_info wme; + struct iwi_configuration config; + struct iwi_associate assoc; struct iwi_rateset rs; - u_int32_t data; - int error, x; - - if ( ( sc->flags & IWI_FLAG_FW_IBSS ) && - !( ni->ni_capinfo & IEEE80211_CAPINFO_IBSS ) ) { - return -1; /* IBSS F/W requires network ibss capability */ - } + uint16_t capinfo; + uint32_t data; + int error; - DPRINTF(("Configuring adapter\n")); - if ((error = iwi_adapter_config(sc, - IEEE80211_IS_CHAN_5GHZ(ni->ni_chan), 1)) != 0) + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { + memset(&config, 0, sizeof config); + config.bluetooth_coexistence = sc->bluetooth; + config.antenna = sc->antenna; + config.multicast_enabled = 1; + config.use_protection = 1; + config.answer_pbreq = + (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; + config.disable_unicast_decryption = 1; + config.disable_multicast_decryption = 1; + DPRINTF(("Configuring adapter\n")); + error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config, + 1); + if (error != 0) return error; + } #ifdef IWI_DEBUG - if (sc->debug_level > 0) { + if (iwi_debug > 0) { printf("Setting ESSID to "); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("\n"); @@ -2527,55 +2440,79 @@ iwi_auth_and_assoc(struct iwi_softc *sc) IWI_MODE_11G; rs.type = IWI_RATESET_TYPE_NEGOTIATED; rs.nrates = ni->ni_rates.rs_nrates; - if ( sc->enable_neg_best_first != 1 ) { - bcopy(ni->ni_rates.rs_rates, rs.rates, rs.nrates); - } else { - for ( x = 0 ; x < rs.nrates; x++ ) { - /* - * Present the firmware with the most favourable - * of the negotiated rates first. - */ - rs.rates[rs.nrates-x-1] = ni->ni_rates.rs_rates[x]; - } - } - - if ( sc->debug_level > 0 ) { - printf("Setting negotiated rates (%u) : ", rs.nrates); - for ( x = 0 ; x < rs.nrates; x++ ) { - printf("%d ", rs.rates[x]); - } - printf("\n"); - } - + memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates); + DPRINTF(("Setting negociated rates (%u)\n", rs.nrates)); error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 1); if (error != 0) return error; + if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) { + wme.wme_id = IEEE80211_ELEMID_VENDOR; + wme.wme_len = sizeof (struct ieee80211_wme_info) - 2; + wme.wme_oui[0] = 0x00; + wme.wme_oui[1] = 0x50; + wme.wme_oui[2] = 0xf2; + wme.wme_type = WME_OUI_TYPE; + wme.wme_subtype = WME_INFO_OUI_SUBTYPE; + wme.wme_version = WME_VERSION; + wme.wme_info = 0; + + DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len)); + error = iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme, 1); + if (error != 0) + return error; + } + + if (ic->ic_opt_ie != NULL) { + DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len)); + error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie, + ic->ic_opt_ie_len, 1); + if (error != 0) + return error; + } + data = htole32(ni->ni_rssi); DPRINTF(("Setting sensitivity to %d\n", (int8_t)ni->ni_rssi)); error = iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &data, sizeof data, 1); if (error != 0) return error; - bzero(&sc->assoc, sizeof sc->assoc); - sc->assoc.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A : + memset(&assoc, 0, sizeof assoc); + assoc.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A : IWI_MODE_11G; - sc->assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan); - if (sc->authmode == IEEE80211_AUTH_SHARED) - sc->assoc.auth = (ic->ic_wep_txkey << 4) | IWI_AUTH_SHARED; - bcopy(ni->ni_tstamp, sc->assoc.tstamp, 8); - sc->assoc.capinfo = htole16(ni->ni_capinfo); - sc->assoc.lintval = htole16(ic->ic_lintval); - sc->assoc.intval = htole16(ni->ni_intval); - IEEE80211_ADDR_COPY(sc->assoc.bssid, ni->ni_bssid); - if ( ic->ic_opmode == IEEE80211_M_IBSS ) - IEEE80211_ADDR_COPY(sc->assoc.dst, ifp->if_broadcastaddr); + assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) + assoc.auth = ic->ic_crypto.cs_def_txkey << 4 | IWI_AUTH_SHARED; + if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + assoc.policy |= htole16(IWI_POLICY_WME); + if (ic->ic_flags & IEEE80211_F_WPA) + assoc.policy |= htole16(IWI_POLICY_WPA); + memcpy(assoc.tstamp, ni->ni_tstamp.data, 8); + + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + assoc.capinfo = htole16(capinfo); + + assoc.lintval = htole16(ic->ic_lintval); + assoc.intval = htole16(ni->ni_intval); + IEEE80211_ADDR_COPY(assoc.bssid, ni->ni_bssid); + if (ic->ic_opmode == IEEE80211_M_IBSS) + IEEE80211_ADDR_COPY(assoc.dst, ifp->if_broadcastaddr); else - IEEE80211_ADDR_COPY(sc->assoc.dst, ni->ni_bssid); + IEEE80211_ADDR_COPY(assoc.dst, ni->ni_bssid); DPRINTF(("Trying to associate to %6D channel %u auth %u\n", - sc->assoc.bssid, ":", sc->assoc.chan, sc->assoc.auth)); - return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &sc->assoc, sizeof sc->assoc, 1); + assoc.bssid, ":", assoc.chan, assoc.auth)); + return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &assoc, sizeof assoc, 1); } static void @@ -2583,12 +2520,16 @@ iwi_init(void *priv) { struct iwi_softc *sc = priv; struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = ic->ic_ifp; struct iwi_firmware *fw = &sc->fw; + struct iwi_rx_data *data; int i; /* exit immediately if firmware has not been ioctl'd */ if (!(sc->flags & IWI_FLAG_FW_CACHED)) { + if (!(sc->flags & IWI_FLAG_FW_WARNED)) + device_printf(sc->sc_dev, "Please load firmware\n"); + sc->flags |= IWI_FLAG_FW_WARNED; ifp->if_flags &= ~IFF_UP; return; } @@ -2612,89 +2553,57 @@ iwi_init(void *priv) iwi_stop_master(sc); - sc->tx_cur = 0; - sc->tx_queued = 0; - sc->tx_old = IWI_TX_RING_SIZE - 1; - sc->cmd_cur = 0; - sc->rx_cur = IWI_RX_RING_SIZE - 1; - - CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmd_ring_pa); - CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, IWI_CMD_RING_SIZE); - CSR_WRITE_4(sc, IWI_CSR_CMD_READ_INDEX, 0); - CSR_WRITE_4(sc, IWI_CSR_CMD_WRITE_INDEX, sc->cmd_cur); - - CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->tx_ring_pa); - CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, IWI_TX_RING_SIZE); - CSR_WRITE_4(sc, IWI_CSR_TX1_READ_INDEX, 0); - CSR_WRITE_4(sc, IWI_CSR_TX1_WRITE_INDEX, sc->tx_cur); - - CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->tx_ring_pa); - CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, IWI_TX_RING_SIZE); - CSR_WRITE_4(sc, IWI_CSR_TX2_READ_INDEX, 0); - CSR_WRITE_4(sc, IWI_CSR_TX2_WRITE_INDEX, 0); - - CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->tx_ring_pa); - CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, IWI_TX_RING_SIZE); - CSR_WRITE_4(sc, IWI_CSR_TX3_READ_INDEX, 0); - CSR_WRITE_4(sc, IWI_CSR_TX3_WRITE_INDEX, 0); - - CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->tx_ring_pa); - CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, IWI_TX_RING_SIZE); - CSR_WRITE_4(sc, IWI_CSR_TX4_READ_INDEX, 0); - CSR_WRITE_4(sc, IWI_CSR_TX4_WRITE_INDEX, 0); - - for (i = 0; i < IWI_RX_RING_SIZE; i++) - CSR_WRITE_4(sc, IWI_CSR_RX_BASE + i * 4, - sc->rx_buf[i].physaddr); + CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr); + CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count); + CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); - /* - * Kick Rx - */ - CSR_WRITE_4(sc, IWI_CSR_RX_WRITE_INDEX, sc->rx_cur); - CSR_WRITE_4(sc, IWI_CSR_RX_READ_INDEX, 0); + CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr); + CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count); + CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur); + + CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr); + CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count); + CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur); + + CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr); + CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count); + CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur); + + CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr); + CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count); + CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur); + + for (i = 0; i < sc->rxq.count; i++) { + data = &sc->rxq.data[i]; + CSR_WRITE_4(sc, data->reg, data->physaddr); + } + + CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1); if (iwi_load_firmware(sc, fw->main, fw->main_size) != 0) { device_printf(sc->sc_dev, "could not load main firmware\n"); goto fail; } - /* - * Force the opmode based on what firmware is loaded. This - * stops folks from killing the firmware by asking it to - * do something it doesn't support. - */ - if ( ic->ic_opmode != IEEE80211_M_MONITOR ) { - ic->ic_opmode = ( sc->flags & IWI_FLAG_FW_IBSS ) - ? IEEE80211_M_IBSS : IEEE80211_M_STA; - } - sc->flags |= IWI_FLAG_FW_INITED; - sc->flags &= ~( IWI_FLAG_SCANNING | - IWI_FLAG_SCAN_COMPLETE | - IWI_FLAG_SCAN_ABORT | - IWI_FLAG_ASSOCIATED ); - if (iwi_config(sc) != 0) { device_printf(sc->sc_dev, "device configuration failed\n"); goto fail; } - if ( ic->ic_opmode != IEEE80211_M_MONITOR ) { - ieee80211_begin_scan(ifp); - ifp->if_flags &= ~IFF_OACTIVE; - ifp->if_flags |= IFF_RUNNING; - } else { - ieee80211_begin_scan(ifp); - ifp->if_flags &= ~IFF_OACTIVE; - ifp->if_flags |= IFF_RUNNING; - } + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_flags |= IFF_RUNNING; return; -fail: - if ( !(sc->flags & IWI_FLAG_RESET) ) - ifp->if_flags &= ~IFF_UP; +fail: ifp->if_flags &= ~IFF_UP; iwi_stop(sc); } @@ -2703,347 +2612,111 @@ iwi_stop(void *priv) { struct iwi_softc *sc = priv; struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct iwi_tx_buf *buf; - int i; + struct ifnet *ifp = ic->ic_ifp; + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); iwi_stop_master(sc); - CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SW_RESET); - /* - * Release Tx buffers - */ - for (i = 0; i < IWI_TX_RING_SIZE; i++) { - buf = &sc->tx_buf[i]; + CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); - if (buf->m != NULL) { - bus_dmamap_sync(sc->tx_buf_dmat, buf->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->tx_buf_dmat, buf->map); - m_freem(buf->m); - buf->m = NULL; - - if (buf->ni != NULL) { - if (buf->ni != ic->ic_bss) - ieee80211_free_node(ic, buf->ni); - buf->ni = NULL; - } - } - } + /* reset rings */ + iwi_reset_cmd_ring(sc, &sc->cmdq); + iwi_reset_tx_ring(sc, &sc->txq[0]); + iwi_reset_tx_ring(sc, &sc->txq[1]); + iwi_reset_tx_ring(sc, &sc->txq[2]); + iwi_reset_tx_ring(sc, &sc->txq[3]); + iwi_reset_rx_ring(sc, &sc->rxq); + sc->sc_tx_timer = 0; ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); -} - -static int8_t -iwi_cache_station(struct iwi_softc *sc, u_int8_t *mac) -{ - int i, x, base, elemsize = sizeof(struct iwi_fw_station); - for (i = 0; i < sc->num_stations; i++) - if (!memcmp(sc->stations[i], mac, IEEE80211_ADDR_LEN)) - break; - if (i == IWI_FW_MAX_STATIONS) - return 0xff; - memcpy(sc->stations[i], mac, IEEE80211_ADDR_LEN); - for (x = 0, base = IWI_STATION_TABLE + (i * elemsize) ; - x < IEEE80211_ADDR_LEN ; x++ ) { - CSR_WRITE_1(sc, base + x, mac[x]); - } - if ( (i + 1) > sc->num_stations ) - sc->num_stations++; - return i; -} - -static u_int8_t -iwi_find_station(struct iwi_softc *sc, u_int8_t *mac) -{ - u_int8_t i; - for (i = 0; i < sc->num_stations; i++) - if (!memcmp(sc->stations[i], mac, IEEE80211_ADDR_LEN)) - return i; - return 0xff; -} - -static const char * -iwi_error_desc(u_int32_t val) -{ - switch (val) { - case IWI_FW_ERROR_OK: - return "OK"; - case IWI_FW_ERROR_FAIL: - return "FAIL"; - case IWI_FW_ERROR_MEMORY_UNDERFLOW: - return "MEMORY_UNDERFLOW"; - case IWI_FW_ERROR_MEMORY_OVERFLOW: - return "MEMORY_OVERFLOW"; - case IWI_FW_ERROR_BAD_PARAM: - return "BAD_PARAMETER"; - case IWI_FW_ERROR_BAD_CHECKSUM: - return "BAD_CHECKSUM"; - case IWI_FW_ERROR_NMI_INTERRUPT: - return "NMI_INTERRUPT"; - case IWI_FW_ERROR_BAD_DATABASE: - return "BAD_DATABASE"; - case IWI_FW_ERROR_ALLOC_FAIL: - return "ALLOC_FAIL"; - case IWI_FW_ERROR_DMA_UNDERRUN: - return "DMA_UNDERRUN"; - case IWI_FW_ERROR_DMA_STATUS: - return "DMA_STATUS"; - case IWI_FW_ERROR_DINOSTATUS_ERROR: - return "DINOSTATUS_ERROR"; - case IWI_FW_ERROR_EEPROMSTATUS_ERROR: - return "EEPROMSTATUS_ERROR"; - case IWI_FW_ERROR_SYSASSERT: - return "SYSASSERT"; - case IWI_FW_ERROR_FATAL_ERROR: - return "FATAL"; - default: - return "UNKNOWN_ERROR"; - } -} - -static void -iwi_dump_fw_event_log(struct iwi_softc *sc) -{ - u_int32_t ev, time, data, i, count, base; - base = CSR_READ_4(sc, IWI_FW_EVENT_LOG); - count = MEM_READ_4(sc, base); - if ( count > 0 && (sc->flags & IWI_FLAG_FW_INITED) ) { - printf("Reading %d event log entries from base address 0x%x.\n", - count, base); - if (IWI_FW_EVENT_START_OFFSET <= count * IWI_FW_EVENT_ELEM_SIZE) - device_printf(sc->sc_dev,"Start IWI Event Log Dump:\n"); - for (i = IWI_FW_EVENT_START_OFFSET; - i <= count * IWI_FW_EVENT_ELEM_SIZE; - i += IWI_FW_EVENT_ELEM_SIZE) { - ev = MEM_READ_4(sc, base + i); - time = MEM_READ_4(sc, base + i + 1 * sizeof(u_int32_t)); - data = MEM_READ_4(sc, base + i + 2 * sizeof(u_int32_t)); - printf("%d %8p %8.8d\n", time, (void *) data, ev); - } - } else { - printf("There are no entries in the firmware event log.\n"); - } -} - -static void -iwi_dump_fw_error_log(struct iwi_softc *sc) -{ - u_int32_t i = 0; - int32_t count, base; - base = CSR_READ_4(sc, IWI_FW_ERROR_LOG); - count = MEM_READ_4(sc, base); - if ( count > 0 && (sc->flags & IWI_FLAG_FW_INITED) ) { - printf("Reading %d error log entries " - "from base address 0x%p.\n", count, (void *)base); - for ( i = IWI_FW_ERROR_START_OFFSET; - i <= count * IWI_FW_EVENT_ELEM_SIZE; - i += IWI_FW_ERROR_ELEM_SIZE ) { - u_int32_t elems; - printf("%15.15s", - iwi_error_desc(MEM_READ_4(sc, base + i))); - printf(" time(%8.8d)", MEM_READ_4(sc, base + i + 4)); - for ( elems = 2 ; elems < 7 ; elems++ ) { - printf(" %8p", (void *) - MEM_READ_4(sc, base + i + (4 * elems))); - } - printf("\n"); - } - } -} - -static int -iwi_sysctl_cts_to_self(SYSCTL_HANDLER_ARGS) -{ - struct iwi_softc *sc = (void *)arg1; - int cts_to_self = sc->enable_cts_to_self; - int error = sysctl_handle_int(oidp, &cts_to_self, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - - if ( !error && req->newptr && cts_to_self != sc->enable_cts_to_self ) { - switch ( cts_to_self ) { - case -1: case 0: case 1: - sc->enable_cts_to_self = cts_to_self; - error = iwi_adapter_config(sc, 0, 0); - break; - } - } - return error; -} - - -static int -iwi_sysctl_antenna_diversity(SYSCTL_HANDLER_ARGS) -{ - struct iwi_softc *sc = (void *)arg1; - int antenna_diversity = sc->antenna_diversity; - int error = sysctl_handle_int(oidp, &antenna_diversity, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - - if ( !error && req->newptr && antenna_diversity != sc->antenna_diversity ) { - switch ( antenna_diversity ) { - case 1: case 3: case 0: case -1: - sc->antenna_diversity = antenna_diversity; - error = iwi_adapter_config(sc, 0, 0); - break; - } - } - return error; -} - -static int -iwi_sysctl_bg_autodetect(SYSCTL_HANDLER_ARGS) -{ - struct iwi_softc *sc = (void *)arg1; - int bg_autodetect = sc->enable_bg_autodetect; - int error = sysctl_handle_int(oidp, &bg_autodetect, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - - if ( !error && req->newptr && bg_autodetect != sc->enable_bg_autodetect ) { - switch ( bg_autodetect ) { - case 1: case 0: case -1: - sc->enable_bg_autodetect = bg_autodetect; - error = iwi_adapter_config(sc, 0, 0); - break; - } - } - return error; -} - -static int -iwi_sysctl_bt_coexist(SYSCTL_HANDLER_ARGS) -{ - struct iwi_softc *sc = (void *)arg1; - int bt_coexist = sc->enable_bt_coexist; - int error = sysctl_handle_int(oidp, &bt_coexist, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - - if ( !error && req->newptr && bt_coexist != sc->enable_bt_coexist ) { - switch ( bt_coexist ) { - case 1: case 0: case -1: - sc->enable_bt_coexist = bt_coexist; - error = iwi_adapter_config(sc, 0, 0); - break; - } - } - return error; -} - -static int -iwi_sysctl_dump_logs(SYSCTL_HANDLER_ARGS) -{ - struct iwi_softc *sc = arg1; - int result = -1; - int error = sysctl_handle_int(oidp, &result, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - - if (!error && req->newptr && result == 1) { - iwi_dump_fw_event_log(sc); - iwi_dump_fw_error_log(sc); - } - return error; } static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS) { struct iwi_softc *sc = arg1; - u_int32_t size; - struct iwi_dump_buffer dump; + struct ifnet *ifp = &sc->sc_ic.ic_if; + uint32_t size, buf[128]; - (void)arg2; /* silence WARNS == 6 */ - (void)oidp; /* silence WARNS == 6 */ + lwkt_serialize_enter(ifp->if_serializer); if (!(sc->flags & IWI_FLAG_FW_INITED)) { - bzero(dump.buf, sizeof dump.buf); - return SYSCTL_OUT(req, &dump, sizeof dump); + memset(buf, 0, sizeof buf); + goto back; } size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); - CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &dump.buf[1], size); - - return SYSCTL_OUT(req, &dump, sizeof dump); + CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size); +back: + lwkt_serialize_exit(ifp->if_serializer); + return SYSCTL_OUT(req, buf, sizeof buf); } static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS) { struct iwi_softc *sc = arg1; + struct ifnet *ifp = &sc->sc_ic.ic_if; int val; - (void)arg2; /* silence WARNS == 6 */ - (void)oidp; /* silence WARNS == 6 */ - + lwkt_serialize_enter(ifp->if_serializer); val = (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) ? 1 : 0; + lwkt_serialize_exit(ifp->if_serializer); + return SYSCTL_OUT(req, &val, sizeof val); } -static int -iwi_sysctl_neg_best_rates_first(SYSCTL_HANDLER_ARGS) -{ - struct iwi_softc *sc = arg1; - int best_first = sc->enable_neg_best_first; - int error = sysctl_handle_int(oidp, &best_first, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - (void)oidp; /* silence WARNS == 6 */ - - if ( !error && req->newptr && best_first != sc->enable_neg_best_first ) { - switch ( best_first ) { - case 1: case 0: case -1: - sc->enable_neg_best_first = best_first; - break; - } - } - return error; -} +static const int8_t iwi_bitmap[256] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, -1 +}; static int -iwi_sysctl_disable_unicast_decryption(SYSCTL_HANDLER_ARGS) +iwi_alloc_ibss_node(struct iwi_softc *sc) { - struct iwi_softc *sc = arg1; - int disable_uni = sc->disable_unicast_decryption; - int error = sysctl_handle_int(oidp, &disable_uni, 0, req); + int i; - (void)arg2; /* silence WARNS == 6 */ - (void)oidp; /* silence WARNS == 6 */ + ASSERT_SERIALIZED(sc->sc_ic.ic_if.if_serializer); - if (!error && req->newptr && disable_uni != sc->disable_unicast_decryption) { - switch ( disable_uni ) { - case 1: case 0: case -1: - sc->disable_unicast_decryption = disable_uni; - break; + for (i = 0; i < IWI_MAX_IBSSNODE_NBYTE; ++i) { + int ret; + + ret = iwi_bitmap[sc->sc_ibss_node[i]]; + if (ret != -1) { + sc->sc_ibss_node[i] |= (1 << ret); + ret += (i * NBBY); + return ret; } } - return error; + return -1; } -static int -iwi_sysctl_disable_multicast_decryption(SYSCTL_HANDLER_ARGS) +static void +iwi_free_ibss_node(struct iwi_softc *sc, int ibss_node) { - struct iwi_softc *sc = arg1; - int disable_mul = sc->disable_multicast_decryption; - int error = sysctl_handle_int(oidp, &disable_mul, 0, req); - - (void)arg2; /* silence WARNS == 6 */ - (void)oidp; /* silence WARNS == 6 */ + int i, b; - if (!error && req->newptr && disable_mul!=sc->disable_multicast_decryption){ - switch ( disable_mul ) { - case 1: case 0: case -1: - sc->disable_multicast_decryption = disable_mul; - break; - } - } - return error; -} + ASSERT_SERIALIZED(sc->sc_ic.ic_if.if_serializer); + KASSERT(ibss_node < IWI_MAX_IBSSNODE, ("free ibss node, out of range")); + i = ibss_node / NBBY; + b = ibss_node % NBBY; + sc->sc_ibss_node[i] &= ~(1 << b); +} diff --git a/sys/dev/netif/iwi/if_iwireg.h b/sys/dev/netif/iwi/if_iwireg.h index b5ba82567a..c74ea7fdc4 100644 --- a/sys/dev/netif/iwi/if_iwireg.h +++ b/sys/dev/netif/iwi/if_iwireg.h @@ -1,8 +1,8 @@ /* - * Copyright (c) 2004, 2005 + * Copyright (c) 2004-2006 * Damien Bergamini . * Copyright (c) 2004, 2005 - * Andrew Atrens . + * Andrew Atrens . * * All rights reserved. * @@ -28,12 +28,16 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/iwi/if_iwireg.h,v 1.1 2005/03/06 05:02:02 dillon Exp $ + * $FreeBSD: src/sys/dev/iwi/if_iwireg.h,v 1.2.2.2 2006/01/29 13:54:19 damien Exp $ + * $DragonFly: src/sys/dev/netif/iwi/if_iwireg.h,v 1.2 2006/05/18 13:51:45 sephe Exp $ */ -#define IWI_TX_RING_SIZE 64 -#define IWI_CMD_RING_SIZE 16 -#define IWI_RX_RING_SIZE 32 +#define IWI_CMD_RING_COUNT 16 +#define IWI_TX_RING_COUNT 64 +#define IWI_RX_RING_COUNT 32 + +#define IWI_TX_DESC_SIZE (sizeof (struct iwi_tx_desc)) +#define IWI_CMD_DESC_SIZE (sizeof (struct iwi_cmd_desc)) #define IWI_CSR_INTR 0x0008 #define IWI_CSR_INTR_MASK 0x000c @@ -54,91 +58,62 @@ #define IWI_CSR_TX3_SIZE 0x021c #define IWI_CSR_TX4_BASE 0x0220 #define IWI_CSR_TX4_SIZE 0x0224 -#define IWI_CSR_CMD_READ_INDEX 0x0280 -#define IWI_CSR_TX1_READ_INDEX 0x0284 -#define IWI_CSR_TX2_READ_INDEX 0x0288 -#define IWI_CSR_TX3_READ_INDEX 0x028c -#define IWI_CSR_TX4_READ_INDEX 0x0290 -#define IWI_CSR_RX_READ_INDEX 0x02a0 +#define IWI_CSR_CMD_RIDX 0x0280 +#define IWI_CSR_TX1_RIDX 0x0284 +#define IWI_CSR_TX2_RIDX 0x0288 +#define IWI_CSR_TX3_RIDX 0x028c +#define IWI_CSR_TX4_RIDX 0x0290 +#define IWI_CSR_RX_RIDX 0x02a0 #define IWI_CSR_RX_BASE 0x0500 #define IWI_CSR_TABLE0_SIZE 0x0700 #define IWI_CSR_TABLE0_BASE 0x0704 -#define IWI_CSR_CURRENT_TX_RATE IWI_CSR_TABLE0_BASE - -#define IWI_CSR_GET_TABLE0_BASE 0x0380 -#define IWI_CSR_GET_TABLE1_BASE 0x0384 -#define IWI_CSR_GET_TABLE2_BASE 0x0388 -#define IWI_CSR_GET_TABLE3_BASE 0x038C - -#define IWI_CSR_CMD_WRITE_INDEX 0x0f80 -#define IWI_CSR_TX1_WRITE_INDEX 0x0f84 -#define IWI_CSR_TX2_WRITE_INDEX 0x0f88 -#define IWI_CSR_TX3_WRITE_INDEX 0x0f8c -#define IWI_CSR_TX4_WRITE_INDEX 0x0f90 -#define IWI_CSR_RX_WRITE_INDEX 0x0fa0 +#define IWI_CSR_NODE_BASE 0x0c0c +#define IWI_CSR_CMD_WIDX 0x0f80 +#define IWI_CSR_TX1_WIDX 0x0f84 +#define IWI_CSR_TX2_WIDX 0x0f88 +#define IWI_CSR_TX3_WIDX 0x0f8c +#define IWI_CSR_TX4_WIDX 0x0f90 +#define IWI_CSR_RX_WIDX 0x0fa0 #define IWI_CSR_READ_INT 0x0ff4 -#define IWI_STATION_TABLE 0x0c0c - -#define IWI_FW_ERROR_LOG 0x0610 -#define IWI_FW_EVENT_LOG 0x0614 - +/* aliases */ +#define IWI_CSR_CURRENT_TX_RATE IWI_CSR_TABLE0_BASE -/* possible flags for IWI_CSR_INTR */ -#define IWI_INTR_RX_TRANSFER 0x00000002 -#define IWI_INTR_STATUS_CHG 0x00000010 -#define IWI_INTR_BEACON_EXP 0x00000020 -#define IWI_INTR_CMD_TRANSFER 0x00000800 -#define IWI_INTR_TX1_TRANSFER 0x00001000 -#define IWI_INTR_TX2_TRANSFER 0x00002000 -#define IWI_INTR_TX3_TRANSFER 0x00004000 -#define IWI_INTR_TX4_TRANSFER 0x00008000 -#define IWI_INTR_SLMODE_CDONE 0x00010000 -#define IWI_INTR_PREP_PWDOWN 0x00100000 -#define IWI_INTR_PWDOWN 0x00200000 +/* flags for IWI_CSR_INTR */ +#define IWI_INTR_RX_DONE 0x00000002 +#define IWI_INTR_CMD_DONE 0x00000800 +#define IWI_INTR_TX1_DONE 0x00001000 +#define IWI_INTR_TX2_DONE 0x00002000 +#define IWI_INTR_TX3_DONE 0x00004000 +#define IWI_INTR_TX4_DONE 0x00008000 #define IWI_INTR_FW_INITED 0x01000000 -#define IWI_INTR_DIS_PHY_DONE 0x02000000 -#define IWI_INTR_RADIO_OFF 0x04000000 +#define IWI_INTR_RADIO_OFF 0x04000000 #define IWI_INTR_FATAL_ERROR 0x40000000 #define IWI_INTR_PARITY_ERROR 0x80000000 -#define IWI_HANDLED_INTR_MASK \ - (IWI_INTR_RX_TRANSFER | IWI_INTR_CMD_TRANSFER | \ - IWI_INTR_TX1_TRANSFER | IWI_INTR_TX2_TRANSFER | \ - IWI_INTR_TX3_TRANSFER | IWI_INTR_TX4_TRANSFER | \ - IWI_INTR_FW_INITED | IWI_INTR_FATAL_ERROR | \ - IWI_INTR_RADIO_OFF | IWI_INTR_PARITY_ERROR) - #define IWI_INTR_MASK \ - (IWI_INTR_RX_TRANSFER | IWI_INTR_CMD_TRANSFER | \ - IWI_INTR_TX1_TRANSFER | IWI_INTR_TX2_TRANSFER | \ - IWI_INTR_TX3_TRANSFER | IWI_INTR_TX4_TRANSFER | \ - IWI_INTR_FW_INITED | IWI_INTR_FATAL_ERROR | \ - IWI_INTR_PARITY_ERROR | IWI_INTR_STATUS_CHG | \ - IWI_INTR_BEACON_EXP | IWI_INTR_SLMODE_CDONE | \ - IWI_INTR_PREP_PWDOWN | IWI_INTR_PWDOWN | \ - IWI_INTR_DIS_PHY_DONE | IWI_INTR_RADIO_OFF) - -/* possible flags for register IWI_CSR_RST */ + (IWI_INTR_RX_DONE | IWI_INTR_CMD_DONE | IWI_INTR_TX1_DONE | \ + IWI_INTR_TX2_DONE | IWI_INTR_TX3_DONE | IWI_INTR_TX4_DONE | \ + IWI_INTR_FW_INITED | IWI_INTR_RADIO_OFF | \ + IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR) + +/* flags for IWI_CSR_RST */ #define IWI_RST_PRINCETON_RESET 0x00000001 -#define IWI_RST_SW_RESET 0x00000080 +#define IWI_RST_SOFT_RESET 0x00000080 #define IWI_RST_MASTER_DISABLED 0x00000100 #define IWI_RST_STOP_MASTER 0x00000200 -/* possible flags for register IWI_CSR_CTL */ +/* flags for IWI_CSR_CTL */ #define IWI_CTL_CLOCK_READY 0x00000001 #define IWI_CTL_ALLOW_STANDBY 0x00000002 #define IWI_CTL_INIT 0x00000004 -/* possible flags for register IWI_CSR_IO */ +/* flags for IWI_CSR_IO */ #define IWI_IO_RADIO_ENABLED 0x00010000 -/* possible flags for IWI_CSR_READ_INT */ +/* flags for IWI_CSR_READ_INT */ #define IWI_READ_INT_INIT_HOST 0x20000000 -/* table2 offsets */ -#define IWI_INFO_ADAPTER_MAC 40 - /* constants for command blocks */ #define IWI_CB_DEFAULT_CTL 0x8cea0000 #define IWI_CB_MAXDATALEN 8191 @@ -158,20 +133,22 @@ #define IWI_RATE_OFDM54 3 struct iwi_hdr { - u_int8_t type; + uint8_t type; #define IWI_HDR_TYPE_DATA 0 #define IWI_HDR_TYPE_COMMAND 1 #define IWI_HDR_TYPE_NOTIF 3 #define IWI_HDR_TYPE_FRAME 9 - u_int8_t seq; - u_int8_t flags; + + uint8_t seq; + uint8_t flags; #define IWI_HDR_FLAG_IRQ 0x04 - u_int8_t reserved; + + uint8_t reserved; } __packed; struct iwi_notif { - u_int32_t reserved[2]; - u_int8_t type; + uint32_t reserved[2]; + uint8_t type; #define IWI_NOTIF_TYPE_ASSOCIATION 10 #define IWI_NOTIF_TYPE_AUTHENTICATION 11 #define IWI_NOTIF_TYPE_SCAN_CHANNEL 12 @@ -179,119 +156,129 @@ struct iwi_notif { #define IWI_NOTIF_TYPE_BEACON 17 #define IWI_NOTIF_TYPE_CALIBRATION 20 #define IWI_NOTIF_TYPE_NOISE 25 - u_int8_t flags; - u_int16_t len; -} __packed; -/* structure for notification IWI_NOTIF_TYPE_NOISE */ -struct iwi_notif_noise { - u_int32_t value; + uint8_t flags; + uint16_t len; } __packed; /* structure for notification IWI_NOTIF_TYPE_AUTHENTICATION */ struct iwi_notif_authentication { - u_int8_t state; + uint8_t state; #define IWI_DEAUTHENTICATED 0 #define IWI_AUTHENTICATED 9 } __packed; /* structure for notification IWI_NOTIF_TYPE_ASSOCIATION */ struct iwi_notif_association { - u_int8_t state; + uint8_t state; #define IWI_DEASSOCIATED 0 #define IWI_ASSOCIATED 12 + struct ieee80211_frame frame; - u_int16_t capinfo; - u_int16_t status; - u_int16_t associd; + uint16_t capinfo; + uint16_t status; + uint16_t associd; } __packed; /* structure for notification IWI_NOTIF_TYPE_SCAN_CHANNEL */ struct iwi_notif_scan_channel { - u_int8_t nchan; - u_int8_t reserved[47]; + uint8_t nchan; + uint8_t reserved[47]; } __packed; /* structure for notification IWI_NOTIF_TYPE_SCAN_COMPLETE */ struct iwi_notif_scan_complete { - u_int8_t type; - u_int8_t nchan; - u_int8_t status; - u_int8_t reserved; + uint8_t type; + uint8_t nchan; + uint8_t status; + uint8_t reserved; } __packed; /* received frame header */ struct iwi_frame { - u_int32_t reserved1; - u_int8_t parent_tsf[4]; - u_int8_t chan; - u_int8_t status; - u_int8_t rate; - u_int8_t rssi; /* receiver signal strength indicator */ - u_int8_t agc; /* automatic gain control */ - u_int8_t rssi_dbm; - u_int16_t signal; - u_int16_t noise; - u_int8_t antenna; - u_int8_t control; - u_int8_t rtscts_rate; - u_int8_t rtscts_seen; - u_int16_t len; + uint32_t reserved1[2]; + uint8_t chan; + uint8_t status; + uint8_t rate; + uint8_t rssi; + uint8_t agc; + uint8_t rssi_dbm; + uint16_t signal; + uint16_t noise; + uint8_t antenna; + uint8_t control; + uint8_t reserved2[2]; + uint16_t len; } __packed; /* header for transmission */ struct iwi_tx_desc { struct iwi_hdr hdr; - u_int32_t work_area_ptr; - u_int8_t station_number; /* 0 for BSS */ - u_int8_t reserved1[3]; - u_int8_t cmd; + uint32_t reserved1; + uint8_t station; + uint8_t reserved2[3]; + uint8_t cmd; #define IWI_DATA_CMD_TX 0x0b - u_int8_t seq; - u_int16_t len; - u_int8_t priority; - u_int8_t flags; + + uint8_t seq; + uint16_t len; + uint8_t priority; + uint8_t flags; #define IWI_DATA_FLAG_SHPREAMBLE 0x04 #define IWI_DATA_FLAG_NO_WEP 0x20 #define IWI_DATA_FLAG_NEED_ACK 0x80 - u_int8_t xflags; - u_int8_t wep_txkey; - u_int8_t wepkey[IEEE80211_KEYBUF_SIZE]; - u_int8_t rate; - u_int8_t antenna; - u_int8_t reserved2[10]; - struct ieee80211_qosframe_addr4 wh; - u_int32_t iv[2]; + uint8_t xflags; +#define IWI_DATA_XFLAG_QOS 0x10 - u_int32_t nseg; + uint8_t weptxkey; + uint8_t wepkey[IEEE80211_KEYBUF_SIZE]; + uint8_t rate; + uint8_t antenna; + uint8_t reserved3[10]; + struct ieee80211_qosframe_addr4 wh; + uint32_t iv; + uint32_t eiv; + uint32_t nseg; #define IWI_MAX_NSEG 6 - u_int32_t seg_addr[IWI_MAX_NSEG]; - u_int16_t seg_len[IWI_MAX_NSEG]; + + uint32_t seg_addr[IWI_MAX_NSEG]; + uint16_t seg_len[IWI_MAX_NSEG]; } __packed; /* command */ struct iwi_cmd_desc { struct iwi_hdr hdr; - u_int8_t type; + uint8_t type; #define IWI_CMD_ENABLE 2 -#define IWI_CMD_SET_CONFIGURATION 6 +#define IWI_CMD_SET_CONFIG 6 #define IWI_CMD_SET_ESSID 8 #define IWI_CMD_SET_MAC_ADDRESS 11 #define IWI_CMD_SET_RTS_THRESHOLD 15 +#define IWI_CMD_SET_FRAG_THRESHOLD 16 #define IWI_CMD_SET_POWER_MODE 17 #define IWI_CMD_SET_WEP_KEY 18 -#define IWI_CMD_SCAN 20 #define IWI_CMD_ASSOCIATE 21 #define IWI_CMD_SET_RATES 22 -#define IWI_CMD_SCAN_ABORT 23 +#define IWI_CMD_ABORT_SCAN 23 +#define IWI_CMD_SET_WME_PARAMS 25 +#define IWI_CMD_SCAN 26 +#define IWI_CMD_SET_OPTIE 31 #define IWI_CMD_DISABLE 33 #define IWI_CMD_SET_IV 34 #define IWI_CMD_SET_TX_POWER 35 #define IWI_CMD_SET_SENSITIVITY 42 - u_int8_t len; - u_int16_t reserved; - u_int8_t data[120]; +#define IWI_CMD_SET_WMEIE 84 + + uint8_t len; + uint16_t reserved; + uint8_t data[120]; +} __packed; + +/* node information (IBSS) */ +struct iwi_ibssnode { + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t reserved[2]; } __packed; /* constants for 'mode' fields */ @@ -299,30 +286,28 @@ struct iwi_cmd_desc { #define IWI_MODE_11B 1 #define IWI_MODE_11G 2 -/* macro for command IWI_CMD_SET_SENSITIVITY */ -#define IWI_RSSIDBM2RAW(rssi) ((rssi) - 112) - /* possible values for command IWI_CMD_SET_POWER_MODE */ #define IWI_POWER_MODE_CAM 0 /* structure for command IWI_CMD_SET_RATES */ struct iwi_rateset { - u_int8_t mode; - u_int8_t nrates; - u_int8_t type; + uint8_t mode; + uint8_t nrates; + uint8_t type; #define IWI_RATESET_TYPE_NEGOTIATED 0 #define IWI_RATESET_TYPE_SUPPORTED 1 - u_int8_t reserved; - u_int8_t rates[12]; + + uint8_t reserved; + uint8_t rates[12]; } __packed; /* structure for command IWI_CMD_SET_TX_POWER */ struct iwi_txpower { - u_int8_t nchan; - u_int8_t mode; + uint8_t nchan; + uint8_t mode; struct { - u_int8_t chan; - u_int8_t power; + uint8_t chan; + uint8_t power; #define IWI_TXPOWER_MAX 20 #define IWI_TXPOWER_RATIO (IEEE80211_TXPOWER_MAX / IWI_TXPOWER_MAX) } __packed chan[37]; @@ -330,89 +315,110 @@ struct iwi_txpower { /* structure for command IWI_CMD_ASSOCIATE */ struct iwi_associate { - u_int8_t chan; - u_int8_t auth; + uint8_t chan; + uint8_t auth; #define IWI_AUTH_OPEN 0 #define IWI_AUTH_SHARED 1 #define IWI_AUTH_NONE 3 - u_int8_t type; - u_int8_t reserved; - u_int16_t policy_support; - u_int8_t plen; - u_int8_t mode; - u_int8_t bssid[IEEE80211_ADDR_LEN]; - u_int8_t tstamp[8]; - u_int16_t capinfo; - u_int16_t lintval; - u_int16_t intval; - u_int8_t dst[IEEE80211_ADDR_LEN]; - u_int16_t atim_window; - u_int8_t smr; - u_int8_t reserved1; - u_int16_t reserved2; + + uint8_t type; + uint8_t reserved1; + uint16_t policy; +#define IWI_POLICY_WME 1 +#define IWI_POLICY_WPA 2 + + uint8_t plen; + uint8_t mode; + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t tstamp[8]; + uint16_t capinfo; + uint16_t lintval; + uint16_t intval; + uint8_t dst[IEEE80211_ADDR_LEN]; + uint32_t reserved3; + uint16_t reserved4; } __packed; /* structure for command IWI_CMD_SCAN */ struct iwi_scan { - u_int8_t type; -#define IWI_SCAN_PASSIVE_TILL_FIRST_BEACON 0 -#define IWI_SCAN_PASSIVE_FULL_DWELL 1 -#define IWI_SCAN_TYPE_DIRECT 2 -#define IWI_SCAN_TYPE_BROADCAST 3 -#define IWI_SCAN_TYPE_BROADCAST_AND_DIRECT 4 - u_int16_t intval; - u_int8_t channels[54]; + uint32_t index; + uint8_t channels[54]; #define IWI_CHAN_5GHZ (0 << 6) #define IWI_CHAN_2GHZ (1 << 6) - u_int8_t reserved[3]; + + uint8_t type[27]; +#define IWI_SCAN_TYPE_PASSIVE 0x11 +#define IWI_SCAN_TYPE_DIRECTED 0x22 +#define IWI_SCAN_TYPE_BROADCAST 0x33 +#define IWI_SCAN_TYPE_BDIRECTED 0x44 + + uint8_t reserved1; + uint16_t reserved2; + uint16_t passive; /* dwell time */ + uint16_t directed; /* dwell time */ + uint16_t broadcast; /* dwell time */ + uint16_t bdirected; /* dwell time */ } __packed; -/* structure for command IWI_CMD_SET_CONFIGURATION */ +/* structure for command IWI_CMD_SET_CONFIG */ struct iwi_configuration { - u_int8_t bluetooth_coexistence; - u_int8_t reserved1; - u_int8_t answer_broadcast_probe_req; - u_int8_t allow_invalid_frames; - u_int8_t enable_multicast; - u_int8_t exclude_unicast_unencrypted; - u_int8_t disable_unicast_decryption; - u_int8_t exclude_multicast_unencrypted; - u_int8_t disable_multicast_decryption; - u_int8_t antenna_diversity; - u_int8_t pass_crc_to_host; - u_int8_t bg_autodetect; - u_int8_t enable_cts_to_self; - u_int8_t enable_multicast_filtering; - u_int8_t bluetooth_threshold; - u_int8_t reserved2; - u_int8_t allow_beacon_and_probe_resp; - u_int8_t allow_mgt; - u_int8_t noise_reported; - u_int8_t reserved3; + uint8_t bluetooth_coexistence; + uint8_t reserved1; + uint8_t answer_pbreq; + uint8_t allow_invalid_frames; + uint8_t multicast_enabled; + uint8_t drop_unicast_unencrypted; + uint8_t disable_unicast_decryption; + uint8_t drop_multicast_unencrypted; + uint8_t disable_multicast_decryption; + uint8_t antenna; + uint8_t reserved2; + uint8_t use_protection; + uint8_t protection_ctsonly; + uint8_t enable_multicast_filtering; + uint8_t bluetooth_threshold; + uint8_t reserved4; + uint8_t allow_beacon_and_probe_resp; + uint8_t allow_mgt; + uint8_t noise_reported; + uint8_t reserved5; } __packed; /* structure for command IWI_CMD_SET_WEP_KEY */ struct iwi_wep_key { - u_int8_t cmd; + uint8_t cmd; #define IWI_WEP_KEY_CMD_SETKEY 0x08 - u_int8_t seq; - u_int8_t idx; - u_int8_t len; - u_int8_t key[IEEE80211_KEYBUF_SIZE]; + + uint8_t seq; + uint8_t idx; + uint8_t len; + uint8_t key[IEEE80211_KEYBUF_SIZE]; } __packed; -/* EEPROM = Electrically Erasable Programmable Read-Only Memory */ +/* structure for command IWI_CMD_SET_WME_PARAMS */ +struct iwi_wme_params { + uint16_t cwmin[WME_NUM_AC]; + uint16_t cwmax[WME_NUM_AC]; + uint8_t aifsn[WME_NUM_AC]; + uint8_t acm[WME_NUM_AC]; + uint16_t burst[WME_NUM_AC]; +} __packed; +#define IWI_MEM_EVENT_CTL 0x00300004 #define IWI_MEM_EEPROM_CTL 0x00300040 +/* possible flags for register IWI_MEM_EVENT */ +#define IWI_LED_ASSOC (1 << 5) +#define IWI_LED_MASK 0xd9fffffb + #define IWI_EEPROM_MAC 0x21 #define IWI_EEPROM_DELAY 1 /* minimum hold time (microsecond) */ -#define IWI_EEPROM_C (1 << 0) /* Serial Clock */ -#define IWI_EEPROM_S (1 << 1) /* Chip Select */ -#define IWI_EEPROM_D (1 << 2) /* Serial data input */ -#define IWI_EEPROM_Q (1 << 4) /* Serial data output */ +#define IWI_EEPROM_C (1 << 0) /* Serial Clock */ +#define IWI_EEPROM_S (1 << 1) /* Chip Select */ +#define IWI_EEPROM_D (1 << 2) /* Serial data input */ +#define IWI_EEPROM_Q (1 << 4) /* Serial data output */ #define IWI_EEPROM_SHIFT_D 2 #define IWI_EEPROM_SHIFT_Q 4 @@ -442,9 +448,21 @@ struct iwi_wep_key { #define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) +#define CSR_WRITE_REGION_1(sc, offset, datap, count) \ + bus_space_write_region_1((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + /* * indirect memory space access macros */ +#define MEM_READ_1(sc, addr) \ + (CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)), \ + CSR_READ_1((sc), IWI_CSR_INDIRECT_DATA)) + +#define MEM_READ_4(sc, addr) \ + (CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)), \ + CSR_READ_4((sc), IWI_CSR_INDIRECT_DATA)) + #define MEM_WRITE_1(sc, addr, val) do { \ CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)); \ CSR_WRITE_1((sc), IWI_CSR_INDIRECT_DATA, (val)); \ @@ -465,40 +483,6 @@ struct iwi_wep_key { CSR_WRITE_MULTI_1((sc), IWI_CSR_INDIRECT_DATA, (buf), (len)); \ } while (/* CONSTCOND */0) - -#define IWI_FW_ERROR_OK 0 -#define IWI_FW_ERROR_FAIL 1 -#define IWI_FW_ERROR_MEMORY_UNDERFLOW 2 -#define IWI_FW_ERROR_MEMORY_OVERFLOW 3 -#define IWI_FW_ERROR_BAD_PARAM 4 -#define IWI_FW_ERROR_BAD_CHECKSUM 5 -#define IWI_FW_ERROR_NMI_INTERRUPT 6 -#define IWI_FW_ERROR_BAD_DATABASE 7 -#define IWI_FW_ERROR_ALLOC_FAIL 8 -#define IWI_FW_ERROR_DMA_UNDERRUN 9 -#define IWI_FW_ERROR_DMA_STATUS 10 -#define IWI_FW_ERROR_DINOSTATUS_ERROR 11 -#define IWI_FW_ERROR_EEPROMSTATUS_ERROR 12 -#define IWI_FW_ERROR_SYSASSERT 13 -#define IWI_FW_ERROR_FATAL_ERROR 14 - - -#define IWI_FW_EVENT_ELEM_SIZE (3 * sizeof(u_int32_t)) -#define IWI_FW_EVENT_START_OFFSET (sizeof(u_int32_t) + 2 * sizeof(u_int16_t)) -#define IWI_FW_ERROR_ELEM_SIZE (7 * sizeof(u_int32_t)) -#define IWI_FW_ERROR_START_OFFSET (1 * sizeof(u_int32_t)) - - -#define IWI_FW_MAX_STATIONS 32 - -struct iwi_fw_station { - u_int8_t mac[ETHER_ADDR_LEN]; - u_int8_t reserved; - u_int8_t support_mode; -} __packed; - - - /* * EEPROM access macro */ diff --git a/sys/dev/netif/iwi/if_iwivar.h b/sys/dev/netif/iwi/if_iwivar.h index 9ee9b82177..42435b4473 100644 --- a/sys/dev/netif/iwi/if_iwivar.h +++ b/sys/dev/netif/iwi/if_iwivar.h @@ -2,7 +2,7 @@ * Copyright (c) 2004, 2005 * Damien Bergamini . * Copyright (c) 2004, 2005 - * Andrew Atrens . + * Andrew Atrens . * * All rights reserved. * @@ -28,7 +28,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/dev/netif/iwi/if_iwivar.h,v 1.4 2005/12/18 02:47:34 sephe Exp $ + * $FreeBSD: src/sys/dev/iwi/if_iwivar.h,v 1.4.2.1 2005/09/26 17:31:36 damien Exp $ + * $DragonFly: src/sys/dev/netif/iwi/if_iwivar.h,v 1.5 2006/05/18 13:51:45 sephe Exp $ */ struct iwi_firmware { @@ -42,13 +43,12 @@ struct iwi_firmware { struct iwi_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; - u_int8_t wr_flags; - u_int8_t wr_rate; - u_int16_t wr_chan_freq; - u_int16_t wr_chan_flags; - u_int8_t wr_antsignal; - u_int8_t wr_antnoise; - u_int8_t wr_antenna; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_antsignal; + uint8_t wr_antenna; }; #define IWI_RX_RADIOTAP_PRESENT \ @@ -56,80 +56,102 @@ struct iwi_rx_radiotap_header { (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ - (1 << IEEE80211_RADIOTAP_DB_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_ANTENNA)) struct iwi_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; - u_int8_t wt_flags; - u_int16_t wt_chan_freq; - u_int16_t wt_chan_flags; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; }; #define IWI_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) +struct iwi_cmd_ring { + bus_dma_tag_t desc_dmat; + bus_dmamap_t desc_map; + bus_addr_t physaddr; + struct iwi_cmd_desc *desc; + int count; + int queued; + int cur; + int next; +}; + +struct iwi_tx_data { + bus_dmamap_t map; + struct mbuf *m; + struct ieee80211_node *ni; +}; + +struct iwi_tx_ring { + bus_dma_tag_t desc_dmat; + bus_dma_tag_t data_dmat; + bus_dmamap_t desc_map; + bus_addr_t physaddr; + bus_addr_t csr_ridx; + bus_addr_t csr_widx; + struct iwi_tx_desc *desc; + struct iwi_tx_data *data; + int count; + int queued; + int cur; + int next; +}; + +struct iwi_rx_data { + bus_dmamap_t map; + bus_addr_t physaddr; + uint32_t reg; + struct mbuf *m; +}; + +struct iwi_rx_ring { + bus_dma_tag_t data_dmat; + struct iwi_rx_data *data; + int count; + int cur; +}; + +struct iwi_node { + struct ieee80211_node in_node; + int in_station; +#define IWI_MAX_IBSSNODE_NBYTE 4 +#define IWI_MAX_IBSSNODE (IWI_MAX_IBSSNODE_NBYTE * NBBY) +}; + struct iwi_softc { + struct ifnet *sc_ifp; struct ieee80211com sc_ic; int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); + void (*sc_node_free)(struct ieee80211_node *); device_t sc_dev; - struct iwi_firmware fw; - u_int32_t flags; + uint32_t flags; #define IWI_FLAG_FW_CACHED (1 << 0) -#define IWI_FLAG_FW_IBSS (1 << 1) -#define IWI_FLAG_FW_INITED (1 << 2) -#define IWI_FLAG_SCANNING (1 << 3) -#define IWI_FLAG_SCAN_COMPLETE (1 << 4) -#define IWI_FLAG_SCAN_ABORT (1 << 5) -#define IWI_FLAG_ASSOCIATED (1 << 6) -#define IWI_FLAG_RF_DISABLED (1 << 7) -#define IWI_FLAG_RESET (1 << 8) -#define IWI_FLAG_EXIT (1 << 9) - - struct iwi_tx_desc *tx_desc; - bus_dma_tag_t iwi_parent_tag; - bus_dma_tag_t tx_ring_dmat; - bus_dmamap_t tx_ring_map; - bus_addr_t tx_ring_pa; - bus_dma_tag_t tx_buf_dmat; - - struct iwi_tx_buf { - bus_dmamap_t map; - struct mbuf *m; - struct ieee80211_node *ni; - } tx_buf[IWI_TX_RING_SIZE]; - - int tx_cur; - int tx_old; - int tx_queued; - - struct iwi_cmd_desc *cmd_desc; - bus_dma_tag_t cmd_ring_dmat; - bus_dmamap_t cmd_ring_map; - bus_addr_t cmd_ring_pa; - int cmd_cur; - - bus_dma_tag_t rx_buf_dmat; - - struct iwi_rx_buf { - bus_dmamap_t map; - bus_addr_t physaddr; - struct mbuf *m; - } rx_buf[IWI_RX_RING_SIZE]; - - int rx_cur; +#define IWI_FLAG_FW_INITED (1 << 1) +#define IWI_FLAG_FW_WARNED (1 << 2) +#define IWI_FLAG_SCANNING (1 << 3) + + struct iwi_cmd_ring cmdq; + struct iwi_tx_ring txq[WME_NUM_AC]; + struct iwi_rx_ring rxq; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; + int mem_rid; + int irq_rid; - int authmode; + int antenna; + int dwelltime; + int bluetooth; int sc_tx_timer; @@ -137,53 +159,26 @@ struct iwi_softc { union { struct iwi_rx_radiotap_header th; - u_int8_t pad[64]; - } sc_rxtapu; + uint8_t pad[64]; + } sc_rxtapu; #define sc_rxtap sc_rxtapu.th int sc_rxtap_len; union { struct iwi_tx_radiotap_header th; - u_int8_t pad[64]; - } sc_txtapu; + uint8_t pad[64]; + } sc_txtapu; #define sc_txtap sc_txtapu.th int sc_txtap_len; - int num_stations; - u_int8_t stations[IWI_FW_MAX_STATIONS][ETHER_ADDR_LEN]; - struct lwkt_token sc_lock; - struct lwkt_token sc_intrlock; + uint8_t sc_ibss_node[IWI_MAX_IBSSNODE_NBYTE]; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; - - int debug_level; - - int enable_bg_autodetect; - int enable_bt_coexist; - int enable_cts_to_self; - int antenna_diversity; /* 1 = A, 3 = B, 0 = A + B */ - int enable_neg_best_first; - int disable_unicast_decryption; - int disable_multicast_decryption; - - struct thread *event_thread; - - struct iwi_associate assoc; - - int scan_counter; - }; #define SIOCSLOADFW _IOW('i', 137, struct ifreq) -#define SIOCSLOADIBSSFW _IOW('i', 138, struct ifreq) -#define SIOCSKILLFW _IOW('i', 139, struct ifreq) - -/* tsleepable events */ -#define IWI_FW_WAKE_MONITOR(sc) (sc + 1) -#define IWI_FW_INITIALIZED(sc) (sc + 2) -#define IWI_FW_CMD_ACKED(sc) (sc + 3) -#define IWI_FW_SCAN_COMPLETED(sc) (sc + 4) -#define IWI_FW_DEASSOCIATED(sc) (sc + 5) -#define IWI_FW_MON_EXIT(sc) (sc + 6) +#define SIOCSKILLFW _IOW('i', 138, struct ifreq) +#define IWI_FW_INITIALIZED(sc) (sc + 1) +#define IWI_FW_CMD_ACKED(sc) (sc + 2) diff --git a/sys/dev/netif/ndis/if_ndis.c b/sys/dev/netif/ndis/if_ndis.c index c90b16f5d1..c1e4458a3f 100644 --- a/sys/dev/netif/ndis/if_ndis.c +++ b/sys/dev/netif/ndis/if_ndis.c @@ -30,7 +30,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/if_ndis/if_ndis.c,v 1.65 2004/07/07 17:46:30 wpaul Exp $ - * $DragonFly: src/sys/dev/netif/ndis/if_ndis.c,v 1.14 2005/12/31 23:35:38 dillon Exp $ + * $DragonFly: src/sys/dev/netif/ndis/if_ndis.c,v 1.15 2006/05/18 13:51:45 sephe Exp $ */ #include @@ -640,8 +640,8 @@ nonettypes: if (r == 0) ic->ic_caps |= IEEE80211_C_PMGT; bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr)); - ieee80211_ifattach(ifp); - ieee80211_media_init(ifp, ieee80211_media_change, + ieee80211_ifattach(ic); + ieee80211_media_init(ic, ieee80211_media_change, ndis_media_status); ic->ic_ibss_chan = IEEE80211_CHAN_ANYC; ic->ic_bss->ni_chan = ic->ic_ibss_chan; @@ -709,7 +709,7 @@ ndis_detach(dev) lwkt_serialize_exit(ifp->if_serializer); if (sc->ndis_80211) - ieee80211_ifdetach(ifp); + ieee80211_ifdetach(&sc->ic); else ether_ifdetach(ifp); } @@ -1402,15 +1402,15 @@ ndis_setstate_80211(sc) /* Set WEP */ -#ifdef IEEE80211_F_WEPON - if (ic->ic_flags & IEEE80211_F_WEPON) { +#ifdef IEEE80211_F_PRIVACY + if (ic->ic_flags & IEEE80211_F_PRIVACY) { #else if (ic->ic_wep_mode >= IEEE80211_WEP_ON) { #endif for (i = 0; i < IEEE80211_WEP_NKID; i++) { - if (ic->ic_nw_keys[i].wk_len) { + if (ic->ic_nw_keys[i].wk_keylen) { bzero((char *)&wep, sizeof(wep)); - wep.nw_keylen = ic->ic_nw_keys[i].wk_len; + wep.nw_keylen = ic->ic_nw_keys[i].wk_keylen; #ifdef notdef /* 5 and 13 are the only valid key lengths */ if (ic->ic_nw_keys[i].wk_len < 5) @@ -1422,7 +1422,7 @@ ndis_setstate_80211(sc) wep.nw_keyidx = i; wep.nw_length = (sizeof(uint32_t) * 3) + wep.nw_keylen; - if (i == ic->ic_wep_txkey) + if (i == ic->ic_def_txkey) wep.nw_keyidx |= NDIS_80211_WEPKEY_TX; bcopy(ic->ic_nw_keys[i].wk_key, wep.nw_keydata, wep.nw_length); @@ -1440,7 +1440,7 @@ ndis_setstate_80211(sc) if (rval) device_printf(sc->ndis_dev, "enable WEP failed: %d\n", rval); -#ifndef IEEE80211_F_WEPON +#ifndef IEEE80211_F_PRIVACY if (ic->ic_wep_mode != IEEE80211_WEP_8021X && ic->ic_wep_mode != IEEE80211_WEP_ON) arg = NDIS_80211_PRIVFILT_ACCEPTALL; @@ -1564,7 +1564,7 @@ ndis_setstate_80211(sc) static void ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211com *ic = (void *)ifp; /* XXX */ struct ieee80211_node *ni = NULL; imr->ifm_status = IFM_AVALID; @@ -1606,7 +1606,7 @@ ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_MODE_11G: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G); break; - case IEEE80211_MODE_TURBO: + case IEEE80211_MODE_TURBO_A: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A) | IFM_IEEE80211_TURBO; break; @@ -1855,7 +1855,7 @@ ndis_ioctl(ifp, command, data, cr) case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->ndis_80211) { - error = ieee80211_ioctl(ifp, command, data, cr); + error = ieee80211_ioctl(&sc->ic, command, data, cr); if (error == ENETRESET) { ndis_setstate_80211(sc); /*ndis_init(sc);*/ @@ -1886,7 +1886,7 @@ ndis_ioctl(ifp, command, data, cr) default: sc->ndis_skip = 1; if (sc->ndis_80211) { - error = ieee80211_ioctl(ifp, command, data, cr); + error = ieee80211_ioctl(&sc->ic, command, data, cr); if (error == ENETRESET) { ndis_setstate_80211(sc); error = 0; diff --git a/sys/dev/netif/ray/if_ray.c b/sys/dev/netif/ray/if_ray.c index 5b449cc98c..12e70673a4 100644 --- a/sys/dev/netif/ray/if_ray.c +++ b/sys/dev/netif/ray/if_ray.c @@ -29,7 +29,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/ray/if_ray.c,v 1.47.2.4 2001/08/14 22:54:05 dmlb Exp $ - * $DragonFly: src/sys/dev/netif/ray/Attic/if_ray.c,v 1.27 2005/12/31 14:08:00 sephe Exp $ + * $DragonFly: src/sys/dev/netif/ray/Attic/if_ray.c,v 1.28 2006/05/18 13:51:45 sephe Exp $ * */ @@ -336,7 +336,8 @@ static void ray_rx_data (struct ray_softc *sc, struct mbuf *m0, u_int8_t siglev static void ray_rx_mgt (struct ray_softc *sc, struct mbuf *m0); static void ray_rx_mgt_auth (struct ray_softc *sc, struct mbuf *m0); static void ray_rx_mgt_beacon (struct ray_softc *sc, struct mbuf *m0); -static void ray_rx_mgt_info (struct ray_softc *sc, struct mbuf *m0, struct ieee80211_information *elements); +static void ray_rx_mgt_info (struct ray_softc *sc, struct mbuf *m0, + union ieee80211_information *elements); static void ray_rx_update_cache (struct ray_softc *sc, u_int8_t *src, u_int8_t siglev, u_int8_t antenna); static void ray_stop (struct ray_softc *sc, struct ray_comq_entry *com); static int ray_stop_user (struct ray_softc *sc); @@ -2178,7 +2179,7 @@ ray_rx_mgt_beacon(struct ray_softc *sc, struct mbuf *m0) { struct ieee80211_frame *header = mtod(m0, struct ieee80211_frame *); ieee80211_mgt_beacon_t beacon = (u_int8_t *)(header+1); - struct ieee80211_information elements; + union ieee80211_information elements; u_int64_t *timestamp; @@ -2195,7 +2196,8 @@ RAY_DPRINTF(sc, RAY_DBG_MGT, "capability\t0x%x", IEEE80211_BEACON_CAPABILITY(bea } static void -ray_rx_mgt_info(struct ray_softc *sc, struct mbuf *m0, struct ieee80211_information *elements) +ray_rx_mgt_info(struct ray_softc *sc, struct mbuf *m0, + union ieee80211_information *elements) { struct ifnet *ifp = &sc->arpcom.ac_if; struct ieee80211_frame *header = mtod(m0, struct ieee80211_frame *); diff --git a/sys/dev/netif/ray/if_rayvar.h b/sys/dev/netif/ray/if_rayvar.h index 35c1d1e2b1..ea651283c2 100644 --- a/sys/dev/netif/ray/if_rayvar.h +++ b/sys/dev/netif/ray/if_rayvar.h @@ -29,7 +29,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/ray/if_rayvar.h,v 1.12.2.4 2001/08/14 22:54:07 dmlb Exp $ - * $DragonFly: src/sys/dev/netif/ray/Attic/if_rayvar.h,v 1.4 2005/02/20 12:49:34 asmodai Exp $ + * $DragonFly: src/sys/dev/netif/ray/Attic/if_rayvar.h,v 1.5 2006/05/18 13:51:45 sephe Exp $ * */ @@ -322,3 +322,41 @@ static int mib_info[RAY_MIB_MAX+1][3] = RAY_MIB_INFO; #ifndef RAY_MAP_CM #define RAY_MAP_CM(sc) #endif /* RAY_MAP_CM */ + +/* + * Management information element payloads + */ +union ieee80211_information { + char ssid[IEEE80211_NWID_LEN+1]; + struct rates { + uint8_t *p; + } rates; + struct fh { + uint16_t dwell; + uint8_t set; + uint8_t pattern; + uint8_t index; + } fh; + struct ds { + uint8_t channel; + } ds; + struct cf { + uint8_t count; + uint8_t period; + uint8_t maxdur[2]; + uint8_t dur[2]; + } cf; + struct tim { + uint8_t count; + uint8_t period; + uint8_t bitctl; + /* uint8_t pvt[251]; The driver needs to use this. */ + } tim; + struct ibss { + uint16_t atim; + } ibss; + struct challenge { + uint8_t *p; + uint8_t len; + } challenge; +}; diff --git a/sys/dev/netif/wi/if_wi.c b/sys/dev/netif/wi/if_wi.c index 136f97cea2..998ec56536 100644 --- a/sys/dev/netif/wi/if_wi.c +++ b/sys/dev/netif/wi/if_wi.c @@ -31,8 +31,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/wi/if_wi.c,v 1.166 2004/04/01 00:38:45 sam Exp $ - * $DragonFly: src/sys/dev/netif/wi/if_wi.c,v 1.35 2005/12/31 14:25:04 sephe Exp $ + * $FreeBSD: src/sys/dev/wi/if_wi.c,v 1.180.2.7 2005/10/05 13:16:29 avatar Exp $ + * $DragonFly: src/sys/dev/netif/wi/if_wi.c,v 1.36 2006/05/18 13:51:45 sephe Exp $ */ /* @@ -143,6 +143,8 @@ static int wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int); static int wi_read_rid(struct wi_softc *, int, void *, int *); static int wi_write_rid(struct wi_softc *, int, void *, int); +static int wi_key_alloc(struct ieee80211com *, const struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); static int wi_newstate(struct ieee80211com *, enum ieee80211_state, int); static int wi_scan_ap(struct wi_softc *, u_int16_t, u_int16_t); @@ -252,7 +254,9 @@ wi_attach(device_t dev) ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + sc->sc_firmware_type = WI_NOTYPE; sc->wi_cmd_count = 500; + /* Reset the NIC. */ error = wi_reset(sc); if (error) @@ -297,8 +301,11 @@ wi_attach(device_t dev) ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_STA; - ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_AHDEMO; + ic->ic_caps = IEEE80211_C_PMGT | + IEEE80211_C_IBSS | + IEEE80211_C_WEP; ic->ic_state = IEEE80211_S_INIT; + ic->ic_max_aid = WI_MAX_AID; /* * Query the card for available channels and setup the @@ -457,11 +464,13 @@ wi_attach(device_t dev) /* * Call MI attach routine. */ - ieee80211_ifattach(ifp); + ieee80211_ifattach(ic); /* override state transition method */ sc->sc_newstate = ic->ic_newstate; + sc->sc_key_alloc = ic->ic_crypto.cs_key_alloc; + ic->ic_crypto.cs_key_alloc = wi_key_alloc; ic->ic_newstate = wi_newstate; - ieee80211_media_init(ifp, wi_media_change, wi_media_status); + ieee80211_media_init(ic, wi_media_change, wi_media_status); bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th), @@ -483,16 +492,19 @@ wi_attach(device_t dev) sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len); sc->sc_rx_th.wr_ihdr.it_present = htole32(WI_RX_RADIOTAP_PRESENT); - error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE, wi_intr, sc, &sc->wi_intrhand, ifp->if_serializer); if (error) { - ieee80211_ifdetach(ifp); + bpfdetach(ifp); + ieee80211_ifdetach(ic); device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); goto fail; } + if (bootverbose) + ieee80211_announce(ic); + return(0); fail: @@ -515,7 +527,8 @@ wi_detach(device_t dev) lwkt_serialize_exit(ifp->if_serializer); - ieee80211_ifdetach(ifp); + bpfdetach(ifp); + ieee80211_ifdetach(&sc->sc_ic); wi_free(dev); return (0); } @@ -674,8 +687,10 @@ wi_init(void *arg) IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN); - wi_write_val(sc, WI_RID_PM_ENABLED, - (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); + if (ic->ic_caps & IEEE80211_C_PMGT) { + wi_write_val(sc, WI_RID_PM_ENABLED, + (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); + } /* not yet common 802.11 configuration */ wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen); @@ -695,10 +710,10 @@ wi_init(void *arg) if (ic->ic_opmode == IEEE80211_M_HOSTAP && sc->sc_firmware_type == WI_INTERSIL) { - wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_lintval); + wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_bintval); wi_write_val(sc, WI_RID_BASIC_RATE, 0x03); /* 1, 2 */ wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */ - wi_write_val(sc, WI_RID_DTIM_PERIOD, 1); + wi_write_val(sc, WI_RID_DTIM_PERIOD, ic->ic_dtim_period); } /* @@ -717,8 +732,12 @@ wi_init(void *arg) } /* Configure WEP. */ - if (ic->ic_caps & IEEE80211_C_WEP) + if (ic->ic_caps & IEEE80211_C_WEP) { + sc->sc_cnfauthmode = ic->ic_bss->ni_authmode; wi_write_wep(sc); + } else { + sc->sc_encryption = 0; + } /* Set multicast filter. */ wi_write_multi(sc); @@ -749,9 +768,10 @@ wi_init(void *arg) ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; if (ic->ic_opmode == IEEE80211_M_AHDEMO || + ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_opmode == IEEE80211_M_HOSTAP) - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + ieee80211_create_ibss(ic, ic->ic_ibss_chan); /* Enable interrupts if not polling */ #ifdef DEVICE_POLLING @@ -799,7 +819,6 @@ wi_stop(struct ifnet *ifp, int disable) DELAY(100000); - ifp->if_flags &= ~(IFF_OACTIVE | IFF_RUNNING); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); if (sc->sc_enabled && !sc->wi_gone) { CSR_WRITE_2(sc, WI_INT_EN, 0); @@ -816,9 +835,9 @@ wi_stop(struct ifnet *ifp, int disable) sc->sc_tx_timer = 0; sc->sc_scan_timer = 0; - sc->sc_syn_timer = 0; sc->sc_false_syns = 0; sc->sc_naps = 0; + ifp->if_flags &= ~(IFF_OACTIVE | IFF_RUNNING); ifp->if_timer = 0; } @@ -833,10 +852,7 @@ wi_start(struct ifnet *ifp) struct wi_frame frmhdr; int cur, fid, off, error; - if (sc->wi_gone) - return; - - if (sc->sc_flags & WI_FLAGS_OUTRANGE) + if (sc->wi_gone || (sc->sc_flags & WI_FLAGS_OUTRANGE)) return; memset(&frmhdr, 0, sizeof(frmhdr)); @@ -866,6 +882,8 @@ wi_start(struct ifnet *ifp) frmhdr.wi_ehdr.ether_type = 0; wh = mtod(m0, struct ieee80211_frame *); } else { + struct ether_header *eh; + if (ic->ic_state != IEEE80211_S_RUN) break; m0 = ifq_poll(&ifp->if_snd); @@ -875,33 +893,50 @@ wi_start(struct ifnet *ifp) ifp->if_flags |= IFF_OACTIVE; break; } + ifq_dequeue(&ifp->if_snd, m0); + if (m0->m_len < sizeof(struct ether_header)) { + m0 = m_pullup(m0, sizeof(struct ether_header)); + if (m0 == NULL) { + ifp->if_oerrors++; + continue; + } + } + + eh = mtod(m0, struct ether_header *); + ni = ieee80211_find_txnode(ic, eh->ether_dhost); + if (ni == NULL) { + m_freem(m0); + ifp->if_oerrors++; + continue; + } + ifp->if_opackets++; m_copydata(m0, 0, ETHER_HDR_LEN, (caddr_t)&frmhdr.wi_ehdr); BPF_MTAP(ifp, m0); - m0 = ieee80211_encap(ifp, m0, &ni); + m0 = ieee80211_encap(ic, m0, ni); if (m0 == NULL) { + ieee80211_free_node(ni); ifp->if_oerrors++; continue; } wh = mtod(m0, struct ieee80211_frame *); - if (ic->ic_flags & IEEE80211_F_WEPON) - wh->i_fc[1] |= IEEE80211_FC1_WEP; - } if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0); frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX); - if (ic->ic_opmode == IEEE80211_M_HOSTAP && - (wh->i_fc[1] & IEEE80211_FC1_WEP)) { - if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) { + /* XXX check key for SWCRYPT instead of using operating mode */ + if ((wh->i_fc[1] & IEEE80211_FC1_WEP) && + (sc->sc_encryption & HOST_ENCRYPT)) { + if (ieee80211_crypto_encap(ic, ni, m0) == NULL) { + if (ni != NULL) + ieee80211_free_node(ni); + m_freem(m0); ifp->if_oerrors++; - if (ni && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); continue; } frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT); @@ -925,8 +960,8 @@ wi_start(struct ifnet *ifp) error = wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0 || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0; m_freem(m0); - if (ni && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); + if (ni != NULL) + ieee80211_free_node(ni); if (error) { ifp->if_oerrors++; continue; @@ -1014,20 +1049,8 @@ wi_watchdog(struct ifnet *ifp) ifp->if_timer = 1; } - if (sc->sc_syn_timer) { - if (--sc->sc_syn_timer == 0) { - struct ieee80211com *ic = (struct ieee80211com *) ifp; - DPRINTF2((ifp, "wi_watchdog: %d false syns\n", - sc->sc_false_syns)); - sc->sc_false_syns = 0; - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - sc->sc_syn_timer = 5; - } - ifp->if_timer = 1; - } - /* TODO: rate control */ - ieee80211_watchdog(ifp); + ieee80211_watchdog(&sc->sc_ic); } static int @@ -1120,7 +1143,7 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) ireq->i_len); break; default: - error = ieee80211_ioctl(ifp, cmd, data, cr); + error = ieee80211_ioctl(ic, cmd, data, cr); break; } break; @@ -1150,7 +1173,7 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) sc->sc_nodelen = ireq->i_len; break; default: - error = ieee80211_ioctl(ifp, cmd, data, cr); + error = ieee80211_ioctl(ic, cmd, data, cr); break; } break; @@ -1159,7 +1182,7 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) wi_init(sc); break; default: - error = ieee80211_ioctl(ifp, cmd, data, cr); + error = ieee80211_ioctl(ic, cmd, data, cr); break; } if (error == ENETRESET) { @@ -1194,7 +1217,7 @@ wi_media_status(struct ifnet *ifp, struct ifmediareq *imr) u_int16_t val; int rate, len; - if (sc->wi_gone || !sc->sc_enabled) { + if (sc->wi_gone) { /* hardware gone (e.g. ejected) */ imr->ifm_active = IFM_IEEE80211 | IFM_NONE; imr->ifm_status = 0; return; @@ -1202,26 +1225,30 @@ wi_media_status(struct ifnet *ifp, struct ifmediareq *imr) imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; + if (!sc->sc_enabled) { /* port !enabled, have no status */ + imr->ifm_active |= IFM_NONE; + return; + } if (ic->ic_state == IEEE80211_S_RUN && (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0) imr->ifm_status |= IFM_ACTIVE; len = sizeof(val); - if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) != 0) - rate = 0; - else { + if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 && + len == sizeof(val)) { /* convert to 802.11 rate */ + val = le16toh(val); rate = val * 2; if (sc->sc_firmware_type == WI_LUCENT) { - if (rate == 4 * 2) + if (rate == 10) rate = 11; /* 5.5Mbps */ - else if (rate == 5 * 2) - rate = 22; /* 11Mbps */ } else { if (rate == 4*2) rate = 11; /* 5.5Mbps */ else if (rate == 8*2) rate = 22; /* 11Mbps */ } + } else { + rate = 0; } imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B); switch (ic->ic_opmode) { @@ -1260,10 +1287,19 @@ wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN]) * change-of-BSSID indications. */ if ((ifp->if_flags & IFF_PROMISC) != 0 && - sc->sc_false_syns >= WI_MAX_FALSE_SYNS) + !ppsratecheck(&sc->sc_last_syn, &sc->sc_false_syns, + WI_MAX_FALSE_SYNS)) return; - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1); + /* + * XXX hack; we should create a new node with the new bssid + * and replace the existing ic_bss with it but since we don't + * process management frames to collect state we cheat by + * reusing the existing node as we know wi_newstate will be + * called and it will overwrite the node state. + */ + ieee80211_sta_join(ic, ieee80211_ref_node(ni)); } static void @@ -1435,6 +1471,17 @@ wi_rx_intr(struct wi_softc *sc) CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* + * WEP is decrypted by hardware and the IV + * is stripped. Clear WEP bit so we don't + * try to process it in ieee80211_input. + * XXX fix for TKIP, et. al. + */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } + if (sc->sc_drvbpf) { /* XXX replace divide by table */ sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5; @@ -1446,15 +1493,6 @@ wi_rx_intr(struct wi_softc *sc) bpf_ptap(sc->sc_drvbpf, m, &sc->sc_rx_th, sc->sc_rx_th_len); } - wh = mtod(m, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - /* - * WEP is decrypted by hardware. Clear WEP bit - * header for ieee80211_input(). - */ - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - } - /* synchronize driver's BSSID with firmware's BSSID */ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS) @@ -1463,29 +1501,19 @@ wi_rx_intr(struct wi_softc *sc) /* * Locate the node for sender, track state, and * then pass this node (referenced) up to the 802.11 - * layer for its use. We are required to pass - * something so we fallback to ic_bss when this frame - * is from an unknown sender. + * layer for its use. */ - if (ic->ic_opmode != IEEE80211_M_STA) { - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) - ni = ieee80211_ref_node(ic->ic_bss); - } else - ni = ieee80211_ref_node(ic->ic_bss); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) wh); /* * Send frame up for processing. */ - ieee80211_input(ifp, m, ni, rssi, rstamp); + ieee80211_input(ic, m, ni, rssi, rstamp); /* * The frame may have caused the node to be marked for * reclamation (e.g. in response to a DEAUTH message) * so use free_node here instead of unref_node. */ - if (ni == ic->ic_bss) - ieee80211_unref_node(&ni); - else - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); } static void @@ -1804,7 +1832,7 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) case WI_RID_TX_CRYPT_KEY: case WI_RID_DEFLT_CRYPT_KEYS: case WI_RID_TX_RATE: - return ieee80211_cfgget(ifp, cmd, data, cr); + return ieee80211_cfgget(ic, cmd, data, cr); case WI_RID_MICROWAVE_OVEN: if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) { @@ -1858,7 +1886,7 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) case WI_RID_READ_APS: if (ic->ic_opmode == IEEE80211_M_HOSTAP) - return ieee80211_cfgget(ifp, cmd, data, cr); + return ieee80211_cfgget(ic, cmd, data, cr); if (sc->sc_scan_timer > 0) { error = EINPROGRESS; break; @@ -1895,11 +1923,11 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) break; case WI_RID_READ_CACHE: - return ieee80211_cfgget(ifp, cmd, data, cr); + return ieee80211_cfgget(ic, cmd, data, cr); case WI_RID_SCAN_RES: /* compatibility interface */ if (ic->ic_opmode == IEEE80211_M_HOSTAP) - return ieee80211_cfgget(ifp, cmd, data, cr); + return ieee80211_cfgget(ic, cmd, data, cr); if (sc->sc_scan_timer > 0) { error = EINPROGRESS; break; @@ -1981,7 +2009,7 @@ wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) sc->sc_nodelen); break; default: - return ieee80211_cfgget(ifp, cmd, data, cr); + return ieee80211_cfgget(ic, cmd, data, cr); } break; } @@ -2080,7 +2108,7 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_TX_RATE: switch (le16toh(wreq.wi_val[0])) { case 3: - ic->ic_fixed_rate = -1; + ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; break; default: rs = &ic->ic_sup_rates[IEEE80211_MODE_11B]; @@ -2153,7 +2181,7 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) if (error) break; } - error = ieee80211_cfgset(ifp, cmd, data); + error = ieee80211_cfgset(ic, cmd, data); break; } return error; @@ -2166,7 +2194,7 @@ wi_write_txrate(struct wi_softc *sc) int i; u_int16_t rate; - if (ic->ic_fixed_rate < 0) + if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) rate = 0; /* auto */ else rate = (ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ic->ic_fixed_rate] & @@ -2214,6 +2242,24 @@ wi_write_txrate(struct wi_softc *sc) return wi_write_val(sc, WI_RID_TX_RATE, rate); } +static int +wi_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + struct wi_softc *sc = ic->ic_ifp->if_softc; + + /* + * When doing host encryption of outbound frames fail requests + * for keys that are not marked w/ the SWCRYPT flag so the + * net80211 layer falls back to s/w crypto. Note that we also + * fixup existing keys below to handle mode changes. + */ + if ((sc->sc_encryption & HOST_ENCRYPT) && + (k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) + return 0; + return sc->sc_key_alloc(ic, k, keyix, rxkeyix); +} + static int wi_write_wep(struct wi_softc *sc) { @@ -2225,27 +2271,30 @@ wi_write_wep(struct wi_softc *sc) switch (sc->sc_firmware_type) { case WI_LUCENT: - val = (ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0; + val = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0; error = wi_write_val(sc, WI_RID_ENCRYPTION, val); if (error) break; - error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_wep_txkey); + if (!(ic->ic_flags & IEEE80211_F_PRIVACY)) + break; + error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_def_txkey); if (error) break; memset(wkey, 0, sizeof(wkey)); for (i = 0; i < IEEE80211_WEP_NKID; i++) { - keylen = ic->ic_nw_keys[i].wk_len; + keylen = ic->ic_nw_keys[i].wk_keylen; wkey[i].wi_keylen = htole16(keylen); memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key, keylen); } error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS, wkey, sizeof(wkey)); + sc->sc_encryption = 0; break; case WI_INTERSIL: case WI_SYMBOL: - if (ic->ic_flags & IEEE80211_F_WEPON) { + if (ic->ic_flags & IEEE80211_F_PRIVACY) { /* * ONLY HWB3163 EVAL-CARD Firmware version * less than 0.8 variant2 @@ -2262,6 +2311,7 @@ wi_write_wep(struct wi_softc *sc) } wi_write_val(sc, WI_RID_CNFAUTHMODE, sc->sc_cnfauthmode); + /* XXX should honor IEEE80211_F_DROPUNENC */ val = PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED; /* * Encryption firmware has a bug for HostAP mode. @@ -2277,17 +2327,25 @@ wi_write_wep(struct wi_softc *sc) error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val); if (error) break; + sc->sc_encryption = val; + if ((val & PRIVACY_INVOKED) == 0) + break; error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, - ic->ic_wep_txkey); + ic->ic_def_txkey); if (error) break; + if (val & HOST_DECRYPT) + break; /* * It seems that the firmware accept 104bit key only if * all the keys have 104bit length. We get the length of * the transmit key and use it for all other keys. * Perhaps we should use software WEP for such situation. */ - keylen = ic->ic_nw_keys[ic->ic_wep_txkey].wk_len; + if (ic->ic_def_txkey != IEEE80211_KEYIX_NONE) + keylen = ic->ic_nw_keys[ic->ic_def_txkey].wk_keylen; + else /* XXX should not hapen */ + keylen = IEEE80211_WEP_KEYLEN; if (keylen > IEEE80211_WEP_KEYLEN) keylen = 13; /* 104bit keys */ else @@ -2300,6 +2358,19 @@ wi_write_wep(struct wi_softc *sc) } break; } + /* + * XXX horrible hack; insure pre-existing keys are + * setup properly to do s/w crypto. + */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + struct ieee80211_key *k = &ic->ic_nw_keys[i]; + if (k->wk_flags & IEEE80211_KEY_XMIT) { + if (sc->sc_encryption & HOST_ENCRYPT) + k->wk_flags |= IEEE80211_KEY_SWCRYPT; + else + k->wk_flags &= ~IEEE80211_KEY_SWCRYPT; + } + } return error; } @@ -2307,15 +2378,10 @@ static int wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) { int i, s = 0; - static volatile int count = 0; if (sc->wi_gone) return (ENODEV); - if (count > 0) - panic("Hey partner, hold on there!"); - count++; - /* wait for the busy bit to clear */ for (i = sc->wi_cmd_count; i > 0; i--) { /* 500ms */ if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) @@ -2325,7 +2391,6 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) if (i == 0) { if_printf(&sc->sc_ic.ic_if, "wi_cmd: busy bit won't clear.\n" ); sc->wi_gone = 1; - count--; return(ETIMEDOUT); } @@ -2349,7 +2414,6 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) s = CSR_READ_2(sc, WI_STATUS); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); if (s & WI_STAT_CMD_RESULT) { - count--; return(EIO); } break; @@ -2357,7 +2421,6 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) DELAY(WI_DELAY); } - count--; if (i == WI_TIMEOUT) { if_printf(&sc->sc_ic.ic_if, "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s); @@ -2514,12 +2577,12 @@ wi_alloc_fid(struct wi_softc *sc, int len, int *idp) for (i = 0; i < WI_TIMEOUT; i++) { if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) break; - if (i == WI_TIMEOUT) { - if_printf(&sc->sc_ic.ic_if, "timeout in alloc\n"); - return ETIMEDOUT; - } DELAY(1); } + if (i == WI_TIMEOUT) { + if_printf(&sc->sc_ic.ic_if, "timeout in alloc\n"); + return ETIMEDOUT; + } *idp = CSR_READ_2(sc, WI_ALLOC_FID); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); return 0; @@ -2590,40 +2653,48 @@ wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) ieee80211_state_name[ic->ic_state], ieee80211_state_name[nstate])); + /* + * Internal to the driver the INIT and RUN states are used + * so bypass the net80211 state machine for other states. + * Beware however that this requires use to net80211 state + * management that otherwise would be handled for us. + */ switch (nstate) { case IEEE80211_S_INIT: - ic->ic_flags &= ~IEEE80211_F_SIBSS; sc->sc_flags &= ~WI_FLAGS_OUTRANGE; - return (*sc->sc_newstate)(ic, nstate, arg); + return sc->sc_newstate(ic, nstate, arg); case IEEE80211_S_RUN: sc->sc_flags &= ~WI_FLAGS_OUTRANGE; buflen = IEEE80211_ADDR_LEN; + IEEE80211_ADDR_COPY(old_bssid, ni->ni_bssid); wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen); IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid); buflen = sizeof(val); wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen); /* XXX validate channel */ ni->ni_chan = &ic->ic_channels[le16toh(val)]; + ic->ic_curchan = ni->ni_chan; + ic->ic_ibss_chan = ni->ni_chan; + sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = htole16(ni->ni_chan->ic_freq); sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = htole16(ni->ni_chan->ic_flags); - if (IEEE80211_ADDR_EQ(old_bssid, ni->ni_bssid)) - sc->sc_false_syns++; - else - sc->sc_false_syns = 0; - - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - ni->ni_esslen = ic->ic_des_esslen; - memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); - ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B]; - ni->ni_intval = ic->ic_lintval; - ni->ni_capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) - ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; - } else { + /* + * XXX hack; unceremoniously clear + * IEEE80211_F_DROPUNENC when operating with + * wep enabled so we don't drop unencoded frames + * at the 802.11 layer. This is necessary because + * we must strip the WEP bit from the 802.11 header + * before passing frames to ieee80211_input because + * the card has already stripped the WEP crypto + * header from the packet. + */ + if (ic->ic_flags & IEEE80211_F_PRIVACY) + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) { /* XXX check return value */ buflen = sizeof(ssid); wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen); @@ -2632,7 +2703,7 @@ wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/ memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen); } - break; + return sc->sc_newstate(ic, nstate, arg); case IEEE80211_S_SCAN: case IEEE80211_S_AUTH: @@ -2657,8 +2728,8 @@ wi_scan_ap(struct wi_softc *sc, u_int16_t chanmask, u_int16_t txrate) (void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0); break; case WI_INTERSIL: - val[0] = chanmask; /* channel */ - val[1] = txrate; /* tx rate */ + val[0] = htole16(chanmask); /* channel */ + val[1] = htole16(txrate); /* tx rate */ error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val)); break; case WI_SYMBOL: diff --git a/sys/dev/netif/wi/if_wivar.h b/sys/dev/netif/wi/if_wivar.h index 18446cbe90..9bf01e2bc5 100644 --- a/sys/dev/netif/wi/if_wivar.h +++ b/sys/dev/netif/wi/if_wivar.h @@ -31,8 +31,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/wi/if_wivar.h,v 1.22 2004/04/01 00:38:45 sam Exp $ - * $DragonFly: src/sys/dev/netif/wi/if_wivar.h,v 1.9 2005/12/16 21:05:48 dillon Exp $ + * $FreeBSD: src/sys/dev/wi/if_wivar.h,v 1.25.2.1 2005/10/05 13:13:46 avatar Exp $ + * $DragonFly: src/sys/dev/netif/wi/if_wivar.h,v 1.10 2006/05/18 13:51:45 sephe Exp $ */ #if 0 @@ -60,10 +60,15 @@ #define WI_RID_ROAMING_MODE 0xFC2D #define WI_RID_CUR_TX_RATE 0xFD44 /* current TX rate */ +#define WI_MAX_AID 256 /* max stations for ap operation */ + struct wi_softc { struct ieee80211com sc_ic; int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); + int (*sc_key_alloc)(struct ieee80211com *, + const struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); int wi_gone; int sc_enabled; int sc_reset; @@ -114,6 +119,7 @@ struct wi_softc { u_int16_t sc_roaming_mode; u_int16_t sc_microwave_oven; u_int16_t sc_authtype; + u_int16_t sc_encryption; int sc_nodelen; char sc_nodename[IEEE80211_NWID_LEN]; @@ -130,7 +136,6 @@ struct wi_softc { int sc_txcur; /* index of current TX*/ int sc_tx_timer; int sc_scan_timer; - int sc_syn_timer; struct wi_counters sc_stats; u_int16_t sc_ibss_port; @@ -156,6 +161,7 @@ struct wi_softc { u_int16_t wi_confbits_param0; } wi_debug; + struct timeval sc_last_syn; int sc_false_syns; u_int16_t sc_txbuf[IEEE80211_MAX_LEN/2]; diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 01212a95ca..906646dc9d 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -4,7 +4,7 @@ # Check the LINT configuration file in sys/i386/conf, for an # exhaustive list of options. # -# $DragonFly: src/sys/i386/conf/Attic/GENERIC,v 1.33 2006/05/11 08:23:20 swildner Exp $ +# $DragonFly: src/sys/i386/conf/Attic/GENERIC,v 1.34 2006/05/18 13:51:45 sephe Exp $ machine i386 cpu I386_CPU @@ -214,20 +214,24 @@ device ep device fe0 at isa? disable port 0x300 # Xircom Ethernet device xe -# Generic 802.11 stack, used by wi -device wlan -# PRISM I IEEE 802.11b wireless NIC. -device awi -# WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really -# exists only as a PCMCIA device, so there is no ISA attachment needed -# and resources will always be dynamically assigned by the pccard code. -device wi -# Aironet 4500/4800 802.11 wireless NICs. Note: the declaration below will + +# Wireless NIC cards +device wlan # 802.11 support +device wlan_ccmp # 802.11 CCMP support +device wlan_tkip # 802.11 TKIP support +device wlan_wep # 802.11 WEP support +# Aironet 4500/4800 802.11 wireless NICs. Note: the declaration below will # work for PCMCIA and PCI cards, as well as ISA cards set to ISA PnP # mode (the factory default). If you set the switches on your ISA # card for a manually chosen I/O address and IRQ, you must specify # those parameters here. device an +#device awi # PRISM I IEEE 802.11b wireless NIC +# WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really +# exists only as a PCMCIA device, so there is no ISA attachment needed +# and resources will always be dynamically assigned by the pccard code. +device wi + # The probe order of these is presently determined by i386/isa/isa_compat.c. device ie0 at isa? disable port 0x300 irq 10 iomem 0xd0000 #device le0 at isa? disable port 0x300 irq 5 iomem 0xd0000 diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 209e392b63..52d9077086 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.75 2006/05/14 18:07:29 swildner Exp $ +# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.76 2006/05/18 13:51:45 sephe Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1411,10 +1411,15 @@ device sr0 at isa? port 0x300 irq 5 iomem 0xd0000 device sn0 at isa? port 0x300 irq 10 # Wlan support is mandatory for some wireless LAN devices. -device wlan # Wireless LAN support +device wlan # 802.11 support +device wlan_acl # 802.11 MAC-based access control for AP +device wlan_ccmp # 802.11 CCMP support +device wlan_tkip # 802.11 TKIP support +device wlan_wep # 802.11 WEP support +device wlan_xauth # 802.11 WPA or 802.1x authentication for AP options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output -device awi # AMD PCnetMobile +#device awi # AMD PCnetMobile device an # Aironet Communications 4500/4800 device ipw # Intel PRO/Wireless 2100 device iwi # Intel PRO/Wireless 2200BG/2915ABG diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h index 62f805a0a1..ec8040c3da 100644 --- a/sys/net/ethernet.h +++ b/sys/net/ethernet.h @@ -2,7 +2,7 @@ * Fundamental constants relating to ethernet. * * $FreeBSD: src/sys/net/ethernet.h,v 1.12.2.8 2002/12/01 14:03:09 sobomax Exp $ - * $DragonFly: src/sys/net/ethernet.h,v 1.11 2005/02/11 22:25:57 joerg Exp $ + * $DragonFly: src/sys/net/ethernet.h,v 1.12 2006/05/18 13:51:45 sephe Exp $ * */ @@ -326,6 +326,7 @@ extern const uint8_t etherbroadcastaddr[ETHER_ADDR_LEN]; #define ETHERTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ #define ETHERTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ #define ETHERTYPE_LANPROBE 0x8888 /* HP LanProbe test? */ +#define ETHERTYPE_PAE 0x888e /* EAPOL PAE/802.1x */ #define ETHERTYPE_LOOPBACK 0x9000 /* Loopback: used to test interfaces */ #define ETHERTYPE_LBACK ETHERTYPE_LOOPBACK /* DEC MOP loopback */ #define ETHERTYPE_XNSSM 0x9001 /* 3Com (Formerly Bridge Communications), XNS Systems Management */ diff --git a/sys/net/route.h b/sys/net/route.h index e005fb49eb..c48fc52305 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -82,7 +82,7 @@ * * @(#)route.h 8.4 (Berkeley) 1/9/95 * $FreeBSD: src/sys/net/route.h,v 1.36.2.5 2002/02/01 11:48:01 ru Exp $ - * $DragonFly: src/sys/net/route.h,v 1.17 2006/05/06 02:43:12 dillon Exp $ + * $DragonFly: src/sys/net/route.h,v 1.18 2006/05/18 13:51:45 sephe Exp $ */ #ifndef _NET_ROUTE_H_ @@ -262,6 +262,7 @@ struct rt_msghdr { #define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ #define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ #define RTM_IFANNOUNCE 0x11 /* iface arrival/departure */ +#define RTM_IEEE80211 0x12 /* IEEE80211 wireless event */ /* * Bitmask values for rtm_inits and rmx_locks. @@ -329,6 +330,7 @@ struct ucred; void route_init (void); void rt_dstmsg(int type, struct sockaddr *dst, int error); int rt_getifa (struct rt_addrinfo *); +void rt_ieee80211msg(struct ifnet *, int, void *, size_t); void rt_ifannouncemsg (struct ifnet *, int); void rt_ifmsg (struct ifnet *); int rt_llroute (struct sockaddr *dst, struct rtentry *rt0, diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 8d5d69c3c4..4b31edfec4 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -82,7 +82,7 @@ * * @(#)rtsock.c 8.7 (Berkeley) 10/12/95 * $FreeBSD: src/sys/net/rtsock.c,v 1.44.2.11 2002/12/04 14:05:41 ru Exp $ - * $DragonFly: src/sys/net/rtsock.c,v 1.31 2006/05/06 02:43:12 dillon Exp $ + * $DragonFly: src/sys/net/rtsock.c,v 1.32 2006/05/18 13:51:45 sephe Exp $ */ #include "opt_sctp.h" @@ -743,6 +743,7 @@ rt_msghdrsize(int type) case RTM_IFINFO: return sizeof(struct if_msghdr); case RTM_IFANNOUNCE: + case RTM_IEEE80211: return sizeof(struct if_announcemsghdr); default: return sizeof(struct rt_msghdr); @@ -1054,33 +1055,84 @@ rt_newmaddrmsg(int cmd, struct ifmultiaddr *ifma) rts_input(m, familyof(ifma->ifma_addr)); } -/* - * This is called to generate routing socket messages indicating - * network interface arrival and departure. - */ -void -rt_ifannouncemsg(struct ifnet *ifp, int what) +static struct mbuf * +rt_makeifannouncemsg(struct ifnet *ifp, int type, int what, + struct rt_addrinfo *info) { - struct rt_addrinfo addrinfo; - struct mbuf *m; struct if_announcemsghdr *ifan; + struct mbuf *m; if (route_cb.any_count == 0) - return; + return NULL; - bzero(&addrinfo, sizeof addrinfo); - m = rt_msg_mbuf(RTM_IFANNOUNCE, &addrinfo); + bzero(info, sizeof(*info)); + m = rt_msg_mbuf(type, info); if (m == NULL) - return; + return NULL; ifan = mtod(m, struct if_announcemsghdr *); ifan->ifan_index = ifp->if_index; strlcpy(ifan->ifan_name, ifp->if_xname, sizeof ifan->ifan_name); ifan->ifan_what = what; + return m; +} + +/* + * This is called to generate routing socket messages indicating + * IEEE80211 wireless events. + * XXX we piggyback on the RTM_IFANNOUNCE msg format in a clumsy way. + */ +void +rt_ieee80211msg(struct ifnet *ifp, int what, void *data, size_t data_len) +{ + struct rt_addrinfo info; + struct mbuf *m; + m = rt_makeifannouncemsg(ifp, RTM_IEEE80211, what, &info); + if (m == NULL) + return; + + /* + * Append the ieee80211 data. Try to stick it in the + * mbuf containing the ifannounce msg; otherwise allocate + * a new mbuf and append. + * + * NB: we assume m is a single mbuf. + */ + if (data_len > M_TRAILINGSPACE(m)) { + struct mbuf *n = m_get(M_NOWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + return; + } + bcopy(data, mtod(n, void *), data_len); + n->m_len = data_len; + m->m_next = n; + } else if (data_len > 0) { + bcopy(data, mtod(m, u_int8_t *) + m->m_len, data_len); + m->m_len += data_len; + } + if (m->m_flags & M_PKTHDR) + m->m_pkthdr.len += data_len; + mtod(m, struct if_announcemsghdr *)->ifan_msglen += data_len; rts_input(m, 0); } +/* + * This is called to generate routing socket messages indicating + * network interface arrival and departure. + */ +void +rt_ifannouncemsg(struct ifnet *ifp, int what) +{ + struct rt_addrinfo addrinfo; + struct mbuf *m; + + m = rt_makeifannouncemsg(ifp, RTM_IFANNOUNCE, what, &addrinfo); + if (m != NULL) + rts_input(m, 0); +} + static int resizewalkarg(struct walkarg *w, int len) { diff --git a/sys/netproto/802_11/Makefile b/sys/netproto/802_11/Makefile index 5367850446..ad5fc7d381 100644 --- a/sys/netproto/802_11/Makefile +++ b/sys/netproto/802_11/Makefile @@ -1,16 +1,5 @@ -# $DragonFly: src/sys/netproto/802_11/Makefile,v 1.4 2005/02/18 11:41:42 corecode Exp $ +# $DragonFly: src/sys/netproto/802_11/Makefile,v 1.5 2006/05/18 13:51:46 sephe Exp $ -KMOD = wlan -SRCS = ieee80211.c ieee80211_crypto.c ieee80211_input.c ieee80211_ioctl.c -SRCS += ieee80211_node.c ieee80211_output.c ieee80211_proto.c -SRCS += bus_if.h device_if.h opt_inet.h opt_ipx.h +SUBDIR= wlan wlan_acl wlan_ccmp wlan_tkip wlan_wep wlan_xauth -.if !defined(BUILDING_WITH_KERNEL) -opt_inet.h: - echo "#define INET 1" > ${.TARGET} - -opt_ipx.h: - echo "#define IPX 1" > ${.TARGET} -.endif - -.include +.include diff --git a/sys/netproto/802_11/_ieee80211.h b/sys/netproto/802_11/_ieee80211.h new file mode 100644 index 0000000000..8201369e29 --- /dev/null +++ b/sys/netproto/802_11/_ieee80211.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/_ieee80211.h,v 1.2.2.1 2005/09/03 22:40:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/_ieee80211.h,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ +#ifndef _NET80211__IEEE80211_H_ +#define _NET80211__IEEE80211_H_ + +enum ieee80211_phytype { + IEEE80211_T_DS, /* direct sequence spread spectrum */ + IEEE80211_T_FH, /* frequency hopping */ + IEEE80211_T_OFDM, /* frequency division multiplexing */ + IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ +}; +#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ + +/* XXX not really a mode; there are really multiple PHY's */ +enum ieee80211_phymode { + IEEE80211_MODE_AUTO = 0, /* autoselect */ + IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ + IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ + IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ + IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ + IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */ + IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */ +}; +#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO_G+1) + +enum ieee80211_opmode { + IEEE80211_M_STA = 1, /* infrastructure station */ + IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ + IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ + IEEE80211_M_HOSTAP = 6, /* Software Access Point */ + IEEE80211_M_MONITOR = 8 /* Monitor mode */ +}; + +/* + * 802.11g protection mode. + */ +enum ieee80211_protmode { + IEEE80211_PROT_NONE = 0, /* no protection */ + IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ + IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +/* + * Authentication mode. + */ +enum ieee80211_authmode { + IEEE80211_AUTH_NONE = 0, + IEEE80211_AUTH_OPEN = 1, /* open */ + IEEE80211_AUTH_SHARED = 2, /* shared-key */ + IEEE80211_AUTH_8021X = 3, /* 802.1x */ + IEEE80211_AUTH_AUTO = 4, /* auto-select/accept */ + /* NB: these are used only for ioctls */ + IEEE80211_AUTH_WPA = 5, /* WPA/RSN w/ 802.1x/PSK */ +}; + +/* + * Roaming mode is effectively who controls the operation + * of the 802.11 state machine when operating as a station. + * State transitions are controlled either by the driver + * (typically when management frames are processed by the + * hardware/firmware), the host (auto/normal operation of + * the 802.11 layer), or explicitly through ioctl requests + * when applications like wpa_supplicant want control. + */ +enum ieee80211_roamingmode { + IEEE80211_ROAMING_DEVICE= 0, /* driver/hardware control */ + IEEE80211_ROAMING_AUTO = 1, /* 802.11 layer control */ + IEEE80211_ROAMING_MANUAL= 2, /* application control */ +}; + +/* + * Channels are specified by frequency and attributes. + */ +struct ieee80211_channel { + uint16_t ic_freq; /* setting in Mhz */ + uint16_t ic_flags; /* see below */ +}; + +#define IEEE80211_CHAN_MAX 255 +#define IEEE80211_CHAN_BYTES 32 /* howmany(IEEE80211_CHAN_MAX, NBBY) */ +#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ +#define IEEE80211_CHAN_ANYC \ + ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) + +/* bits 0-3 are for private use by drivers */ +/* channel attributes */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* + * Useful combinations of channel characteristics. + */ +#define IEEE80211_CHAN_FHSS \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) +#define IEEE80211_CHAN_A \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_B \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) +#define IEEE80211_CHAN_PUREG \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) +#define IEEE80211_CHAN_T \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) +#define IEEE80211_CHAN_108G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) + +#define IEEE80211_IS_CHAN_FHSS(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) +#define IEEE80211_IS_CHAN_A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) +#define IEEE80211_IS_CHAN_B(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) +#define IEEE80211_IS_CHAN_PUREG(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) +#define IEEE80211_IS_CHAN_G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) +#define IEEE80211_IS_CHAN_ANYG(_c) \ + (IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c)) +#define IEEE80211_IS_CHAN_T(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) +#define IEEE80211_IS_CHAN_108G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) + +#define IEEE80211_IS_CHAN_2GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) +#define IEEE80211_IS_CHAN_5GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) +#define IEEE80211_IS_CHAN_OFDM(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) +#define IEEE80211_IS_CHAN_CCK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) +#define IEEE80211_IS_CHAN_GFSK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) + +/* ni_chan encoding for FH phy */ +#define IEEE80211_FH_CHANMOD 80 +#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) +#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) +#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) + +/* + * 802.11 rate set. + */ +#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ +#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ + +struct ieee80211_rateset { + uint8_t rs_nrates; + uint8_t rs_rates[IEEE80211_RATE_MAXSIZE]; +}; + +#endif /* _NET80211__IEEE80211_H_ */ diff --git a/sys/netproto/802_11/ieee80211.h b/sys/netproto/802_11/ieee80211.h index a8c27b23d8..b8f64a0010 100644 --- a/sys/netproto/802_11/ieee80211.h +++ b/sys/netproto/802_11/ieee80211.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,11 @@ * (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: src/sys/net80211/ieee80211.h,v 1.5 2004/04/05 17:47:40 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.9.2.1 2005/07/29 23:31:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ - -#ifndef _NETPROTO_802_11_IEEE80211_H_ -#define _NETPROTO_802_11_IEEE80211_H_ +#ifndef _NET80211_IEEE80211_H_ +#define _NET80211_IEEE80211_H_ /* * 802.11 protocol definitions. @@ -42,7 +41,7 @@ #define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ /* is 802.11 address multicast/broadcast? */ -#define IEEE80211_IS_MULTICAST(a) ETHER_IS_MULTICAST(a) +#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01) /* IEEE 802.11 PLCP header */ struct ieee80211_plcp_hdr { @@ -51,10 +50,10 @@ struct ieee80211_plcp_hdr { uint8_t i_service; uint16_t i_length; uint16_t i_crc; -} __attribute__((__packed__)); +} __packed; -#define IEEE80211_PLCP_SFD 0xF3A0 -#define IEEE80211_PLCP_SERVICE 0x00 +#define IEEE80211_PLCP_SFD 0xF3A0 +#define IEEE80211_PLCP_SERVICE 0x00 /* * generic definitions for IEEE 802.11 frames @@ -68,7 +67,7 @@ struct ieee80211_frame { uint8_t i_seq[2]; /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ /* see below */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_qosframe { uint8_t i_fc[2]; @@ -80,7 +79,7 @@ struct ieee80211_qosframe { uint8_t i_qos[2]; /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ /* see below */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_qoscntl { uint8_t i_qos[2]; @@ -94,7 +93,7 @@ struct ieee80211_frame_addr4 { uint8_t i_addr3[IEEE80211_ADDR_LEN]; uint8_t i_seq[2]; uint8_t i_addr4[IEEE80211_ADDR_LEN]; -} __attribute__((__packed__)); +} __packed; struct ieee80211_qosframe_addr4 { @@ -106,48 +105,7 @@ struct ieee80211_qosframe_addr4 { uint8_t i_seq[2]; uint8_t i_addr4[IEEE80211_ADDR_LEN]; uint8_t i_qos[2]; -} __attribute__((__packed__)); - -/* - * Management Notification Frame - */ -struct ieee80211_mnf { - uint8_t mnf_category; - uint8_t mnf_action; - uint8_t mnf_dialog; - uint8_t mnf_status; -} __attribute__((__packed__)); -#define MNF_SETUP_REQ 0 -#define MNF_SETUP_RESP 1 -#define MNF_TEARDOWN 2 - -/* - * WME/802.11e Tspec Element - */ -struct ieee80211_wme_tspec { - uint8_t ts_id; - uint8_t ts_len; - uint8_t ts_oui[3]; - uint8_t ts_oui_type; - uint8_t ts_oui_subtype; - uint8_t ts_version; - uint8_t ts_tsinfo[3]; - uint8_t ts_nom_msdu[2]; - uint8_t ts_max_msdu[2]; - uint8_t ts_min_svc[4]; - uint8_t ts_max_svc[4]; - uint8_t ts_inactv_intv[4]; - uint8_t ts_susp_intv[4]; - uint8_t ts_start_svc[4]; - uint8_t ts_min_rate[4]; - uint8_t ts_mean_rate[4]; - uint8_t ts_max_burst[4]; - uint8_t ts_min_phy[4]; - uint8_t ts_peak_rate[4]; - uint8_t ts_delay[4]; - uint8_t ts_surplus[2]; - uint8_t ts_medium_time[2]; -} __attribute__((__packed__)); +} __packed; #define IEEE80211_FC0_VERSION_MASK 0x03 #define IEEE80211_FC0_VERSION_SHIFT 0 @@ -189,6 +147,7 @@ struct ieee80211_wme_tspec { #define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60 #define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70 #define IEEE80211_FC0_SUBTYPE_QOS 0x80 +#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0 #define IEEE80211_FC1_DIR_MASK 0x03 #define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ @@ -212,9 +171,121 @@ struct ieee80211_wme_tspec { #define IEEE80211_QOS_TXOP 0x00ff /* bit 8 is reserved */ -#define IEEE80211_QOS_ACKPOLICY 0x0600 -#define IEEE80211_QOS_ESOP 0x0800 -#define IEEE80211_QOS_TID 0xf000 +#define IEEE80211_QOS_ACKPOLICY 0x60 +#define IEEE80211_QOS_ACKPOLICY_S 5 +#define IEEE80211_QOS_ESOP 0x10 +#define IEEE80211_QOS_ESOP_S 4 +#define IEEE80211_QOS_TID 0x0f + +/* does frame have QoS sequence control data */ +#define IEEE80211_QOS_HAS_SEQ(wh) \ + (((wh)->i_fc[0] & \ + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + +/* + * WME/802.11e information element. + */ +struct ieee80211_wme_info { + uint8_t wme_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t wme_len; /* length in bytes */ + uint8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */ + uint8_t wme_type; /* OUI type */ + uint8_t wme_subtype; /* OUI subtype */ + uint8_t wme_version; /* spec revision */ + uint8_t wme_info; /* QoS info */ +} __packed; + +/* + * WME/802.11e Tspec Element + */ +struct ieee80211_wme_tspec { + uint8_t ts_id; + uint8_t ts_len; + uint8_t ts_oui[3]; + uint8_t ts_oui_type; + uint8_t ts_oui_subtype; + uint8_t ts_version; + uint8_t ts_tsinfo[3]; + uint8_t ts_nom_msdu[2]; + uint8_t ts_max_msdu[2]; + uint8_t ts_min_svc[4]; + uint8_t ts_max_svc[4]; + uint8_t ts_inactv_intv[4]; + uint8_t ts_susp_intv[4]; + uint8_t ts_start_svc[4]; + uint8_t ts_min_rate[4]; + uint8_t ts_mean_rate[4]; + uint8_t ts_max_burst[4]; + uint8_t ts_min_phy[4]; + uint8_t ts_peak_rate[4]; + uint8_t ts_delay[4]; + uint8_t ts_surplus[2]; + uint8_t ts_medium_time[2]; +} __packed; + +/* + * WME AC parameter field + */ +struct ieee80211_wme_acparams { + uint8_t acp_aci_aifsn; + uint8_t acp_logcwminmax; + uint16_t acp_txop; +} __packed; + +#define WME_NUM_AC 4 /* 4 AC categories */ + +#define WME_PARAM_ACI 0x60 /* Mask for ACI field */ +#define WME_PARAM_ACI_S 5 /* Shift for ACI field */ +#define WME_PARAM_ACM 0x10 /* Mask for ACM bit */ +#define WME_PARAM_ACM_S 4 /* Shift for ACM bit */ +#define WME_PARAM_AIFSN 0x0f /* Mask for aifsn field */ +#define WME_PARAM_AIFSN_S 0 /* Shift for aifsn field */ +#define WME_PARAM_LOGCWMIN 0x0f /* Mask for CwMin field (in log) */ +#define WME_PARAM_LOGCWMIN_S 0 /* Shift for CwMin field */ +#define WME_PARAM_LOGCWMAX 0xf0 /* Mask for CwMax field (in log) */ +#define WME_PARAM_LOGCWMAX_S 4 /* Shift for CwMax field */ + +#define WME_AC_TO_TID(_ac) ( \ + ((_ac) == WME_AC_VO) ? 6 : \ + ((_ac) == WME_AC_VI) ? 5 : \ + ((_ac) == WME_AC_BK) ? 1 : \ + 0) + +#define TID_TO_WME_AC(_tid) ( \ + ((_tid) < 1) ? WME_AC_BE : \ + ((_tid) < 3) ? WME_AC_BK : \ + ((_tid) < 6) ? WME_AC_VI : \ + WME_AC_VO) + +/* + * WME Parameter Element + */ +struct ieee80211_wme_param { + uint8_t param_id; + uint8_t param_len; + uint8_t param_oui[3]; + uint8_t param_oui_type; + uint8_t param_oui_sybtype; + uint8_t param_version; + uint8_t param_qosInfo; +#define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */ + uint8_t param_reserved; + struct ieee80211_wme_acparams params_acParams[WME_NUM_AC]; +} __packed; + +/* + * Management Notification Frame + */ +struct ieee80211_mnf { + uint8_t mnf_category; + uint8_t mnf_action; + uint8_t mnf_dialog; + uint8_t mnf_status; +} __packed; +#define MNF_SETUP_REQ 0 +#define MNF_SETUP_RESP 1 +#define MNF_TEARDOWN 2 /* * Control frames. @@ -225,7 +296,7 @@ struct ieee80211_frame_min { uint8_t i_addr1[IEEE80211_ADDR_LEN]; uint8_t i_addr2[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_rts { uint8_t i_fc[2]; @@ -233,21 +304,21 @@ struct ieee80211_frame_rts { uint8_t i_ra[IEEE80211_ADDR_LEN]; uint8_t i_ta[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_cts { uint8_t i_fc[2]; uint8_t i_dur[2]; uint8_t i_ra[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_ack { uint8_t i_fc[2]; uint8_t i_dur[2]; uint8_t i_ra[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_pspoll { uint8_t i_fc[2]; @@ -255,7 +326,7 @@ struct ieee80211_frame_pspoll { uint8_t i_bssid[IEEE80211_ADDR_LEN]; uint8_t i_ta[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ uint8_t i_fc[2]; @@ -263,7 +334,7 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ uint8_t i_ra[IEEE80211_ADDR_LEN]; uint8_t i_bssid[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; /* * BEACON management packets @@ -303,6 +374,8 @@ typedef uint8_t *ieee80211_mgt_beacon_t; * 802.11i/WPA information element (maximally sized). */ struct ieee80211_ie_wpa { + uint8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t wpa_len; /* length in bytes */ uint8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ uint8_t wpa_type; /* OUI type */ uint16_t wpa_version; /* spec revision */ @@ -311,95 +384,70 @@ struct ieee80211_ie_wpa { uint32_t wpa_uciphers[8];/* ciphers */ uint16_t wpa_authselcnt; /* authentication selector cnt*/ uint32_t wpa_authsels[8];/* selectors */ -} __attribute__((__packed__)); + uint16_t wpa_caps; /* 802.11i capabilities */ + uint16_t wpa_pmkidcnt; /* 802.11i pmkid count */ + uint16_t wpa_pmkids[8]; /* 802.11i pmkids */ +} __packed; /* - * Management information elements + * Management information element payloads. */ -struct ieee80211_information { - char ssid[IEEE80211_NWID_LEN+1]; - struct rates { - uint8_t *p; - } rates; - struct fh { - uint16_t dwell; - uint8_t set; - uint8_t pattern; - uint8_t index; - } fh; - struct ds { - uint8_t channel; - } ds; - struct cf { - uint8_t count; - uint8_t period; - uint8_t maxdur[2]; - uint8_t dur[2]; - } cf; - struct tim { - uint8_t count; - uint8_t period; - uint8_t bitctl; - /* uint8_t pvt[251]; The driver needs to use this. */ - } tim; - struct ibss { - uint16_t atim; - } ibss; - struct challenge { - uint8_t *p; - uint8_t len; - } challenge; - struct erp { - uint8_t flags; - } erp; - struct country { - uint8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ - struct { - uint8_t schan; /* starting channel */ - uint8_t nchan; /* number channels */ - uint8_t maxtxpwr; - } band[4]; /* up to 4 sub bands */ - } country; - struct ath { - uint8_t flags; - } ath; - struct ieee80211_ie_wpa wpa; -}; enum { - IEEE80211_ELEMID_SSID = 0, - IEEE80211_ELEMID_RATES = 1, - IEEE80211_ELEMID_FHPARMS = 2, - IEEE80211_ELEMID_DSPARMS = 3, - IEEE80211_ELEMID_CFPARMS = 4, - IEEE80211_ELEMID_TIM = 5, - IEEE80211_ELEMID_IBSSPARMS = 6, - IEEE80211_ELEMID_COUNTRY = 7, - IEEE80211_ELEMID_CHALLENGE = 16, + IEEE80211_ELEMID_SSID = 0, + IEEE80211_ELEMID_RATES = 1, + IEEE80211_ELEMID_FHPARMS = 2, + IEEE80211_ELEMID_DSPARMS = 3, + IEEE80211_ELEMID_CFPARMS = 4, + IEEE80211_ELEMID_TIM = 5, + IEEE80211_ELEMID_IBSSPARMS = 6, + IEEE80211_ELEMID_COUNTRY = 7, + IEEE80211_ELEMID_CHALLENGE = 16, /* 17-31 reserved for challenge text extension */ - IEEE80211_ELEMID_ERP = 42, - IEEE80211_ELEMID_XRATES = 50, - IEEE80211_ELEMID_TPC = 150, - IEEE80211_ELEMID_CCKM = 156, - IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ + IEEE80211_ELEMID_ERP = 42, + IEEE80211_ELEMID_RSN = 48, + IEEE80211_ELEMID_XRATES = 50, + IEEE80211_ELEMID_TPC = 150, + IEEE80211_ELEMID_CCKM = 156, + IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ }; -#define IEEE80211_CHALLENGE_LEN 128 - -#define IEEE80211_RATE_BASIC 0x80 -#define IEEE80211_RATE_VAL 0x7f +struct ieee80211_tim_ie { + uint8_t tim_ie; /* IEEE80211_ELEMID_TIM */ + uint8_t tim_len; + uint8_t tim_count; /* DTIM count */ + uint8_t tim_period; /* DTIM period */ + uint8_t tim_bitctl; /* bitmap control */ + uint8_t tim_bitmap[1]; /* variable-length bitmap */ +} __packed; + +struct ieee80211_country_ie { + uint8_t ie; /* IEEE80211_ELEMID_COUNTRY */ + uint8_t len; + uint8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ + struct { + uint8_t schan; /* starting channel */ + uint8_t nchan; /* number channels */ + uint8_t maxtxpwr; /* tx power cap */ + } band[4] __packed; /* up to 4 sub bands */ +} __packed; + +#define IEEE80211_CHALLENGE_LEN 128 + +#define IEEE80211_RATE_BASIC 0x80 +#define IEEE80211_RATE_VAL 0x7f /* EPR information element flags */ -#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 -#define IEEE80211_ERP_USE_PROTECTION 0x02 -#define IEEE80211_ERP_BARKER_MODE 0x04 +#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 +#define IEEE80211_ERP_USE_PROTECTION 0x02 +#define IEEE80211_ERP_LONG_PREAMBLE 0x04 /* Atheros private advanced capabilities info */ -#define ATHEROS_CAP_TURBO_PRIME 0x01 -#define ATHEROS_CAP_COMPRESSION 0x02 -#define ATHEROS_CAP_FAST_FRAME 0x04 +#define ATHEROS_CAP_TURBO_PRIME 0x01 +#define ATHEROS_CAP_COMPRESSION 0x02 +#define ATHEROS_CAP_FAST_FRAME 0x04 /* bits 3-6 reserved */ -#define ATHEROS_CAP_BOOST 0x80 +#define ATHEROS_CAP_BOOST 0x80 #define ATH_OUI 0x7f0300 /* Atheros OUI */ #define ATH_OUI_TYPE 0x01 @@ -407,12 +455,11 @@ enum { #define WPA_OUI 0xf25000 #define WPA_OUI_TYPE 0x01 -#define WPA_OUI_VERSION 1 /* current supported version */ +#define WPA_VERSION 1 /* current supported version */ #define WPA_CSE_NULL 0x00 #define WPA_CSE_WEP40 0x01 #define WPA_CSE_TKIP 0x02 -#define WPA_CSE_WRAP 0x03 /* WPA2/802.11i */ #define WPA_CSE_CCMP 0x04 #define WPA_CSE_WEP104 0x05 @@ -420,6 +467,34 @@ enum { #define WPA_ASE_8021X_UNSPEC 0x01 #define WPA_ASE_8021X_PSK 0x02 +#define RSN_OUI 0xac0f00 +#define RSN_VERSION 1 /* current supported version */ + +#define RSN_CSE_NULL 0x00 +#define RSN_CSE_WEP40 0x01 +#define RSN_CSE_TKIP 0x02 +#define RSN_CSE_WRAP 0x03 +#define RSN_CSE_CCMP 0x04 +#define RSN_CSE_WEP104 0x05 + +#define RSN_ASE_NONE 0x00 +#define RSN_ASE_8021X_UNSPEC 0x01 +#define RSN_ASE_8021X_PSK 0x02 + +#define RSN_CAP_PREAUTH 0x01 + +#define WME_OUI 0xf25000 +#define WME_OUI_TYPE 0x02 +#define WME_INFO_OUI_SUBTYPE 0x00 +#define WME_PARAM_OUI_SUBTYPE 0x01 +#define WME_VERSION 1 + +/* WME stream classes */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ + /* * AUTH management packets * @@ -440,9 +515,9 @@ typedef uint8_t *ieee80211_mgt_auth_t; #define IEEE80211_AUTH_STATUS(auth) \ ((auth)[4] | ((auth)[5] << 8)) -#define IEEE80211_AUTH_ALG_OPEN 0x0000 -#define IEEE80211_AUTH_ALG_SHARED 0x0001 -#define IEEE80211_AUTH_ALG_LEAP 0x0080 +#define IEEE80211_AUTH_ALG_OPEN 0x0000 +#define IEEE80211_AUTH_ALG_SHARED 0x0001 +#define IEEE80211_AUTH_ALG_LEAP 0x0080 enum { IEEE80211_AUTH_OPEN_REQUEST = 1, @@ -498,13 +573,24 @@ enum { IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, }; -#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ -#define IEEE80211_WEP_IVLEN 3 /* 24bit */ -#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ -#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ -#define IEEE80211_WEP_NKID 4 /* number of key ids */ +#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ +#define IEEE80211_WEP_IVLEN 3 /* 24bit */ +#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ +#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ +#define IEEE80211_WEP_NKID 4 /* number of key ids */ -#define IEEE80211_CRC_LEN 4 +/* + * 802.11i defines an extended IV for use with non-WEP ciphers. + * When the EXTIV bit is set in the key id byte an additional + * 4 bytes immediately follow the IV for TKIP. For CCMP the + * EXTIV bit is likewise set but the 8 bytes represent the + * CCMP header rather than IV+extended-IV. + */ +#define IEEE80211_WEP_EXTIV 0x20 +#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */ +#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */ + +#define IEEE80211_CRC_LEN 4 /* * Maximum acceptable MTU is: @@ -513,26 +599,42 @@ enum { * Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default * mtu is Ethernet-compatible; it's set by ether_ifattach. */ -#define IEEE80211_MTU_MAX 2290 -#define IEEE80211_MTU_MIN 32 +#define IEEE80211_MTU_MAX 2290 +#define IEEE80211_MTU_MIN 32 -#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ +#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_ACK_LEN \ + (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) #define IEEE80211_MIN_LEN \ (sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN) +/* + * The 802.11 spec says at most 2007 stations may be + * associated at once. For most AP's this is way more + * than is feasible so we use a default of 128. This + * number may be overridden by the driver and/or by + * user configuration. + */ +#define IEEE80211_AID_MAX 2007 +#define IEEE80211_AID_DEF 128 + +#define IEEE80211_AID(b) ((b) &~ 0xc000) + /* * RTS frame length parameters. The default is specified in - * the 802.11 spec. The max may be wrong for jumbo frames. + * the 802.11 spec as 512; we treat it as implementation-dependent + * so it's defined in ieee80211_var.h. The max may be wrong + * for jumbo frames. */ -#define IEEE80211_RTS_DEFAULT 512 -#define IEEE80211_RTS_MIN 1 -#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN +#define IEEE80211_RTS_MIN 1 +#define IEEE80211_RTS_MAX 2346 -enum { - IEEE80211_AUTH_NONE = 0, - IEEE80211_AUTH_OPEN = 1, - IEEE80211_AUTH_SHARED = 2, -}; +/* + * TX fragmentation parameters. As above for RTS, we treat + * default as implementation-dependent so define it elsewhere. + */ +#define IEEE80211_FRAG_MIN 256 +#define IEEE80211_FRAG_MAX 2346 -#endif /* _NETPROTO_802_11_IEEE80211_H_ */ +#endif /* _NET80211_IEEE80211_H_ */ diff --git a/sys/netproto/802_11/ieee80211_crypto.h b/sys/netproto/802_11/ieee80211_crypto.h index 1a74169329..80b2c348eb 100644 --- a/sys/netproto/802_11/ieee80211_crypto.h +++ b/sys/netproto/802_11/ieee80211_crypto.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,24 +29,201 @@ * (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: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.9.2.1 2005/09/03 22:40:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ - -#ifndef _NETPROTO_802_11_IEEE80211_CRYPTO_H_ -#define _NETPROTO_802_11_IEEE80211_CRYPTO_H_ +#ifndef _NET80211_IEEE80211_CRYPTO_H_ +#define _NET80211_IEEE80211_CRYPTO_H_ /* * 802.11 protocol crypto-related definitions. */ #define IEEE80211_KEYBUF_SIZE 16 +#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */ +/* + * Old WEP-style key. Deprecated. + */ struct ieee80211_wepkey { - int wk_len; - uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; + u_int wk_len; /* key length in bytes */ + uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; }; -void ieee80211_crypto_attach(struct ifnet *); -void ieee80211_crypto_detach(struct ifnet *); -struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int); -#endif /* _NETPROTO_802_11_IEEE80211_CRYPTO_H_ */ +struct ieee80211_cipher; + +/* + * Crypto key state. There is sufficient room for all supported + * ciphers (see below). The underlying ciphers are handled + * separately through loadable cipher modules that register with + * the generic crypto support. A key has a reference to an instance + * of the cipher; any per-key state is hung off wk_private by the + * cipher when it is attached. Ciphers are automatically called + * to detach and cleanup any such state when the key is deleted. + * + * The generic crypto support handles encap/decap of cipher-related + * frame contents for both hardware- and software-based implementations. + * A key requiring software crypto support is automatically flagged and + * the cipher is expected to honor this and do the necessary work. + * Ciphers such as TKIP may also support mixed hardware/software + * encrypt/decrypt and MIC processing. + */ +typedef uint16_t ieee80211_keyix; /* h/w key index */ + +struct ieee80211_key { + uint8_t wk_keylen; /* key length in bytes */ + uint8_t wk_pad; + uint16_t wk_flags; +#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */ +#define IEEE80211_KEY_RECV 0x02 /* key used for recv */ +#define IEEE80211_KEY_GROUP 0x04 /* key used for WPA group operation */ +#define IEEE80211_KEY_SWCRYPT 0x10 /* host-based encrypt/decrypt */ +#define IEEE80211_KEY_SWMIC 0x20 /* host-based enmic/demic */ + ieee80211_keyix wk_keyix; /* h/w key index */ + ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */ + uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; +#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */ +#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */ + uint64_t wk_keyrsc; /* key receive sequence counter */ + uint64_t wk_keytsc; /* key transmit sequence counter */ + const struct ieee80211_cipher *wk_cipher; + void *wk_private; /* private cipher state */ +}; +#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\ + (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP) + +/* + * NB: these values are ordered carefully; there are lots of + * of implications in any reordering. In particular beware + * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY. + */ +#define IEEE80211_CIPHER_WEP 0 +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_OCB 2 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CIPHER_CKIP 5 +#define IEEE80211_CIPHER_NONE 6 /* pseudo value */ + +#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1) + +#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1) + +#if defined(__KERNEL__) || defined(_KERNEL) + +struct ieee80211com; +struct ieee80211_node; +struct mbuf; + +/* + * Crypto state kept in each ieee80211com. Some of this + * can/should be shared when virtual AP's are supported. + * + * XXX save reference to ieee80211com to properly encapsulate state. + * XXX split out crypto capabilities from ic_caps + */ +struct ieee80211_crypto_state { + struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID]; + ieee80211_keyix cs_def_txkey; /* default/group tx key index */ + uint16_t cs_max_keyix; /* max h/w key index */ + + int (*cs_key_alloc)(struct ieee80211com *, + const struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); + int (*cs_key_delete)(struct ieee80211com *, + const struct ieee80211_key *); + int (*cs_key_set)(struct ieee80211com *, + const struct ieee80211_key *, + const uint8_t mac[IEEE80211_ADDR_LEN]); + void (*cs_key_update_begin)(struct ieee80211com *); + void (*cs_key_update_end)(struct ieee80211com *); +}; + +void ieee80211_crypto_attach(struct ieee80211com *); +void ieee80211_crypto_detach(struct ieee80211com *); +int ieee80211_crypto_newkey(struct ieee80211com *, + int cipher, int flags, struct ieee80211_key *); +int ieee80211_crypto_delkey(struct ieee80211com *, + struct ieee80211_key *); +int ieee80211_crypto_setkey(struct ieee80211com *, + struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); +void ieee80211_crypto_delglobalkeys(struct ieee80211com *); + +/* + * Template for a supported cipher. Ciphers register with the + * crypto code and are typically loaded as separate modules + * (the null cipher is always present). + * XXX may need refcnts + */ +struct ieee80211_cipher { + const char *ic_name; /* printable name */ + u_int ic_cipher; /* IEEE80211_CIPHER_* */ + u_int ic_header; /* size of privacy header (bytes) */ + u_int ic_trailer; /* size of privacy trailer (bytes) */ + u_int ic_miclen; /* size of mic trailer (bytes) */ + void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *); + void (*ic_detach)(struct ieee80211_key *); + int (*ic_setkey)(struct ieee80211_key *); + int (*ic_encap)(struct ieee80211_key *, struct mbuf *, + uint8_t keyid); + int (*ic_decap)(struct ieee80211_key *, struct mbuf *, int); + int (*ic_enmic)(struct ieee80211_key *, struct mbuf *, int); + int (*ic_demic)(struct ieee80211_key *, struct mbuf *, int); +}; +extern const struct ieee80211_cipher ieee80211_cipher_none; + +void ieee80211_crypto_register(const struct ieee80211_cipher *); +void ieee80211_crypto_unregister(const struct ieee80211_cipher *); +int ieee80211_crypto_available(u_int cipher); + +struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); +struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *, int); + +/* + * Check and remove any MIC. + */ +static __inline int +ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k, + struct mbuf *m, int force) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? cip->ic_demic(k, m, force) : 1); +} + +/* + * Add any MIC. + */ +static __inline int +ieee80211_crypto_enmic(struct ieee80211com *ic, + struct ieee80211_key *k, struct mbuf *m, int force) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m, force) : 1); +} + +/* + * Reset key state to an unused state. The crypto + * key allocation mechanism insures other state (e.g. + * key data) is properly setup before a key is used. + */ +static __inline void +ieee80211_crypto_resetkey(struct ieee80211com *ic, + struct ieee80211_key *k, ieee80211_keyix ix) +{ + k->wk_cipher = &ieee80211_cipher_none; + k->wk_private = k->wk_cipher->ic_attach(ic, k); + k->wk_keyix = k->wk_rxkeyix = ix; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; +} + +/* + * Crypt-related notification methods. + */ +void ieee80211_notify_replay_failure(struct ieee80211com *, + const struct ieee80211_frame *, const struct ieee80211_key *, + uint64_t rsc); +void ieee80211_notify_michael_failure(struct ieee80211com *, + const struct ieee80211_frame *, u_int keyix); + +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ +#endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff --git a/sys/netproto/802_11/ieee80211_dragonfly.h b/sys/netproto/802_11/ieee80211_dragonfly.h new file mode 100644 index 0000000000..20a2958a86 --- /dev/null +++ b/sys/netproto/802_11/ieee80211_dragonfly.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2003-2005 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. + * 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 ``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: src/sys/net80211/ieee80211_freebsd.h,v 1.5.2.1 2005/09/03 22:40:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_dragonfly.h,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ +#ifndef _NET80211_IEEE80211_DRAGONFLY_H_ +#define _NET80211_IEEE80211_DRAGONFLY_H_ + +/* + * Per-node power-save queue definitions. + */ +#define IEEE80211_NODE_SAVEQ_INIT(_ni, _name) do { \ + (_ni)->ni_savedq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \ +} while (0) +#define IEEE80211_NODE_SAVEQ_DESTROY(_ni) ((void)0) +#define IEEE80211_NODE_SAVEQ_QLEN(_ni) IF_QLEN(&(_ni)->ni_savedq) +#define IEEE80211_NODE_SAVEQ_DEQUEUE(_ni, _m, _qlen) do { \ + IF_DEQUEUE(&(_ni)->ni_savedq, _m); \ + (_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni); \ +} while (0) +#define IEEE80211_NODE_SAVEQ_DRAIN(_ni, _qlen) do { \ + (_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni); \ + IF_DRAIN(&(_ni)->ni_savedq); \ +} while (0) +/* XXX could be optimized */ +#define _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(_ni, _m) do { \ + IF_DEQUEUE(&(_ni)->ni_savedq, m); \ +} while (0) +#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _m, _qlen, _age) do {\ + (_m)->m_nextpkt = NULL; \ + if ((_ni)->ni_savedq.ifq_tail != NULL) { \ + _age -= M_AGE_GET((_ni)->ni_savedq.ifq_tail); \ + (_ni)->ni_savedq.ifq_tail->m_nextpkt = (_m); \ + } else { \ + (_ni)->ni_savedq.ifq_head = (_m); \ + } \ + M_AGE_SET(_m, _age); \ + (_ni)->ni_savedq.ifq_tail = (_m); \ + (_qlen) = ++(_ni)->ni_savedq.ifq_len; \ +} while (0) + +/* + * Node reference counting definitions. + * + * ieee80211_node_initref initialize the reference count to 1 + * ieee80211_node_incref add a reference + * ieee80211_node_decref remove a reference + * ieee80211_node_dectestref remove a reference and return 1 if this + * is the last reference, otherwise 0 + * ieee80211_node_refcnt reference count for printing (only) + */ +#include + +#define ieee80211_node_initref(_ni) \ + do { ((_ni)->ni_refcnt = 1); } while (0) +#define ieee80211_node_incref(_ni) \ + atomic_add_int(&(_ni)->ni_refcnt, 1) +#define ieee80211_node_decref(_ni) \ + atomic_subtract_int(&(_ni)->ni_refcnt, 1) +struct ieee80211_node; +int ieee80211_node_dectestref(struct ieee80211_node *ni); +#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt + +struct mbuf *ieee80211_getmgtframe(uint8_t **frm, u_int pktlen); +#define M_LINK0 M_PROTO1 /* WEP requested */ +#define M_PWR_SAV M_PROTO4 /* bypass PS handling */ +#define M_MORE_DATA M_PROTO5 /* more data frames to follow */ +/* + * Encode WME access control bits in the PROTO flags. + * This is safe since it's passed directly in to the + * driver and there's no chance someone else will clobber + * them on us. + */ +#define M_WME_AC_MASK (M_PROTO2|M_PROTO3) +/* XXX 5 is wrong if M_PROTO* are redefined */ +#define M_WME_AC_SHIFT 5 + +#define M_WME_SETAC(m, ac) \ + ((m)->m_flags = ((m)->m_flags &~ M_WME_AC_MASK) | \ + ((ac) << M_WME_AC_SHIFT)) +#define M_WME_GETAC(m) (((m)->m_flags >> M_WME_AC_SHIFT) & 0x3) + +/* + * Mbufs on the power save queue are tagged with an age and + * timed out. We reuse the hardware checksum field in the + * mbuf packet header to store this data. + */ +#define M_AGE_SET(m,v) (m->m_pkthdr.csum_data = v) +#define M_AGE_GET(m) (m->m_pkthdr.csum_data) +#define M_AGE_SUB(m,adj) (m->m_pkthdr.csum_data -= adj) + +void get_random_bytes(void *, size_t); + +struct ieee80211com; + +void ieee80211_sysctl_attach(struct ieee80211com *); +void ieee80211_sysctl_detach(struct ieee80211com *); + +void ieee80211_load_module(const char *); +int ieee80211_mbuf_append(struct mbuf *, int, const uint8_t *); +struct mbuf *ieee80211_mbuf_clone(struct mbuf *, int); + +/* XXX this stuff belongs elsewhere */ +/* + * Message formats for messages from the net80211 layer to user + * applications via the routing socket. These messages are appended + * to an if_announcemsghdr structure. + */ +struct ieee80211_join_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_leave_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_replay_event { + uint8_t iev_src[6]; /* src MAC */ + uint8_t iev_dst[6]; /* dst MAC */ + uint8_t iev_cipher; /* cipher type */ + uint8_t iev_keyix; /* key id/index */ + uint64_t iev_keyrsc; /* RSC from key */ + uint64_t iev_rsc; /* RSC from frame */ +}; + +struct ieee80211_michael_event { + uint8_t iev_src[6]; /* src MAC */ + uint8_t iev_dst[6]; /* dst MAC */ + uint8_t iev_cipher; /* cipher type */ + uint8_t iev_keyix; /* key id/index */ +}; + +#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */ +#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */ +#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */ +#define RTM_IEEE80211_JOIN 103 /* station join (ap mode) */ +#define RTM_IEEE80211_LEAVE 104 /* station leave (ap mode) */ +#define RTM_IEEE80211_SCAN 105 /* scan complete, results available */ +#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */ +#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */ +#define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */ + +#endif /* _NET80211_IEEE80211_DRAGONFLY_H_ */ diff --git a/sys/netproto/802_11/ieee80211_ioctl.h b/sys/netproto/802_11/ieee80211_ioctl.h index f02b5b2295..e5e4cb5be0 100644 --- a/sys/netproto/802_11/ieee80211_ioctl.h +++ b/sys/netproto/802_11/ieee80211_ioctl.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,17 +29,70 @@ * (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: src/sys/net80211/ieee80211_ioctl.h,v 1.5 2004/03/30 22:57:57 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_ioctl.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.10.2.4 2005/12/22 19:18:23 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_ioctl.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ - -#ifndef _NETPROTO_802_11_IEEE80211_IOCTL_H_ -#define _NETPROTO_802_11_IEEE80211_IOCTL_H_ +#ifndef _NET80211_IEEE80211_IOCTL_H_ +#define _NET80211_IEEE80211_IOCTL_H_ /* * IEEE 802.11 ioctls. */ +#include +#include +#include + +/* + * Per/node (station) statistics available when operating as an AP. + */ +struct ieee80211_nodestats { + uint32_t ns_rx_data; /* rx data frames */ + uint32_t ns_rx_mgmt; /* rx management frames */ + uint32_t ns_rx_ctrl; /* rx control frames */ + uint32_t ns_rx_ucast; /* rx unicast frames */ + uint32_t ns_rx_mcast; /* rx multi/broadcast frames */ + uint64_t ns_rx_bytes; /* rx data count (bytes) */ + uint64_t ns_rx_beacons; /* rx beacon frames */ + uint32_t ns_rx_proberesp; /* rx probe response frames */ + + uint32_t ns_rx_dup; /* rx discard 'cuz dup */ + uint32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ + uint32_t ns_rx_wepfail; /* rx wep processing failed */ + uint32_t ns_rx_demicfail; /* rx demic failed */ + uint32_t ns_rx_decap; /* rx decapsulation failed */ + uint32_t ns_rx_defrag; /* rx defragmentation failed */ + uint32_t ns_rx_disassoc; /* rx disassociation */ + uint32_t ns_rx_deauth; /* rx deauthentication */ + uint32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ + uint32_t ns_rx_unauth; /* rx on unauthorized port */ + uint32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ + uint32_t ns_tx_data; /* tx data frames */ + uint32_t ns_tx_mgmt; /* tx management frames */ + uint32_t ns_tx_ucast; /* tx unicast frames */ + uint32_t ns_tx_mcast; /* tx multi/broadcast frames */ + uint64_t ns_tx_bytes; /* tx data count (bytes) */ + uint32_t ns_tx_probereq; /* tx probe request frames */ + + uint32_t ns_tx_novlantag; /* tx discard 'cuz no tag */ + uint32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */ + + uint32_t ns_ps_discard; /* ps discard 'cuz of age */ + + /* MIB-related state */ + uint32_t ns_tx_assoc; /* [re]associations */ + uint32_t ns_tx_assoc_fail; /* [re]association failures */ + uint32_t ns_tx_auth; /* [re]authentications */ + uint32_t ns_tx_auth_fail; /* [re]authentication failures*/ + uint32_t ns_tx_deauth; /* deauthentications */ + uint32_t ns_tx_deauth_code; /* last deauth reason */ + uint32_t ns_tx_disassoc; /* disassociations */ + uint32_t ns_tx_disassoc_code; /* last disassociation reason */ +}; + +/* + * Summary statistics. + */ struct ieee80211_stats { uint32_t is_rx_badversion; /* rx frame with bad version */ uint32_t is_rx_tooshort; /* rx frame too short */ @@ -48,11 +101,13 @@ struct ieee80211_stats { uint32_t is_rx_wrongdir; /* rx w/ wrong direction */ uint32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ uint32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ - uint32_t is_rx_nowep; /* rx w/ wep but wep !config */ + uint32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ + uint32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ uint32_t is_rx_wepfail; /* rx wep processing failed */ uint32_t is_rx_decap; /* rx decapsulation failed */ uint32_t is_rx_mgtdiscard; /* rx discard mgt frames */ uint32_t is_rx_ctl; /* rx discard ctrl frames */ + uint32_t is_rx_beacon; /* rx beacon frames */ uint32_t is_rx_rstoobig; /* rx rate set truncated */ uint32_t is_rx_elem_missing; /* rx required element missing*/ uint32_t is_rx_elem_toobig; /* rx element too big */ @@ -64,26 +119,247 @@ struct ieee80211_stats { uint32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ uint32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ uint32_t is_rx_auth_fail; /* rx sta auth failure */ + uint32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */ uint32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ uint32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ uint32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ uint32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ + uint32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ uint32_t is_rx_deauth; /* rx deauthentication */ uint32_t is_rx_disassoc; /* rx disassociation */ uint32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ - uint32_t is_rx_nombuf; /* rx failed for lack of mbuf */ + uint32_t is_rx_nobuf; /* rx failed for lack of buf */ uint32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ uint32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ uint32_t is_rx_bad_auth; /* rx bad auth request */ - uint32_t is_tx_nombuf; /* tx failed for lack of mbuf */ + uint32_t is_rx_unauth; /* rx on unauthorized port */ + uint32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ + uint32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ + uint32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ + uint32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ + uint32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ + uint32_t is_rx_tkipformat; /* rx format bad (TKIP) */ + uint32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ + uint32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ + uint32_t is_rx_badcipher; /* rx failed 'cuz key type */ + uint32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */ + uint32_t is_rx_acl; /* rx discard 'cuz acl policy */ + uint32_t is_tx_nobuf; /* tx failed for lack of buf */ uint32_t is_tx_nonode; /* tx failed for no node */ uint32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ + uint32_t is_tx_badcipher; /* tx failed 'cuz key type */ + uint32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */ + uint32_t is_tx_noheadroom; /* tx failed 'cuz no space */ + uint32_t is_tx_fragframes; /* tx frames fragmented */ + uint32_t is_tx_frags; /* tx fragments created */ uint32_t is_scan_active; /* active scans started */ uint32_t is_scan_passive; /* passive scans started */ uint32_t is_node_timeout; /* nodes timed out inactivity */ uint32_t is_crypto_nomem; /* no memory for crypto ctx */ + uint32_t is_crypto_tkip; /* tkip crypto done in s/w */ + uint32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ + uint32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ + uint32_t is_crypto_tkipcm; /* tkip counter measures */ + uint32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ + uint32_t is_crypto_wep; /* wep crypto done in s/w */ + uint32_t is_crypto_setkey_cipher;/* cipher rejected key */ + uint32_t is_crypto_setkey_nokey; /* no key index for setkey */ + uint32_t is_crypto_delkey; /* driver key delete failed */ + uint32_t is_crypto_badcipher; /* unknown cipher */ + uint32_t is_crypto_nocipher; /* cipher not available */ + uint32_t is_crypto_attachfail; /* cipher attach failed */ + uint32_t is_crypto_swfallback; /* cipher fallback to s/w */ + uint32_t is_crypto_keyfail; /* driver key alloc failed */ + uint32_t is_crypto_enmicfail; /* en-MIC failed */ + uint32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ + uint32_t is_ibss_norate; /* merge failed-rate mismatch */ + uint32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ + uint32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ + uint32_t is_ps_qempty; /* ps-poll w/ nothing to send */ + uint32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */ + uint32_t is_ff_tooshort; /* fast frame rx decap error */ + uint32_t is_ff_split; /* fast frame rx split error */ + uint32_t is_ff_decap; /* fast frames decap'd */ + uint32_t is_ff_encap; /* fast frames encap'd for tx */ + uint32_t is_rx_badbintval; /* rx frame w/ bogus bintval */ + uint32_t is_spare[9]; +}; + +/* + * Max size of optional information elements. We artificially + * constrain this; it's limited only by the max frame size (and + * the max parameter size of the wireless extensions). + */ +#define IEEE80211_MAX_OPT_IE 256 + +/* + * WPA/RSN get/set key request. Specify the key/cipher + * type and whether the key is to be used for sending and/or + * receiving. The key index should be set only when working + * with global keys (use IEEE80211_KEYIX_NONE for ``no index''). + * Otherwise a unicast/pairwise key is specified by the bssid + * (on a station) or mac address (on an ap). They key length + * must include any MIC key data; otherwise it should be no + more than IEEE80211_KEYBUF_SIZE. + */ +struct ieee80211req_key { + uint8_t ik_type; /* key/cipher type */ + uint8_t ik_pad; + uint16_t ik_keyix; /* key index */ + uint8_t ik_keylen; /* key length in bytes */ + uint8_t ik_flags; +/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */ +#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */ + uint8_t ik_macaddr[IEEE80211_ADDR_LEN]; + uint64_t ik_keyrsc; /* key receive sequence counter */ + uint64_t ik_keytsc; /* key transmit sequence counter */ + uint8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; +}; + +/* + * Delete a key either by index or address. Set the index + * to IEEE80211_KEYIX_NONE when deleting a unicast key. + */ +struct ieee80211req_del_key { + uint8_t idk_keyix; /* key index */ + uint8_t idk_macaddr[IEEE80211_ADDR_LEN]; +}; + +/* + * MLME state manipulation request. IEEE80211_MLME_ASSOC + * only makes sense when operating as a station. The other + * requests can be used when operating as a station or an + * ap (to effect a station). + */ +struct ieee80211req_mlme { + uint8_t im_op; /* operation to perform */ +#define IEEE80211_MLME_ASSOC 1 /* associate station */ +#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */ +#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */ +#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */ +#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */ + uint8_t im_ssid_len; /* length of optional ssid */ + uint16_t im_reason; /* 802.11 reason code */ + uint8_t im_macaddr[IEEE80211_ADDR_LEN]; + uint8_t im_ssid[IEEE80211_NWID_LEN]; +}; + +/* + * MAC ACL operations. + */ +enum { + IEEE80211_MACCMD_POLICY_OPEN = 0, /* set policy: no ACL's */ + IEEE80211_MACCMD_POLICY_ALLOW = 1, /* set policy: allow traffic */ + IEEE80211_MACCMD_POLICY_DENY = 2, /* set policy: deny traffic */ + IEEE80211_MACCMD_FLUSH = 3, /* flush ACL database */ + IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */ + IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */ + IEEE80211_MACCMD_LIST = 6, /* get ACL database */ +}; + +struct ieee80211req_maclist { + uint8_t ml_macaddr[IEEE80211_ADDR_LEN]; +}; + +/* + * Set the active channel list. Note this list is + * intersected with the available channel list in + * calculating the set of channels actually used in + * scanning. + */ +struct ieee80211req_chanlist { + uint8_t ic_channels[IEEE80211_CHAN_BYTES]; }; +/* + * Get the active channel list info. + */ +struct ieee80211req_chaninfo { + u_int ic_nchans; + struct ieee80211_channel ic_chans[IEEE80211_CHAN_MAX]; +}; + +/* + * Retrieve the WPA/RSN information element for an associated station. + */ +struct ieee80211req_wpaie { + uint8_t wpa_macaddr[IEEE80211_ADDR_LEN]; + uint8_t wpa_ie[IEEE80211_MAX_OPT_IE]; +}; + +/* + * Retrieve per-node statistics. + */ +struct ieee80211req_sta_stats { + union { + /* NB: explicitly force 64-bit alignment */ + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint64_t pad; + } is_u; + struct ieee80211_nodestats is_stats; +}; + +/* + * Station information block; the mac address is used + * to retrieve other data like stats, unicast key, etc. + */ +struct ieee80211req_sta_info { + uint16_t isi_len; /* length (mult of 4) */ + uint16_t isi_freq; /* MHz */ + uint16_t isi_flags; /* channel flags */ + uint16_t isi_state; /* state flags */ + uint8_t isi_authmode; /* authentication algorithm */ + uint8_t isi_rssi; + uint8_t isi_capinfo; /* capabilities */ + uint8_t isi_erp; /* ERP element */ + uint8_t isi_macaddr[IEEE80211_ADDR_LEN]; + uint8_t isi_nrates; + /* negotiated rates */ + uint8_t isi_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t isi_txrate; /* index to isi_rates[] */ + uint16_t isi_ie_len; /* IE length */ + uint16_t isi_associd; /* assoc response */ + uint16_t isi_txpower; /* current tx power */ + uint16_t isi_vlan; /* vlan tag */ + uint16_t isi_txseqs[17]; /* seq to be transmitted */ + uint16_t isi_rxseqs[17]; /* seq previous for qos frames*/ + uint16_t isi_inact; /* inactivity timer */ + /* XXX frag state? */ + /* variable length IE data */ +}; + +/* + * Retrieve per-station information; to retrieve all + * specify a mac address of ff:ff:ff:ff:ff:ff. + */ +struct ieee80211req_sta_req { + union { + /* NB: explicitly force 64-bit alignment */ + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint64_t pad; + } is_u; + struct ieee80211req_sta_info info[1]; /* variable length */ +}; + +/* + * Get/set per-station tx power cap. + */ +struct ieee80211req_sta_txpow { + uint8_t it_macaddr[IEEE80211_ADDR_LEN]; + uint8_t it_txpow; +}; + +/* + * WME parameters are set and return using i_val and i_len. + * i_val holds the value itself. i_len specifies the AC + * and, as appropriate, then high bit specifies whether the + * operation is to be applied to the BSS or ourself. + */ +#define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */ +#define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */ +#define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */ + +#if defined(__FreeBSD__) || defined(__DragonFly__) /* * FreeBSD-style ioctls. */ @@ -98,38 +374,101 @@ struct ieee80211req { #define SIOCS80211 _IOW('i', 234, struct ieee80211req) #define SIOCG80211 _IOWR('i', 235, struct ieee80211req) -#define IEEE80211_IOC_SSID 1 -#define IEEE80211_IOC_NUMSSIDS 2 -#define IEEE80211_IOC_WEP 3 -#define IEEE80211_WEP_NOSUP -1 -#define IEEE80211_WEP_OFF 0 -#define IEEE80211_WEP_ON 1 -#define IEEE80211_WEP_MIXED 2 -#define IEEE80211_IOC_WEPKEY 4 -#define IEEE80211_IOC_NUMWEPKEYS 5 -#define IEEE80211_IOC_WEPTXKEY 6 -#define IEEE80211_IOC_AUTHMODE 7 -#define IEEE80211_IOC_STATIONNAME 8 -#define IEEE80211_IOC_CHANNEL 9 -#define IEEE80211_IOC_POWERSAVE 10 -#define IEEE80211_POWERSAVE_NOSUP -1 -#define IEEE80211_POWERSAVE_OFF 0 -#define IEEE80211_POWERSAVE_CAM 1 -#define IEEE80211_POWERSAVE_PSP 2 -#define IEEE80211_POWERSAVE_PSP_CAM 3 -#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM -#define IEEE80211_IOC_POWERSAVESLEEP 11 +#define IEEE80211_IOC_SSID 1 +#define IEEE80211_IOC_NUMSSIDS 2 +#define IEEE80211_IOC_WEP 3 +#define IEEE80211_WEP_NOSUP -1 +#define IEEE80211_WEP_OFF 0 +#define IEEE80211_WEP_ON 1 +#define IEEE80211_WEP_MIXED 2 +#define IEEE80211_IOC_WEPKEY 4 +#define IEEE80211_IOC_NUMWEPKEYS 5 +#define IEEE80211_IOC_WEPTXKEY 6 +#define IEEE80211_IOC_AUTHMODE 7 +#define IEEE80211_IOC_STATIONNAME 8 +#define IEEE80211_IOC_CHANNEL 9 +#define IEEE80211_IOC_POWERSAVE 10 +#define IEEE80211_POWERSAVE_NOSUP -1 +#define IEEE80211_POWERSAVE_OFF 0 +#define IEEE80211_POWERSAVE_CAM 1 +#define IEEE80211_POWERSAVE_PSP 2 +#define IEEE80211_POWERSAVE_PSP_CAM 3 +#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM +#define IEEE80211_IOC_POWERSAVESLEEP 11 #define IEEE80211_IOC_RTSTHRESHOLD 12 -#define IEEE80211_IOC_PROTMODE 13 -#define IEEE80211_PROTMODE_OFF 0 -#define IEEE80211_PROTMODE_CTS 1 -#define IEEE80211_PROTMODE_RTSCTS 2 -#define IEEE80211_IOC_TXPOWER 14 +#define IEEE80211_IOC_PROTMODE 13 +#define IEEE80211_PROTMODE_OFF 0 +#define IEEE80211_PROTMODE_CTS 1 +#define IEEE80211_PROTMODE_RTSCTS 2 +#define IEEE80211_IOC_TXPOWER 14 /* global tx power limit */ +#define IEEE80211_IOC_BSSID 15 +#define IEEE80211_IOC_ROAMING 16 /* roaming mode */ +#define IEEE80211_IOC_PRIVACY 17 /* privacy invoked */ +#define IEEE80211_IOC_DROPUNENCRYPTED 18 /* discard unencrypted frames */ +#define IEEE80211_IOC_WPAKEY 19 +#define IEEE80211_IOC_DELKEY 20 +#define IEEE80211_IOC_MLME 21 +#define IEEE80211_IOC_OPTIE 22 /* optional info. element */ +#define IEEE80211_IOC_SCAN_REQ 23 +#define IEEE80211_IOC_SCAN_RESULTS 24 +#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */ +#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */ +#define IEEE80211_IOC_CHANLIST 27 /* channel list */ +#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */ +#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */ +#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */ +#define IEEE80211_IOC_MCASTCIPHER 31 /* multicast/default cipher */ +#define IEEE80211_IOC_MCASTKEYLEN 32 /* multicast key length */ +#define IEEE80211_IOC_UCASTCIPHERS 33 /* unicast cipher suites */ +#define IEEE80211_IOC_UCASTCIPHER 34 /* unicast cipher */ +#define IEEE80211_IOC_UCASTKEYLEN 35 /* unicast key length */ +#define IEEE80211_IOC_DRIVER_CAPS 36 /* driver capabilities */ +#define IEEE80211_IOC_KEYMGTALGS 37 /* key management algorithms */ +#define IEEE80211_IOC_RSNCAPS 38 /* RSN capabilities */ +#define IEEE80211_IOC_WPAIE 39 /* WPA information element */ +#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */ +#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */ +#define IEEE80211_IOC_CHANINFO 42 /* channel info list */ +#define IEEE80211_IOC_TXPOWMAX 43 /* max tx power for channel */ +#define IEEE80211_IOC_STA_TXPOW 44 /* per-station tx power limit */ +#define IEEE80211_IOC_STA_INFO 45 /* station/neighbor info */ +#define IEEE80211_IOC_WME_CWMIN 46 /* WME: ECWmin */ +#define IEEE80211_IOC_WME_CWMAX 47 /* WME: ECWmax */ +#define IEEE80211_IOC_WME_AIFS 48 /* WME: AIFSN */ +#define IEEE80211_IOC_WME_TXOPLIMIT 49 /* WME: txops limit */ +#define IEEE80211_IOC_WME_ACM 50 /* WME: ACM (bss only) */ +#define IEEE80211_IOC_WME_ACKPOLICY 51 /* WME: ACK policy (!bss only)*/ +#define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */ +#define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */ +#define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */ +#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */ +#define IEEE80211_IOC_PUREG 56 /* pure 11g (no 11b stations) */ +#define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */ +#define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */ +#define IEEE80211_IOC_BURST 75 /* packet bursting */ -#ifndef IEEE80211_CHAN_ANY -#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ -#endif +/* + * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS. + */ +struct ieee80211req_scan_result { + uint16_t isr_len; /* length (mult of 4) */ + uint16_t isr_freq; /* MHz */ + uint16_t isr_flags; /* channel flags */ + uint8_t isr_noise; + uint8_t isr_rssi; + uint8_t isr_intval; /* beacon interval */ + uint8_t isr_capinfo; /* capabilities */ + uint8_t isr_erp; /* ERP element */ + uint8_t isr_bssid[IEEE80211_ADDR_LEN]; + uint8_t isr_nrates; + uint8_t isr_rates[IEEE80211_RATE_MAXSIZE]; + uint8_t isr_ssid_len; /* SSID length */ + uint8_t isr_ie_len; /* IE length */ + uint8_t isr_pad[5]; + /* variable length SSID followed by IE data */ +}; #define SIOCG80211STATS _IOWR('i', 236, struct ifreq) +#endif /* __FreeBSD__ || __DragonFly__ */ -#endif /* _NETPROTO_802_11_IEEE80211_IOCTL_H_ */ +#endif /* _NET80211_IEEE80211_IOCTL_H_ */ diff --git a/sys/netproto/802_11/ieee80211_node.h b/sys/netproto/802_11/ieee80211_node.h index 7dd9c7c979..bd058f0a8a 100644 --- a/sys/netproto/802_11/ieee80211_node.h +++ b/sys/netproto/802_11/ieee80211_node.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,31 +29,58 @@ * (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: src/sys/net80211/ieee80211_node.h,v 1.10 2004/04/05 22:10:26 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_node.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.17.2.5 2006/03/13 03:05:47 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_node.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ +#ifndef _NET80211_IEEE80211_NODE_H_ +#define _NET80211_IEEE80211_NODE_H_ -#ifndef _NETPROTO_802_11_IEEE80211_NODE_H_ -#define _NETPROTO_802_11_IEEE80211_NODE_H_ +#include /* for ieee80211_nodestats */ -#define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */ -#define IEEE80211_TRANS_WAIT 5 /* transition wait */ -#define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */ -#define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT) +/* + * Each ieee80211com instance has a single timer that fires once a + * second. This is used to initiate various work depending on the + * state of the instance: scanning (passive or active), ``transition'' + * (waiting for a response to a management frame when operating + * as a station), and node inactivity processing (when operating + * as an AP). For inactivity processing each node has a timeout + * set in it's ni_inact field that is decremented on each timeout + * and the node is reclaimed when the counter goes to zero. We + * use different inactivity timeout values depending on whether + * the node is associated and authorized (either by 802.1x or + * open/shared key authentication) or associated but yet to be + * authorized. The latter timeout is shorter to more aggressively + * reclaim nodes that leave part way through the 802.1x exchange. + */ +#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */ +#define IEEE80211_INACT_INIT (30/IEEE80211_INACT_WAIT) /* initial */ +#define IEEE80211_INACT_AUTH (180/IEEE80211_INACT_WAIT) /* associated but not authorized */ +#define IEEE80211_INACT_RUN (300/IEEE80211_INACT_WAIT) /* authorized */ +#define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */ +#define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */ + +#define IEEE80211_TRANS_WAIT 5 /* mgt frame tx timer (secs) */ #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ #define IEEE80211_NODE_HASH(addr) \ - (((uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE) + (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \ + IEEE80211_NODE_HASHSIZE) -#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ -#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ - -struct ieee80211_rateset { - uint8_t rs_nrates; - uint8_t rs_rates[IEEE80211_RATE_MAXSIZE]; +struct ieee80211_rsnparms { + uint8_t rsn_mcastcipher; /* mcast/group cipher */ + uint8_t rsn_mcastkeylen; /* mcast key length */ + uint8_t rsn_ucastcipherset; /* unicast cipher set */ + uint8_t rsn_ucastcipher; /* selected unicast cipher */ + uint8_t rsn_ucastkeylen; /* unicast key length */ + uint8_t rsn_keymgmtset; /* key mangement algorithms */ + uint8_t rsn_keymgmt; /* selected key mgmt algo */ + uint16_t rsn_caps; /* capabilities */ }; +struct ieee80211_node_table; +struct ieee80211com; + /* * Node specific information. Note that drivers are expected * to derive from this structure to add device-specific per-node @@ -61,10 +88,31 @@ struct ieee80211_rateset { * the ieee80211com structure. */ struct ieee80211_node { + struct ieee80211com *ni_ic; + struct ieee80211_node_table *ni_table; TAILQ_ENTRY(ieee80211_node) ni_list; LIST_ENTRY(ieee80211_node) ni_hash; u_int ni_refcnt; - u_int ni_scangen; /* gen# for timeout scan */ + uint8_t ni_authmode; /* authentication algorithm */ + uint16_t ni_flags; /* special-purpose state */ +#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ +#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ +#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */ +/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */ +#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */ +#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */ + uint16_t ni_associd; /* assoc response */ + uint16_t ni_txpower; /* current transmit power */ + uint16_t ni_vlan; /* vlan tag */ + uint32_t *ni_challenge; /* shared-key challenge */ + uint8_t *ni_wpa_ie; /* captured WPA/RSN ie */ + uint8_t *ni_wme_ie; /* captured WME ie */ + uint16_t ni_txseqs[17]; /* tx seq per-tid */ + uint16_t ni_rxseqs[17]; /* rx seq previous per-tid*/ + uint32_t ni_rxfragstamp; /* time stamp of last rx frag */ + struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */ + struct ieee80211_rsnparms ni_rsn; /* RSN/WPA parameters */ + struct ieee80211_key ni_ucastkey; /* unicast key */ /* hardware */ uint32_t ni_rstamp; /* recv timestamp */ @@ -75,82 +123,210 @@ struct ieee80211_node { uint8_t ni_bssid[IEEE80211_ADDR_LEN]; /* beacon, probe response */ - uint8_t ni_tstamp[8]; /* from last rcv'd beacon */ + union { + uint8_t data[8]; + uint64_t tsf; + } ni_tstamp; /* from last rcv'd beacon */ uint16_t ni_intval; /* beacon interval */ uint16_t ni_capinfo; /* capabilities */ uint8_t ni_esslen; uint8_t ni_essid[IEEE80211_NWID_LEN]; struct ieee80211_rateset ni_rates; /* negotiated rate set */ - uint8_t *ni_country; /* country information XXX */ - struct ieee80211_channel *ni_chan; + struct ieee80211_channel *ni_chan; /* XXX multiple uses */ uint16_t ni_fhdwell; /* FH only */ uint8_t ni_fhindex; /* FH only */ - uint8_t ni_erp; /* 11g only */ - -#ifdef notyet - /* DTIM and contention free period (CFP) */ - uint8_t ni_dtimperiod; - uint8_t ni_cfpperiod; /* # of DTIMs between CFPs */ - uint16_t ni_cfpduremain; /* remaining cfp duration */ - uint16_t ni_cfpmaxduration;/* max CFP duration in TU */ - uint16_t ni_nextdtim; /* time to next DTIM */ - uint16_t ni_timoffset; -#endif + uint8_t ni_erp; /* ERP from beacon/probe resp */ + uint16_t ni_timoff; /* byte offset to TIM ie */ + uint8_t ni_dtim_period; /* DTIM period */ + uint8_t ni_dtim_count; /* DTIM count for last bcn */ /* others */ - uint16_t ni_associd; /* assoc response */ - uint16_t ni_txseq; /* seq to be transmitted */ - uint16_t ni_rxseq; /* seq previous received */ int ni_fails; /* failure count to associate */ - int ni_inact; /* inactivity mark count */ + short ni_inact; /* inactivity mark count */ + short ni_inact_reload;/* inactivity reload value */ int ni_txrate; /* index to ni_rates[] */ + struct ifqueue ni_savedq; /* ps-poll queue */ + struct ieee80211_nodestats ni_stats; /* per-node statistics */ + uint32_t ni_pad[8]; /* future expansion */ }; +MALLOC_DECLARE(M_80211_NODE); + +#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd) + +#define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++) +#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v) +#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v) static __inline struct ieee80211_node * ieee80211_ref_node(struct ieee80211_node *ni) { - atomic_add_int(&ni->ni_refcnt, 1); + ieee80211_node_incref(ni); return ni; } static __inline void ieee80211_unref_node(struct ieee80211_node **ni) { - atomic_subtract_int(&(*ni)->ni_refcnt, 1); + ieee80211_node_decref(*ni); *ni = NULL; /* guard against use */ } struct ieee80211com; -#ifdef MALLOC_DECLARE -MALLOC_DECLARE(M_80211_NODE); +void ieee80211_node_attach(struct ieee80211com *); +void ieee80211_node_lateattach(struct ieee80211com *); +void ieee80211_node_detach(struct ieee80211com *); + +static __inline int +ieee80211_node_is_authorized(const struct ieee80211_node *ni) +{ + return (ni->ni_flags & IEEE80211_NODE_AUTH); +} + +void ieee80211_node_authorize(struct ieee80211_node *); +void ieee80211_node_unauthorize(struct ieee80211_node *); + +void ieee80211_begin_scan(struct ieee80211com *, int); +int ieee80211_next_scan(struct ieee80211com *); +void ieee80211_probe_curchan(struct ieee80211com *, int); +void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *); +void ieee80211_reset_bss(struct ieee80211com *); +void ieee80211_cancel_scan(struct ieee80211com *); +void ieee80211_end_scan(struct ieee80211com *); +int ieee80211_ibss_merge(struct ieee80211_node *); +int ieee80211_sta_join(struct ieee80211com *, struct ieee80211_node *); +void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *); + +/* + * Table of ieee80211_node instances. Each ieee80211com + * has at least one for holding the scan candidates. + * When operating as an access point or in ibss mode there + * is a second table for associated stations or neighbors. + */ +struct ieee80211_node_table { + struct ieee80211com *nt_ic; /* back reference */ + TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */ + LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE]; + const char *nt_name; /* for debugging */ + int nt_inact_timer; /* inactivity timer */ + int nt_inact_init; /* initial node inact setting */ + struct ieee80211_node **nt_keyixmap; /* key ix -> node map */ + int nt_keyixmax; /* keyixmap size */ + + void (*nt_timeout)(struct ieee80211_node_table *); +}; +void ieee80211_node_table_reset(struct ieee80211_node_table *); + +struct ieee80211_node *ieee80211_alloc_node( + struct ieee80211_node_table *, const uint8_t *); +struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *, + const uint8_t *macaddr); +struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *, + const uint8_t *); +#ifdef IEEE80211_DEBUG_REFCNT +void ieee80211_free_node_debug(struct ieee80211_node *, + const char *func, int line); +struct ieee80211_node *ieee80211_find_node_debug( + struct ieee80211_node_table *, const uint8_t *, + const char *func, int line); +struct ieee80211_node * ieee80211_find_rxnode_debug( + struct ieee80211com *, const struct ieee80211_frame_min *, + const char *func, int line); +struct ieee80211_node * ieee80211_find_rxnode_withkey_debug( + struct ieee80211com *, + const struct ieee80211_frame_min *, uint16_t keyix, + const char *func, int line); +struct ieee80211_node *ieee80211_find_txnode_debug( + struct ieee80211com *, const uint8_t *, + const char *func, int line); +struct ieee80211_node *ieee80211_find_node_with_channel_debug( + struct ieee80211_node_table *, const uint8_t *macaddr, + struct ieee80211_channel *, const char *func, int line); +struct ieee80211_node *ieee80211_find_node_with_ssid_debug( + struct ieee80211_node_table *, const uint8_t *macaddr, + u_int ssidlen, const uint8_t *ssid, + const char *func, int line); +#define ieee80211_free_node(ni) \ + ieee80211_free_node_debug(ni, __func__, __LINE__) +#define ieee80211_find_node(nt, mac) \ + ieee80211_find_node_debug(nt, mac, __func__, __LINE__) +#define ieee80211_find_rxnode(nt, wh) \ + ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__) +#define ieee80211_find_rxnode_withkey(nt, wh, keyix) \ + ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__) +#define ieee80211_find_txnode(nt, mac) \ + ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__) +#define ieee80211_find_node_with_channel(nt, mac, c) \ + ieee80211_find_node_with_channel_debug(nt, mac, c, __func__, __LINE__) +#define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \ + ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__) +#else +void ieee80211_free_node(struct ieee80211_node *); +struct ieee80211_node *ieee80211_find_node( + struct ieee80211_node_table *, const uint8_t *); +struct ieee80211_node * ieee80211_find_rxnode( + struct ieee80211com *, const struct ieee80211_frame_min *); +struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *, + const struct ieee80211_frame_min *, uint16_t keyix); +struct ieee80211_node *ieee80211_find_txnode( + struct ieee80211com *, const uint8_t *); +struct ieee80211_node *ieee80211_find_node_with_channel( + struct ieee80211_node_table *, const uint8_t *macaddr, + struct ieee80211_channel *); +struct ieee80211_node *ieee80211_find_node_with_ssid( + struct ieee80211_node_table *, const uint8_t *macaddr, + u_int ssidlen, const uint8_t *ssid); #endif +int ieee80211_node_delucastkey(struct ieee80211_node *); -extern void ieee80211_node_attach(struct ifnet *); -extern void ieee80211_node_lateattach(struct ifnet *); -extern void ieee80211_node_detach(struct ifnet *); - -extern void ieee80211_begin_scan(struct ifnet *); -extern void ieee80211_next_scan(struct ifnet *); -extern void ieee80211_end_scan(struct ifnet *); -extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, - uint8_t *); -extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *, - uint8_t *); -extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, - uint8_t *); -extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *, - uint8_t *); -extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *, - uint8_t *macaddr, struct ieee80211_channel *); -extern void ieee80211_free_node(struct ieee80211com *, - struct ieee80211_node *); -extern void ieee80211_free_allnodes(struct ieee80211com *); typedef void ieee80211_iter_func(void *, struct ieee80211_node *); -extern void ieee80211_iterate_nodes(struct ieee80211com *ic, +void ieee80211_iterate_nodes(struct ieee80211_node_table *, ieee80211_iter_func *, void *); -extern void ieee80211_timeout_nodes(struct ieee80211com *); -extern void ieee80211_create_ibss(struct ieee80211com* , - struct ieee80211_channel *); -#endif /* _NETPROTO_802_11_IEEE80211_NODE_H_ */ +void ieee80211_dump_node(struct ieee80211_node_table *, + struct ieee80211_node *); +void ieee80211_dump_nodes(struct ieee80211_node_table *); + +struct ieee80211_node *ieee80211_fakeup_adhoc_node( + struct ieee80211_node_table *, const uint8_t macaddr[]); +void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int); +void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *); +uint8_t ieee80211_getrssi(struct ieee80211com *ic); + +/* + * Parameters supplied when adding/updating an entry in a + * scan cache. Pointer variables should be set to NULL + * if no data is available. Pointer references can be to + * local data; any information that is saved will be copied. + * All multi-byte values must be in host byte order. + */ +struct ieee80211_scanparams { + uint16_t capinfo; /* 802.11 capabilities */ + uint16_t fhdwell; /* FHSS dwell interval */ + uint8_t chan; /* */ + uint8_t bchan; + uint8_t fhindex; + uint8_t erp; + uint16_t bintval; + uint8_t timoff; + uint8_t *tim; + uint8_t *tstamp; + uint8_t *country; + uint8_t *ssid; + uint8_t *rates; + uint8_t *xrates; + uint8_t *wpa; + uint8_t *wme; +}; + +void ieee80211_add_scan(struct ieee80211com *, + const struct ieee80211_scanparams *, + const struct ieee80211_frame *, + int subtype, int rssi, int rstamp); +void ieee80211_init_neighbor(struct ieee80211_node *, + const struct ieee80211_frame *, + const struct ieee80211_scanparams *); +struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *, + const struct ieee80211_frame *, + const struct ieee80211_scanparams *); +#endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/netproto/802_11/ieee80211_proto.h b/sys/netproto/802_11/ieee80211_proto.h index 4464f5bb46..22158ff5bc 100644 --- a/sys/netproto/802_11/ieee80211_proto.h +++ b/sys/netproto/802_11/ieee80211_proto.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,11 @@ * (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: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.11.2.5 2006/02/12 19:00:39 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ - -#ifndef _NETPROTO_802_11_IEEE80211_PROTO_H_ -#define _NETPROTO_802_11_IEEE80211_PROTO_H_ +#ifndef _NET80211_IEEE80211_PROTO_H_ +#define _NET80211_IEEE80211_PROTO_H_ /* * 802.11 protocol implementation definitions. @@ -53,28 +52,208 @@ enum ieee80211_state { ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg)) extern const char *ieee80211_mgt_subtype_name[]; +extern const char *ieee80211_phymode_name[]; -extern void ieee80211_proto_attach(struct ifnet *); -extern void ieee80211_proto_detach(struct ifnet *); +void ieee80211_proto_attach(struct ieee80211com *); +void ieee80211_proto_detach(struct ieee80211com *); struct ieee80211_node; -extern void ieee80211_input(struct ifnet *, struct mbuf *, +int ieee80211_input(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, uint32_t); -extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, +int ieee80211_setup_rates(struct ieee80211_node *ni, + const uint8_t *rates, const uint8_t *xrates, int flags); +void ieee80211_saveie(uint8_t **, const uint8_t *); +void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, uint32_t); -extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, +int ieee80211_send_nulldata(struct ieee80211_node *); +int ieee80211_send_probereq(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t *ssid, size_t ssidlen, + const void *optie, size_t optielen); +int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, int, int); -extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *, - struct ieee80211_node **); -extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *); -extern uint8_t *ieee80211_add_rates(uint8_t *frm, - const struct ieee80211_rateset *); +int ieee80211_classify(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, + struct mbuf *); + +void ieee80211_reset_erp(struct ieee80211com *); +void ieee80211_set_shortslottime(struct ieee80211com *, int onoff); +int ieee80211_iserp_rateset(struct ieee80211com *, + struct ieee80211_rateset *); +void ieee80211_set11gbasicrates(struct ieee80211_rateset *, + enum ieee80211_phymode); + +/* + * Return the size of the 802.11 header for a management or data frame. + */ +static __inline int +ieee80211_hdrsize(const void *data) +{ + const struct ieee80211_frame *wh = data; + int size = sizeof(struct ieee80211_frame); + + /* NB: we don't handle control frames */ + KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL, + ("%s: control frame", __func__)); + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + size += IEEE80211_ADDR_LEN; + if (IEEE80211_QOS_HAS_SEQ(wh)) + size += sizeof(uint16_t); + return size; +} + +/* + * Return the size of the 802.11 header; handles any type of frame. + */ +static __inline int +ieee80211_anyhdrsize(const void *data) +{ + const struct ieee80211_frame *wh = data; + + if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { + switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_CTS: + case IEEE80211_FC0_SUBTYPE_ACK: + return sizeof(struct ieee80211_frame_ack); + } + return sizeof(struct ieee80211_frame_min); + } else + return ieee80211_hdrsize(data); +} + +/* + * Template for an in-kernel authenticator. Authenticators + * register with the protocol code and are typically loaded + * as separate modules as needed. + */ +struct ieee80211_authenticator { + const char *ia_name; /* printable name */ + int (*ia_attach)(struct ieee80211com *); + void (*ia_detach)(struct ieee80211com *); + void (*ia_node_join)(struct ieee80211com *, + struct ieee80211_node *); + void (*ia_node_leave)(struct ieee80211com *, + struct ieee80211_node *); +}; +void ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *); +void ieee80211_authenticator_unregister(int type); +const struct ieee80211_authenticator *ieee80211_authenticator_get(int auth); + +struct ieee80211req; +/* + * Template for an MAC ACL policy module. Such modules + * register with the protocol code and are passed the sender's + * address of each received frame for validation. + */ +struct ieee80211_aclator { + const char *iac_name; /* printable name */ + int (*iac_attach)(struct ieee80211com *); + void (*iac_detach)(struct ieee80211com *); + int (*iac_check)(struct ieee80211com *, + const uint8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_add)(struct ieee80211com *, + const uint8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_remove)(struct ieee80211com *, + const uint8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_flush)(struct ieee80211com *); + int (*iac_setpolicy)(struct ieee80211com *, int); + int (*iac_getpolicy)(struct ieee80211com *); + int (*iac_setioctl)(struct ieee80211com *, struct ieee80211req *); + int (*iac_getioctl)(struct ieee80211com *, struct ieee80211req *); +}; +void ieee80211_aclator_register(const struct ieee80211_aclator *); +void ieee80211_aclator_unregister(const struct ieee80211_aclator *); +const struct ieee80211_aclator *ieee80211_aclator_get(const char *name); + +/* flags for ieee80211_fix_rate() */ +#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ +#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ +#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ +#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ +int ieee80211_fix_rate(struct ieee80211_node *, int); + +/* + * WME/WMM support. + */ +struct wmeParams { + uint8_t wmep_acm; + uint8_t wmep_aifsn; + uint8_t wmep_logcwmin; /* log2(cwmin) */ + uint8_t wmep_logcwmax; /* log2(cwmax) */ + uint8_t wmep_txopLimit; + uint8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */ +}; +#define IEEE80211_TXOP_TO_US(_txop) ((_txop)<<5) +#define IEEE80211_US_TO_TXOP(_us) ((_us)>>5) + +struct chanAccParams { + uint8_t cap_info; /* version of the current set */ + struct wmeParams cap_wmeParams[WME_NUM_AC]; +}; + +struct ieee80211_wme_state { + u_int wme_flags; +#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME agressive mode */ + u_int wme_hipri_traffic; /* VI/VO frames in beacon interval */ + u_int wme_hipri_switch_thresh;/* agressive mode switch thresh */ + u_int wme_hipri_switch_hysteresis;/* agressive mode switch hysteresis */ + + struct wmeParams wme_params[4]; /* from assoc resp for each AC*/ + struct chanAccParams wme_wmeChanParams; /* WME params applied to self */ + struct chanAccParams wme_wmeBssChanParams;/* WME params bcast to stations */ + struct chanAccParams wme_chanParams; /* params applied to self */ + struct chanAccParams wme_bssChanParams; /* params bcast to stations */ + + int (*wme_update)(struct ieee80211com *); +}; + +void ieee80211_wme_initparams(struct ieee80211com *); +void ieee80211_wme_updateparams(struct ieee80211com *); + #define ieee80211_new_state(_ic, _nstate, _arg) \ (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) -extern uint8_t *ieee80211_add_xrates(uint8_t *frm, - const struct ieee80211_rateset *); -extern void ieee80211_print_essid(uint8_t *, int); -extern void ieee80211_dump_pkt(uint8_t *, int, int, int); +void ieee80211_beacon_miss(struct ieee80211com *); +void ieee80211_print_essid(const uint8_t *, int); +void ieee80211_dump_pkt(const uint8_t *, int, int, int); extern const char *ieee80211_state_name[IEEE80211_S_MAX]; -#endif /* _NETPROTO_802_11_IEEE80211_PROTO_H_ */ +extern const char *ieee80211_wme_acnames[]; + +/* + * Beacon frames constructed by ieee80211_beacon_alloc + * have the following structure filled in so drivers + * can update the frame later w/ minimal overhead. + */ +struct ieee80211_beacon_offsets { + uint16_t *bo_caps; /* capabilities */ + uint8_t *bo_tim; /* start of atim/dtim */ + uint8_t *bo_wme; /* start of WME parameters */ + uint8_t *bo_trailer; /* start of fixed-size trailer */ + uint16_t bo_tim_len; /* atim/dtim length in bytes */ + uint16_t bo_trailer_len; /* trailer length in bytes */ + uint8_t *bo_erp; /* start of ERP element */ + void *bo_pad[8]; /* future expansion */ +}; +struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_beacon_offsets *); +int ieee80211_beacon_update(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_beacon_offsets *, + struct mbuf *, int broadcast); + +/* + * Notification methods called from the 802.11 state machine. + * Note that while these are defined here, their implementation + * is OS-specific. + */ +void ieee80211_notify_node_join(struct ieee80211com *, + struct ieee80211_node *, int newassoc); +void ieee80211_notify_node_leave(struct ieee80211com *, + struct ieee80211_node *); +void ieee80211_notify_scan_done(struct ieee80211com *); +#endif /* _NET80211_IEEE80211_PROTO_H_ */ diff --git a/sys/netproto/802_11/ieee80211_radiotap.h b/sys/netproto/802_11/ieee80211_radiotap.h index 14e74540ef..f4e3a8fc84 100644 --- a/sys/netproto/802_11/ieee80211_radiotap.h +++ b/sys/netproto/802_11/ieee80211_radiotap.h @@ -1,6 +1,6 @@ -/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.3 2004/04/05 22:13:21 sam Exp $ */ -/* $NetBSD: ieee80211_radiotap.h,v 1.3 2003/11/16 09:02:42 dyoung Exp $ */ -/* $DragonFly: src/sys/netproto/802_11/ieee80211_radiotap.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ */ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5.2.1 2006/01/29 07:13:58 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.10 2005/01/04 00:34:58 dyoung Exp $ */ +/* $DragonFly: src/sys/netproto/802_11/ieee80211_radiotap.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ /* * Copyright (c) 2003, 2004 David Young. All rights reserved. @@ -30,11 +30,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ +#ifndef _NET_IF_IEEE80211RADIOTAP_H_ +#define _NET_IF_IEEE80211RADIOTAP_H_ -#ifndef _NETPROTO_802_11_IEEE80211RADIOTAP_H_ -#define _NETPROTO_802_11_IEEE80211RADIOTAP_H_ - -/* A generic radio capture format is desirable. There is one for +/* + * A generic radio capture format is desirable. There is one for * Linux, but it is neither rigidly defined (there were not even * units given for some fields) nor easily extensible. * @@ -48,11 +48,17 @@ * function of...") that I cannot set false expectations for lawyerly * readers. */ -#ifdef _KERNEL +#if defined(__KERNEL__) || defined(_KERNEL) #ifndef DLT_IEEE802_11_RADIO #define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ #endif -#endif /* _KERNEL */ +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ + +/* + * XXX tcpdump/libpcap do not tolerate variable-length headers, + * yet, so we pad every radiotap header to 64 bytes. Ugh. + */ +#define IEEE80211_RADIOTAP_HDRLEN 64 /* The radio capture header precedes the 802.11 header. */ struct ieee80211_radiotap_header { @@ -62,101 +68,101 @@ struct ieee80211_radiotap_header { * new fields does not count. */ uint8_t it_pad; - uint16_t it_len; /* length of the whole + uint16_t it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ - uint32_t it_present; /* A bitmap telling which + uint32_t it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. * Additional extensions are made * by setting bit 31. */ -} __attribute__((__packed__)); +} __packed; -/* Name Data type Units - * ---- --------- ----- +/* Name Data type Units + * ---- --------- ----- * - * IEEE80211_RADIOTAP_TSFT uint64_t microseconds + * IEEE80211_RADIOTAP_TSFT uint64_t microseconds * * Value in microseconds of the MAC's 64-bit 802.11 Time * Synchronization Function timer when the first bit of the * MPDU arrived at the MAC. For received frames, only. * - * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap + * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap * * Tx/Rx frequency in MHz, followed by flags (see below). * - * IEEE80211_RADIOTAP_FHSS uint16_t see below + * IEEE80211_RADIOTAP_FHSS uint16_t see below * * For frequency-hopping radios, the hop set (first byte) * and pattern (second byte). * - * IEEE80211_RADIOTAP_RATE uint8_t 500kb/s + * IEEE80211_RADIOTAP_RATE uint8_t 500kb/s * * Tx/Rx data rate * - * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from - * one milliwatt (dBm) + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from + * one milliwatt (dBm) * * RF signal power at the antenna, decibel difference from * one milliwatt. * - * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from - * one milliwatt (dBm) + * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from + * one milliwatt (dBm) * * RF noise power at the antenna, decibel difference from one * milliwatt. * - * IEEE80211_RADIOTAP_DB_ANTSIGNAL uint8_t decibel (dB) + * IEEE80211_RADIOTAP_DB_ANTSIGNAL uint8_t decibel (dB) * * RF signal power at the antenna, decibel difference from an * arbitrary, fixed reference. * - * IEEE80211_RADIOTAP_DB_ANTNOISE uint8_t decibel (dB) + * IEEE80211_RADIOTAP_DB_ANTNOISE uint8_t decibel (dB) * * RF noise power at the antenna, decibel difference from an * arbitrary, fixed reference point. * - * IEEE80211_RADIOTAP_BARKER_CODE_LOCK uint16_t unitless + * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless * * Quality of Barker code lock. Unitless. Monotonically * nondecreasing with "better" lock strength. Called "Signal * Quality" in datasheets. (Is there a standard way to measure * this?) * - * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless + * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless * * Transmit power expressed as unitless distance from max * power set at factory calibration. 0 is max power. * Monotonically nondecreasing with lower power levels. * - * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) * * Transmit power expressed as decibel distance from max power * set at factory calibration. 0 is max power. Monotonically * nondecreasing with lower power levels. * - * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from - * one milliwatt (dBm) + * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from + * one milliwatt (dBm) * * Transmit power expressed as dBm (decibels from a 1 milliwatt * reference). This is the absolute power level measured at * the antenna port. * - * IEEE80211_RADIOTAP_FLAGS uint8_t bitmap + * IEEE80211_RADIOTAP_FLAGS uint8_t bitmap * * Properties of transmitted and received frames. See flags * defined below. * - * IEEE80211_RADIOTAP_ANTENNA uint8_t antenna index + * IEEE80211_RADIOTAP_ANTENNA uint8_t antenna index * * Unitless indication of the Rx/Tx antenna for this packet. * The first antenna is antenna 0. * - * IEEE80211_RADIOTAP_FCS uint32_t data + * IEEE80211_RADIOTAP_FCS uint32_t data * * FCS from frame in network byte order. */ @@ -175,18 +181,17 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_ANTENNA = 11, IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, IEEE80211_RADIOTAP_DB_ANTNOISE = 13, - IEEE80211_RADIOTAP_FCS = 14, IEEE80211_RADIOTAP_EXT = 31, }; #ifndef _KERNEL /* Channel flags. */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ #endif /* !_KERNEL */ @@ -205,5 +210,11 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received * with fragmentation */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* does not pass FCS check */ -#endif /* _NETPROTO_802_11_IEEE80211RADIOTAP_H_ */ +#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */ diff --git a/sys/netproto/802_11/ieee80211_var.h b/sys/netproto/802_11/ieee80211_var.h index 265c5d4c89..2371309dde 100644 --- a/sys/netproto/802_11/ieee80211_var.h +++ b/sys/netproto/802_11/ieee80211_var.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,134 +29,81 @@ * (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: src/sys/net80211/ieee80211_var.h,v 1.15 2004/04/05 22:10:26 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_var.h,v 1.2 2005/01/26 00:37:40 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.22.2.11 2006/03/13 03:05:48 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_var.h,v 1.3 2006/05/18 13:51:46 sephe Exp $ */ - -#ifndef _NETPROTO_802_11_IEEE80211_VAR_H_ -#define _NETPROTO_802_11_IEEE80211_VAR_H_ +#ifndef _NET80211_IEEE80211_VAR_H_ +#define _NET80211_IEEE80211_VAR_H_ /* * Definitions for IEEE 802.11 drivers. */ +#define IEEE80211_DEBUG +#undef IEEE80211_DEBUG_REFCNT /* node refcnt stuff */ + +/* NB: portability glue must go first */ +#ifdef __NetBSD__ +#include +#elif __FreeBSD__ +#include +#elif __linux__ +#include +#elif __DragonFly__ +#include +#else +#error "No support for your operating system!" +#endif +#include #include #include -#include /* for ieee80211_stats */ +#include /* for ieee80211_stats */ #include #include -#define IEEE80211_CHAN_MAX 255 -#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ -#define IEEE80211_CHAN_ANYC \ - ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) +#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */ +#define IEEE80211_TXPOWER_MIN 0 /* kill radio */ -#define IEEE80211_TXPOWER_MAX 100 /* max power */ -#define IEEE80211_TXPOWER_MIN 0 /* kill radio (if possible) */ +#define IEEE80211_DTIM_MAX 15 /* max DTIM period */ +#define IEEE80211_DTIM_MIN 1 /* min DTIM period */ +#define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */ -enum ieee80211_phytype { - IEEE80211_T_DS, /* direct sequence spread spectrum */ - IEEE80211_T_FH, /* frequency hopping */ - IEEE80211_T_OFDM, /* frequency division multiplexing */ - IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ -}; -#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ - -/* XXX not really a mode; there are really multiple PHY's */ -enum ieee80211_phymode { - IEEE80211_MODE_AUTO = 0, /* autoselect */ - IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ - IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ - IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ - IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ - IEEE80211_MODE_TURBO = 5, /* 5GHz, OFDM, 2x clock */ -}; -#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1) - -enum ieee80211_opmode { - IEEE80211_M_STA = 1, /* infrastructure station */ - IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ - IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ - IEEE80211_M_HOSTAP = 6, /* Software Access Point */ - IEEE80211_M_MONITOR = 8 /* Monitor mode */ -}; +/* NB: min+max come from WiFi requirements */ +#define IEEE80211_BINTVAL_MAX 1000 /* max beacon interval (TU's) */ +#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval (TU's) */ +#define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TU's) */ -/* - * 802.11g protection mode. - */ -enum ieee80211_protmode { - IEEE80211_PROT_NONE = 0, /* no protection */ - IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ - IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ -}; +#define IEEE80211_BMISS_MAX 2 /* maximum consecutive bmiss allowed */ +#define IEEE80211_SWBMISS_THRESHOLD 50 /* s/w bmiss threshold (TU's) */ +#define IEEE80211_HWBMISS_DEFAULT 7 /* h/w bmiss threshold (beacons) */ -/* - * Channels are specified by frequency and attributes. - */ -struct ieee80211_channel { - uint16_t ic_freq; /* setting in Mhz */ - uint16_t ic_flags; /* see below */ -}; +#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ +#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ -/* bits 0-3 are for private use by drivers */ -/* channel attributes */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ +#define IEEE80211_FIXED_RATE_NONE -1 +#define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */ -/* - * Useful combinations of channel characteristics. - */ -#define IEEE80211_CHAN_FHSS \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) -#define IEEE80211_CHAN_A \ - (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) -#define IEEE80211_CHAN_B \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) -#define IEEE80211_CHAN_PUREG \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) -#define IEEE80211_CHAN_G \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) -#define IEEE80211_CHAN_T \ - (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) - -#define IEEE80211_IS_CHAN_FHSS(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) -#define IEEE80211_IS_CHAN_A(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) -#define IEEE80211_IS_CHAN_B(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) -#define IEEE80211_IS_CHAN_PUREG(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) -#define IEEE80211_IS_CHAN_G(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) -#define IEEE80211_IS_CHAN_T(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) - -#define IEEE80211_IS_CHAN_2GHZ(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) -#define IEEE80211_IS_CHAN_5GHZ(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) -#define IEEE80211_IS_CHAN_OFDM(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) -#define IEEE80211_IS_CHAN_CCK(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) -#define IEEE80211_IS_CHAN_GFSK(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) - -/* ni_chan encoding for FH phy */ -#define IEEE80211_FH_CHANMOD 80 -#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) -#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) -#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) +#define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX +#define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX + +#define IEEE80211_MS_TO_TU(x) (((x) * 1024) / 1000) +#define IEEE80211_TU_TO_MS(x) (((x) * 1000) / 1024) +#define IEEE80211_TU_TO_TICKS(x)(((x) * hz) / 1024) + +struct ieee80211_aclator; +struct sysctl_ctx_list; struct ieee80211com { struct arpcom ic_ac; +#define ic_if ic_ac.ac_if + SLIST_ENTRY(ieee80211com) ic_next; + struct ifnet *ic_ifp; /* associated device */ + struct ieee80211_stats ic_stats; /* statistics */ + struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */ + uint32_t ic_debug; /* debug msg flags */ + int ic_vap; /* virtual AP index */ + + int (*ic_reset)(struct ifnet *); void (*ic_recv_mgmt)(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, uint32_t); @@ -164,16 +111,19 @@ struct ieee80211com { struct ieee80211_node *, int, int); int (*ic_newstate)(struct ieee80211com *, enum ieee80211_state, int); - void (*ic_newassoc)(struct ieee80211com *, - struct ieee80211_node *, int); + void (*ic_newassoc)(struct ieee80211_node *, int); + void (*ic_updateslot)(struct ifnet *); + void (*ic_set_tim)(struct ieee80211_node *, int); uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; - u_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)]; - u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)]; - u_char ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)]; + uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; + uint8_t ic_chan_active[IEEE80211_CHAN_BYTES]; + uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; + struct ieee80211_node_table ic_scan; /* scan candidates */ struct ifqueue ic_mgtq; uint32_t ic_flags; /* state flags */ + uint32_t ic_flags_ext; /* extended state flags */ uint32_t ic_caps; /* capabilities */ uint16_t ic_modecaps; /* set of mode capabilities */ uint16_t ic_curmode; /* current mode */ @@ -181,97 +131,180 @@ struct ieee80211com { enum ieee80211_opmode ic_opmode; /* operation mode */ enum ieee80211_state ic_state; /* 802.11 state */ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ + enum ieee80211_roamingmode ic_roaming; /* roaming mode */ + struct ieee80211_node_table ic_sta; /* stations/neighbors */ + uint32_t *ic_aid_bitmap; /* association id map */ + uint16_t ic_max_aid; + uint16_t ic_sta_assoc; /* stations associated */ + uint16_t ic_ps_sta; /* stations in power save */ + uint16_t ic_ps_pending; /* ps sta's w/ pending frames */ + uint8_t *ic_tim_bitmap; /* power-save stations w/ data*/ + uint16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */ + uint8_t ic_dtim_period; /* DTIM period */ + uint8_t ic_dtim_count; /* DTIM count for last bcn */ struct ifmedia ic_media; /* interface media config */ struct bpf_if *ic_rawbpf; /* packet filter structure */ struct ieee80211_node *ic_bss; /* information for this node */ struct ieee80211_channel *ic_ibss_chan; + struct ieee80211_channel *ic_curchan; /* current channel */ int ic_fixed_rate; /* index to ic_sup_rates[] */ + int ic_mcast_rate; /* rate for mcast frames */ uint16_t ic_rtsthreshold; uint16_t ic_fragthreshold; - struct lwkt_token ic_nodetoken; /* on node table */ - u_int ic_scangen; /* gen# for timeout scan */ - struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *); - void (*ic_node_free)(struct ieee80211com *, - struct ieee80211_node *); - void (*ic_node_copy)(struct ieee80211com *, - struct ieee80211_node *, - const struct ieee80211_node *); - uint8_t (*ic_node_getrssi)(struct ieee80211com *, - struct ieee80211_node *); - TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */ - LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE]; + uint8_t ic_bmissthreshold; + uint8_t ic_bmiss_count; /* current beacon miss count */ + int ic_bmiss_max; /* max bmiss before scan */ + uint16_t ic_swbmiss_count;/* beacons in last period */ + uint16_t ic_swbmiss_period;/* s/w bmiss period */ + struct callout ic_swbmiss; /* s/w beacon miss timer */ + struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*); + void (*ic_node_free)(struct ieee80211_node *); + void (*ic_node_cleanup)(struct ieee80211_node *); + uint8_t (*ic_node_getrssi)(const struct ieee80211_node*); uint16_t ic_lintval; /* listen interval */ + uint16_t ic_bintval; /* beacon interval */ uint16_t ic_holdover; /* PM hold over duration */ uint16_t ic_txmin; /* min tx retry count */ uint16_t ic_txmax; /* max tx retry count */ uint16_t ic_txlifetime; /* tx lifetime */ - uint16_t ic_txpower; /* tx power setting (dbM) */ - uint16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ + uint16_t ic_txpowlimit; /* global tx power limit */ + uint16_t ic_pad0; /* was ic_bmisstimeout */ + uint16_t ic_nonerpsta; /* # non-ERP stations */ + uint16_t ic_longslotsta; /* # long slot time stations */ int ic_mgt_timer; /* mgmt timeout */ int ic_inact_timer; /* inactivity timer wait */ int ic_des_esslen; uint8_t ic_des_essid[IEEE80211_NWID_LEN]; struct ieee80211_channel *ic_des_chan; /* desired channel */ uint8_t ic_des_bssid[IEEE80211_ADDR_LEN]; - struct ieee80211_wepkey ic_nw_keys[IEEE80211_WEP_NKID]; - int ic_wep_txkey; /* default tx key index */ - void *ic_wep_ctx; /* wep crypt context */ - uint32_t ic_iv; /* initial vector for wep */ - struct ieee80211_stats ic_stats; /* statistics */ + void *ic_opt_ie; /* user-specified IE's */ + uint16_t ic_opt_ie_len; /* length of ni_opt_ie */ + /* + * Inactivity timer settings for nodes. + */ + int ic_inact_init; /* initial setting */ + int ic_inact_auth; /* auth but not assoc setting */ + int ic_inact_run; /* authorized setting */ + int ic_inact_probe; /* inactive probe time */ + + /* + * WME/WMM state. + */ + struct ieee80211_wme_state ic_wme; + + /* + * Cipher state/configuration. + */ + struct ieee80211_crypto_state ic_crypto; +#define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */ +#define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */ + + /* + * 802.1x glue. When an authenticator attaches it + * fills in this section. We assume that when ic_ec + * is setup that the methods are safe to call. + */ + const struct ieee80211_authenticator *ic_auth; + struct eapolcom *ic_ec; + + /* + * Access control glue. When a control agent attaches + * it fills in this section. We assume that when ic_ac + * is setup that the methods are safe to call. + */ + const struct ieee80211_aclator *ic_acl; + void *ic_as; + uint32_t ic_pad[56]; /* future expansion */ }; -#define ic_if ic_ac.ac_if -#define ic_softc ic_if.if_softc #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) #define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) /* ic_flags */ -#define IEEE80211_F_ASCAN 0x00000001 /* STATUS: active scan */ -#define IEEE80211_F_SIBSS 0x00000002 /* STATUS: start IBSS */ -#define IEEE80211_F_WEPON 0x00000100 /* CONF: WEP enabled */ -#define IEEE80211_F_IBSSON 0x00000200 /* CONF: IBSS creation enable */ -#define IEEE80211_F_PMGTON 0x00000400 /* CONF: Power mgmt enable */ -#define IEEE80211_F_DESBSSID 0x00000800 /* CONF: des_bssid is set */ -#define IEEE80211_F_SCANAP 0x00001000 /* CONF: Scanning AP */ -#define IEEE80211_F_ROAMING 0x00002000 /* CONF: roaming enabled */ -#define IEEE80211_F_SWRETRY 0x00004000 /* CONF: sw tx retry enabled */ -#define IEEE80211_F_TXPMGT 0x00018000 /* STATUS: tx power */ -#define IEEE80211_F_TXPOW_OFF 0x00000000 /* TX Power: radio disabled */ -#define IEEE80211_F_TXPOW_FIXED 0x00008000 /* TX Power: fixed rate */ -#define IEEE80211_F_TXPOW_AUTO 0x00010000 /* TX Power: undefined */ -#define IEEE80211_F_SHSLOT 0x00020000 /* CONF: short slot time */ -#define IEEE80211_F_SHPREAMBLE 0x00040000 /* CONF: short preamble */ +/* NB: bits 0x4c available */ +#define IEEE80211_F_FF 0x00000001 /* CONF: ATH FF enabled */ +#define IEEE80211_F_TURBOP 0x00000002 /* CONF: ATH Turbo enabled*/ +#define IEEE80211_F_BURST 0x00000004 /* CONF: bursting enabled */ +/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ +#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ +#define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */ +#define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */ +#define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */ +#define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */ +/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */ +#define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/ +#define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */ +#define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */ +#define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */ +#define IEEE80211_F_BGSCAN 0x00004000 /* CONF: bg scan enabled (???)*/ +#define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */ +#define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */ +#define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */ +#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ +#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */ #define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ #define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ +#define IEEE80211_F_TIMUPDATE 0x00400000 /* STATUS: update beacon tim */ +#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */ +#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */ +#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */ +#define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */ +#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */ +#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ +#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ +#define IEEE80211_F_WMEUPDATE 0x20000000 /* STATUS: update beacon wme */ + +/* ic_flags_ext */ +#define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */ +/* 0x00000006 reserved */ +#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: enable full bgscan completion */ +#define IEEE80211_FEXT_ERPUPDATE 0x00000200 /* STATUS: update ERP element */ +#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */ +#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/ /* ic_caps */ #define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ -#define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */ -#define IEEE80211_C_PMGT 0x00000004 /* CAPABILITY: Power mgmt */ -#define IEEE80211_C_HOSTAP 0x00000008 /* CAPABILITY: HOSTAP avail */ -#define IEEE80211_C_AHDEMO 0x00000010 /* CAPABILITY: Old Adhoc Demo */ -#define IEEE80211_C_SWRETRY 0x00000020 /* CAPABILITY: sw tx retry */ -#define IEEE80211_C_TXPMGT 0x00000040 /* CAPABILITY: tx power mgmt */ -#define IEEE80211_C_SHSLOT 0x00000080 /* CAPABILITY: short slottime */ -#define IEEE80211_C_SHPREAMBLE 0x00000100 /* CAPABILITY: short preamble */ -#define IEEE80211_C_MONITOR 0x00000200 /* CAPABILITY: monitor mode */ - -/* flags for ieee80211_fix_rate() */ -#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ -#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ -#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ -#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ - -void ieee80211_ifattach(struct ifnet *); -void ieee80211_ifdetach(struct ifnet *); -void ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t); +#define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */ +#define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */ +#define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */ +#define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */ +#define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */ +#define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/ +#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ +#define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ +#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ +#define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ +#define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ +#define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ +#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ +#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ +#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ +#define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */ +#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ +#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ +#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ +#define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ +#define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ +#define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */ +/* 0x10000000 reserved */ +#define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */ +#define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ +/* XXX protection/barker? */ + +#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */ + +void ieee80211_ifattach(struct ieee80211com *); +void ieee80211_ifdetach(struct ieee80211com *); +void ieee80211_announce(struct ieee80211com *); +void ieee80211_media_init(struct ieee80211com *, + ifm_change_cb_t, ifm_stat_cb_t); +struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); -int ieee80211_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); -int ieee80211_cfgget(struct ifnet *, u_long, caddr_t, struct ucred *); -int ieee80211_cfgset(struct ifnet *, u_long, caddr_t); -void ieee80211_watchdog(struct ifnet *); -int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int); +int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t, struct ucred *); +int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t, struct ucred *); +int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t); +void ieee80211_watchdog(struct ieee80211com *); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); @@ -282,14 +315,127 @@ int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, struct ieee80211_channel *); -#define IEEE80211_DEBUG +uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int); +uint8_t *ieee80211_add_xrates(uint8_t *, const struct ieee80211_rateset *); +uint8_t *ieee80211_add_rates(uint8_t *, const struct ieee80211_rateset *); + +/* + * Key update synchronization methods. XXX should not be visible. + */ +static __inline void +ieee80211_key_update_begin(struct ieee80211com *ic) +{ + ic->ic_crypto.cs_key_update_begin(ic); +} +static __inline void +ieee80211_key_update_end(struct ieee80211com *ic) +{ + ic->ic_crypto.cs_key_update_end(ic); +} + +/* + * XXX these need to be here for IEEE80211_F_DATAPAD + */ + +/* + * Return the space occupied by the 802.11 header and any + * padding required by the driver. This works for a + * management or data frame. + */ +static __inline int +ieee80211_hdrspace(struct ieee80211com *ic, const void *data) +{ + int size = ieee80211_hdrsize(data); + if (ic->ic_flags & IEEE80211_F_DATAPAD) + size = roundup(size, sizeof(uint32_t)); + return size; +} + +/* + * Like ieee80211_hdrspace, but handles any type of frame. + */ +static __inline int +ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) +{ + int size = ieee80211_anyhdrsize(data); + if (ic->ic_flags & IEEE80211_F_DATAPAD) + size = roundup(size, sizeof(uint32_t)); + return size; +} + +#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ +#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ +#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ +#define IEEE80211_MSG_INPUT 0x08000000 /* input handling */ +#define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */ +#define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */ +#define IEEE80211_MSG_NODE 0x01000000 /* node handling */ +#define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */ +#define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */ +#define IEEE80211_MSG_SCAN 0x00200000 /* scanning */ +#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */ +#define IEEE80211_MSG_STATE 0x00080000 /* state machine */ +#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */ +#define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */ +#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */ +#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */ +#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */ +#define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */ +#define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */ +#define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */ +#define IEEE80211_MSG_WME 0x00000400 /* WME protocol */ +#define IEEE80211_MSG_SUPERG 0x00000200 /* Atheros SuperG protocol */ +#define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */ +#define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */ +#define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */ + +#define IEEE80211_MSG_ANY 0xffffffff /* anything */ + #ifdef IEEE80211_DEBUG -extern int ieee80211_debug; -#define IEEE80211_DPRINTF(X) if (ieee80211_debug) printf X -#define IEEE80211_DPRINTF2(X) if (ieee80211_debug>1) printf X +#define ieee80211_msg(_ic, _m) ((_ic)->ic_debug & (_m)) +#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \ + if (ieee80211_msg(_ic, _m)) \ + ieee80211_note(_ic, _fmt, __VA_ARGS__); \ +} while (0) +#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) do { \ + if (ieee80211_msg(_ic, _m)) \ + ieee80211_note_mac(_ic, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\ +} while (0) +#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) do { \ + if (ieee80211_msg(_ic, _m)) \ + ieee80211_note_mac(_ic, _mac, _fmt, __VA_ARGS__); \ +} while (0) +#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) do { \ + if (ieee80211_msg(_ic, _m)) \ + ieee80211_note_frame(_ic, _wh, _fmt, __VA_ARGS__); \ +} while (0) +void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...); +void ieee80211_note_mac(struct ieee80211com *ic, + const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...); +void ieee80211_note_frame(struct ieee80211com *ic, + const struct ieee80211_frame *wh, const char *fmt, ...); +#define ieee80211_msg_debug(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_DEBUG) +#define ieee80211_msg_dumppkts(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS) +#define ieee80211_msg_input(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_INPUT) +#define ieee80211_msg_radius(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_RADIUS) +#define ieee80211_msg_dumpradius(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_RADDUMP) +#define ieee80211_msg_dumpradkeys(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_RADKEYS) +#define ieee80211_msg_scan(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_SCAN) +#define ieee80211_msg_assoc(_ic) \ + ((_ic)->ic_debug & IEEE80211_MSG_ASSOC) #else -#define IEEE80211_DPRINTF(X) -#define IEEE80211_DPRINTF2(X) +#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) +#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) +#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) +#define ieee80211_msg_dumppkts(_ic) 0 +#define ieee80211_msg(_ic, _m) 0 #endif -#endif /* _NETPROTO_802_11_IEEE80211_VAR_H_ */ +#endif /* _NET80211_IEEE80211_VAR_H_ */ diff --git a/sys/netproto/802_11/wlan/Makefile b/sys/netproto/802_11/wlan/Makefile index 04e9a429d3..5d1c628021 100644 --- a/sys/netproto/802_11/wlan/Makefile +++ b/sys/netproto/802_11/wlan/Makefile @@ -1,8 +1,10 @@ -# $DragonFly: src/sys/netproto/802_11/wlan/Makefile,v 1.4 2005/02/18 11:41:42 corecode Exp $ +# $DragonFly: src/sys/netproto/802_11/wlan/Makefile,v 1.5 2006/05/18 13:51:46 sephe Exp $ KMOD = wlan -SRCS = ieee80211.c ieee80211_crypto.c ieee80211_input.c ieee80211_ioctl.c -SRCS += ieee80211_node.c ieee80211_output.c ieee80211_proto.c +SRCS = ieee80211.c ieee80211_ioctl.c ieee80211_node.c ieee80211_proto.c +SRCS += ieee80211_crypto.c ieee80211_crypto_none.c +SRCS += ieee80211_input.c ieee80211_output.c +SRCS += ieee80211_dragonfly.c SRCS += bus_if.h device_if.h opt_inet.h opt_ipx.h .if !defined(BUILDING_WITH_KERNEL) diff --git a/sys/netproto/802_11/wlan/ieee80211.c b/sys/netproto/802_11/wlan/ieee80211.c index 7d9b53ac0d..3ff77cc9e0 100644 --- a/sys/netproto/802_11/wlan/ieee80211.c +++ b/sys/netproto/802_11/wlan/ieee80211.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,78 +29,114 @@ * (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: src/sys/net80211/ieee80211.c,v 1.13 2004/05/30 17:57:45 phk Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211.c,v 1.5 2006/01/14 13:36:39 swildner Exp $ + * $FreeBSD: src/sys/net80211/ieee80211.c,v 1.19.2.7 2006/03/11 19:25:23 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211.c,v 1.6 2006/05/18 13:51:46 sephe Exp $ */ /* * IEEE 802.11 generic handler */ -#include "opt_inet.h" - #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include +#include + #include -#include -#include #include +#include #include -#include -#include #include #include -#ifdef INET -#include -#include -#endif - -#ifdef IEEE80211_DEBUG -int ieee80211_debug = 0; -SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug, - 0, "IEEE 802.11 media debugging printfs"); -#endif - -static void ieee80211_set11gbasicrates(struct ieee80211_rateset *, - enum ieee80211_phymode); - -static const char *ieee80211_phymode_name[] = { +const char *ieee80211_phymode_name[] = { "auto", /* IEEE80211_MODE_AUTO */ "11a", /* IEEE80211_MODE_11A */ "11b", /* IEEE80211_MODE_11B */ "11g", /* IEEE80211_MODE_11G */ "FH", /* IEEE80211_MODE_FH */ - "turbo", /* IEEE80211_MODE_TURBO */ + "turboA", /* IEEE80211_MODE_TURBO_A */ + "turboG", /* IEEE80211_MODE_TURBO_G */ }; +/* list of all instances */ +SLIST_HEAD(ieee80211_list, ieee80211com); +static struct ieee80211_list ieee80211_list = + SLIST_HEAD_INITIALIZER(ieee80211_list); +static uint8_t ieee80211_vapmap[32]; /* enough for 256 */ + +static void +ieee80211_add_vap(struct ieee80211com *ic) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + int i; + uint8_t b; + + crit_enter(); + + ic->ic_vap = 0; + for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) + ic->ic_vap += NBBY; + if (i == N(ieee80211_vapmap)) + panic("vap table full"); + for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) + ic->ic_vap++; + setbit(ieee80211_vapmap, ic->ic_vap); + SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); + + crit_exit(); +#undef N +} + +static void +ieee80211_remove_vap(struct ieee80211com *ic) +{ + crit_enter(); + + SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); + KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, + ("invalid vap id %d", ic->ic_vap)); + KASSERT(isset(ieee80211_vapmap, ic->ic_vap), + ("vap id %d not allocated", ic->ic_vap)); + clrbit(ieee80211_vapmap, ic->ic_vap); + + crit_exit(); +} + +/* + * Default reset method for use with the ioctl support. This + * method is invoked after any state change in the 802.11 + * layer that should be propagated to the hardware but not + * require re-initialization of the 802.11 state machine (e.g + * rescanning for an ap). We always return ENETRESET which + * should cause the driver to re-initialize the device. Drivers + * can override this method to implement more optimized support. + */ +static int +ieee80211_default_reset(struct ifnet *ifp) +{ + return ENETRESET; +} + void -ieee80211_ifattach(struct ifnet *ifp) +ieee80211_ifattach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp; struct ieee80211_channel *c; int i; + ifp = &ic->ic_ac.ac_if; + ic->ic_ifp = ifp; + ether_ifattach(ifp, ic->ic_myaddr, NULL); bpfattach_dlt(ifp, DLT_IEEE802_11, sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); - ieee80211_crypto_attach(ifp); + + ieee80211_crypto_attach(ic); /* * Fill in 802.11 available channel set, mark @@ -135,33 +171,79 @@ ieee80211_ifattach(struct ifnet *ifp) if (IEEE80211_IS_CHAN_FHSS(c)) ic->ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_curchan == NULL) { + /* arbitrarily pick the first channel */ + ic->ic_curchan = &ic->ic_channels[i]; + } } } /* validate ic->ic_curmode */ if ((ic->ic_modecaps & (1<ic_curmode)) == 0) ic->ic_curmode = IEEE80211_MODE_AUTO; ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ - +#if 0 + /* + * Enable WME by default if we're capable. + */ + if (ic->ic_caps & IEEE80211_C_WME) + ic->ic_flags |= IEEE80211_F_WME; +#endif + if (ic->ic_caps & IEEE80211_C_BURST) + ic->ic_flags |= IEEE80211_F_BURST; ieee80211_setmode(ic, ic->ic_curmode); - if (ic->ic_lintval == 0) - ic->ic_lintval = 100; /* default sleep */ - ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ + ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; + ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; + ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; + + ic->ic_lintval = ic->ic_bintval; + ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; - ieee80211_node_attach(ifp); - ieee80211_proto_attach(ifp); + ieee80211_node_attach(ic); + ieee80211_proto_attach(ic); + + ieee80211_add_vap(ic); + + ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ + + /* + * Install a default reset method for the ioctl support. + * The driver is expected to fill this in before calling us. + */ + if (ic->ic_reset == NULL) + ic->ic_reset = ieee80211_default_reset; } void -ieee80211_ifdetach(struct ifnet *ifp) +ieee80211_ifdetach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; + + ieee80211_remove_vap(ic); + + ieee80211_sysctl_detach(ic); + ieee80211_proto_detach(ic); + ieee80211_crypto_detach(ic); + + /* + * XXX + * ieee80211_node_detach() -> ieee80211_node_table_cleanup() + * -> ieee80211_free_allnodes_locked() + * will assert the serializer + * In order to make the assertion work, hold serializer here + * + * SHOULD BE REMOVED + */ + lwkt_serialize_enter(ifp->if_serializer); + ieee80211_node_detach(ic); + lwkt_serialize_exit(ifp->if_serializer); - ieee80211_proto_detach(ifp); - ieee80211_crypto_detach(ifp); - ieee80211_node_detach(ifp); ifmedia_removeall(&ic->ic_media); + + bpfdetach(ifp); ether_ifdetach(ifp); } @@ -202,11 +284,11 @@ ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) else if (c == IEEE80211_CHAN_ANYC) return IEEE80211_CHAN_ANY; else if (c != NULL) { - if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n", + if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", c->ic_freq, c->ic_flags); return 0; /* XXX */ } else { - if_printf(&ic->ic_if, "invalid channel (NULL)\n"); + if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); return 0; /* XXX */ } } @@ -243,23 +325,36 @@ ieee80211_ieee2mhz(u_int chan, u_int flags) * ieee80211_attach and before most anything else. */ void -ieee80211_media_init(struct ifnet *ifp, +ieee80211_media_init(struct ieee80211com *ic, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { #define ADD(_ic, _s, _o) \ ifmedia_add(&(_ic)->ic_media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; struct ifmediareq imr; int i, j, mode, rate, maxrate, mword, mopt, r; struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; + /* + * XXX + * ieee80211_node_lateattach() -> ieee80211_rest_bss() + * -> ieee80211_alloc_node() -> ieee80211_setup_node() + * will assert the serializer + * In order to make the assertion work, hold serializer here + * + * SHOULD BE REMOVED + */ + lwkt_serialize_enter(ifp->if_serializer); + /* * Do late attach work that must wait for any subclass * (i.e. driver) work such as overriding methods. */ - ieee80211_node_lateattach(ifp); + ieee80211_node_lateattach(ic); + + lwkt_serialize_exit(ifp->if_serializer); /* * Fill in media characteristics. @@ -268,13 +363,14 @@ ieee80211_media_init(struct ifnet *ifp, maxrate = 0; memset(&allrates, 0, sizeof(allrates)); for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { - static const u_int mopts[] = { + static const u_int mopts[] = { IFM_AUTO, IFM_IEEE80211_11A, IFM_IEEE80211_11B, IFM_IEEE80211_11G, IFM_IEEE80211_FH, IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, }; if ((ic->ic_modecaps & (1<ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; - printf("%s%d%sMbps", (i != 0 ? " " : ""), - (rate & IEEE80211_RATE_VAL) / 2, - ((rate & 0x1) != 0 ? ".5" : "")); ADD(ic, mword, mopt); if (ic->ic_caps & IEEE80211_C_IBSS) ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); @@ -325,7 +417,6 @@ ieee80211_media_init(struct ifnet *ifp, if (rate > maxrate) maxrate = rate; } - printf("\n"); } for (i = 0; i < allrates.rs_nrates; i++) { mword = ieee80211_rate2media(ic, allrates.rs_rates[i], @@ -351,6 +442,31 @@ ieee80211_media_init(struct ifnet *ifp, #undef ADD } +void +ieee80211_announce(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + int i, mode, rate, mword; + struct ieee80211_rateset *rs; + + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { + if ((ic->ic_modecaps & (1<ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + mword = ieee80211_rate2media(ic, rate, mode); + if (mword == 0) + continue; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + (rate & IEEE80211_RATE_VAL) / 2, + ((rate & 0x1) != 0 ? ".5" : "")); + } + printf("\n"); + } +} + static int findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) { @@ -364,18 +480,51 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) #undef IEEERATE } +/* + * Find an instance by it's mac address. + */ +struct ieee80211com * +ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic; + + /* XXX lock */ + SLIST_FOREACH(ic, &ieee80211_list, ic_next) + if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) + return ic; + return NULL; +} + +static struct ieee80211com * +ieee80211_find_instance(struct ifnet *ifp) +{ + struct ieee80211com *ic; + + /* XXX lock */ + /* XXX not right for multiple instances but works for now */ + SLIST_FOREACH(ic, &ieee80211_list, ic_next) + if (ic->ic_ifp == ifp) + return ic; + return NULL; +} + /* * Handle a media change request. */ int ieee80211_media_change(struct ifnet *ifp) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211com *ic; struct ifmedia_entry *ime; enum ieee80211_opmode newopmode; enum ieee80211_phymode newphymode; int i, j, newrate, error = 0; + ic = ieee80211_find_instance(ifp); + if (!ic) { + if_printf(ifp, "%s: no 802.11 instance!\n", __func__); + return EINVAL; + } ime = ic->ic_media.ifm_cur; /* * First, identify the phy mode. @@ -400,13 +549,16 @@ ieee80211_media_change(struct ifnet *ifp) return EINVAL; } /* - * Turbo mode is an ``option''. Eventually it - * needs to be applied to 11g too. + * Turbo mode is an ``option''. + * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { - if (newphymode != IEEE80211_MODE_11A) + if (newphymode == IEEE80211_MODE_11A) + newphymode = IEEE80211_MODE_TURBO_A; + else if (newphymode == IEEE80211_MODE_11G) + newphymode = IEEE80211_MODE_TURBO_G; + else return EINVAL; - newphymode = IEEE80211_MODE_TURBO; } /* * Validate requested mode is available. @@ -513,14 +665,15 @@ ieee80211_media_change(struct ifnet *ifp) break; case IEEE80211_M_IBSS: ic->ic_flags |= IEEE80211_F_IBSSON; -#ifdef notdef - if (ic->ic_curmode == IEEE80211_MODE_11G) - ieee80211_set11gbasicrates( - &ic->ic_suprates[newphymode], - IEEE80211_MODE_11B); -#endif break; } + /* + * Yech, slot time may change depending on the + * operating mode so reset it to be sure everything + * is setup appropriately. + */ + ieee80211_reset_erp(ic); + ieee80211_wme_initparams(ic); /* after opmode change */ error = ENETRESET; } #ifdef notdef @@ -533,26 +686,39 @@ ieee80211_media_change(struct ifnet *ifp) void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni = NULL; - int old_status = imr->ifm_status; + struct ieee80211com *ic; + struct ieee80211_rateset *rs; + ic = ieee80211_find_instance(ifp); + if (!ic) { + if_printf(ifp, "%s: no 802.11 instance!\n", __func__); + return; + } imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; -/* XXX - if (ic->ic_state == IEEE80211_S_RUN) { + if (ic->ic_state == IEEE80211_S_RUN) imr->ifm_status |= IFM_ACTIVE; - ifp->if_link_state = LINK_STATE_UP; + /* + * Calculate a current rate if possible. + */ + if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { + /* + * A fixed rate is set, report that. + */ + rs = &ic->ic_sup_rates[ic->ic_curmode]; + imr->ifm_active |= ieee80211_rate2media(ic, + rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); + } else if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * In station mode report the current transmit rate. + */ + rs = &ic->ic_bss->ni_rates; + imr->ifm_active |= ieee80211_rate2media(ic, + rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); } else - ifp->if_link_state = LINK_STATE_DOWN; -*/ - imr->ifm_active |= IFM_AUTO; + imr->ifm_active |= IFM_AUTO; switch (ic->ic_opmode) { case IEEE80211_M_STA: - ni = ic->ic_bss; - /* calculate rate subtype */ - imr->ifm_active |= ieee80211_rate2media(ic, - ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); break; case IEEE80211_M_IBSS: imr->ifm_active |= IFM_IEEE80211_ADHOC; @@ -580,58 +746,41 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_MODE_FH: imr->ifm_active |= IFM_IEEE80211_FH; break; - case IEEE80211_MODE_TURBO: + case IEEE80211_MODE_TURBO_A: imr->ifm_active |= IFM_IEEE80211_11A | IFM_IEEE80211_TURBO; break; + case IEEE80211_MODE_TURBO_G: + imr->ifm_active |= IFM_IEEE80211_11G + | IFM_IEEE80211_TURBO; + break; } - - /* Notify that the link state has changed. */ - if (imr->ifm_status != old_status) - rt_ifmsg(ifp); } void -ieee80211_watchdog(struct ifnet *ifp) +ieee80211_watchdog(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - - if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) - ieee80211_timeout_nodes(ic); - - if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) - ifp->if_timer = 1; -} + struct ieee80211_node_table *nt; + int need_inact_timer = 0; -/* - * Mark the basic rates for the 11g rate table based on the - * operating mode. For real 11g we mark all the 11b rates - * and 6, 12, and 24 OFDM. For 11b compatibility we mark only - * 11b rates. There's also a pseudo 11a-mode used to mark only - * the basic OFDM rates. - */ -static void -ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) -{ - static const struct ieee80211_rateset basic[] = { - { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ - { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */ - { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */ - { 0 }, /* IEEE80211_MODE_FH */ - { 0 }, /* IEEE80211_MODE_TURBO */ - }; - int i, j; - - for (i = 0; i < rs->rs_nrates; i++) { - rs->rs_rates[i] &= IEEE80211_RATE_VAL; - for (j = 0; j < basic[mode].rs_nrates; j++) - if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { - rs->rs_rates[i] |= IEEE80211_RATE_BASIC; - break; - } + if (ic->ic_state != IEEE80211_S_INIT) { + if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + nt = &ic->ic_scan; + if (nt->nt_inact_timer) { + if (--nt->nt_inact_timer == 0) + nt->nt_timeout(nt); + need_inact_timer += nt->nt_inact_timer; + } + nt = &ic->ic_sta; + if (nt->nt_inact_timer) { + if (--nt->nt_inact_timer == 0) + nt->nt_timeout(nt); + need_inact_timer += nt->nt_inact_timer; + } } + if (ic->ic_mgt_timer != 0 || need_inact_timer) + ic->ic_ifp->if_timer = 1; } /* @@ -650,7 +799,8 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ - IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ + IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ + IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ }; struct ieee80211_channel *c; u_int modeflags; @@ -658,8 +808,9 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) /* validate new mode */ if ((ic->ic_modecaps & (1<ic_modecaps)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: mode %u not supported (caps 0x%x)\n", + __func__, mode, ic->ic_modecaps); return EINVAL; } @@ -671,9 +822,11 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) modeflags = chanflags[mode]; for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { c = &ic->ic_channels[i]; + if (c->ic_flags == 0) + continue; if (mode == IEEE80211_MODE_AUTO) { - /* ignore turbo channels for autoselect */ - if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) + /* ignore static turbo channels for autoselect */ + if (!IEEE80211_IS_CHAN_T(c)) break; } else { if ((c->ic_flags & modeflags) == modeflags) @@ -681,8 +834,8 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) } } if (i > IEEE80211_CHAN_MAX) { - IEEE80211_DPRINTF(("%s: no channels found for mode %u\n", - __func__, mode)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: no channels found for mode %u\n", __func__, mode); return EINVAL; } @@ -692,9 +845,11 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { c = &ic->ic_channels[i]; + if (c->ic_flags == 0) + continue; if (mode == IEEE80211_MODE_AUTO) { - /* take anything but pure turbo channels */ - if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) + /* take anything but static turbo channels */ + if (!IEEE80211_IS_CHAN_T(c)) setbit(ic->ic_chan_active, i); } else { if ((c->ic_flags & modeflags) == modeflags) @@ -714,57 +869,80 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) ic->ic_ibss_chan = &ic->ic_channels[i]; break; } + KASSERT(ic->ic_ibss_chan != NULL && + isset(ic->ic_chan_active, + ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), + ("Bad IBSS channel %u", + ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); } + /* + * If the desired channel is set but no longer valid then reset it. + */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* - * Set/reset state flags that influence beacon contents, etc. - * - * XXX what if we have stations already associated??? - * XXX probably not right for autoselect? + * Do mode-specific rate setup. */ - if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) - ic->ic_flags |= IEEE80211_F_SHPREAMBLE; if (mode == IEEE80211_MODE_11G) { - if (ic->ic_caps & IEEE80211_C_SHSLOT) - ic->ic_flags |= IEEE80211_F_SHSLOT; + /* + * Use a mixed 11b/11g rate set. + */ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], IEEE80211_MODE_11G); - } else { - ic->ic_flags &= ~IEEE80211_F_SHSLOT; + } else if (mode == IEEE80211_MODE_11B) { + /* + * Force pure 11b rate set. + */ + ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], + IEEE80211_MODE_11B); } + /* + * Setup an initial rate set according to the + * current/default channel selected above. This + * will be changed when scanning but must exist + * now so driver have a consistent state of ic_ibss_chan. + */ + if (ic->ic_bss) /* NB: can be called before lateattach */ + ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; ic->ic_curmode = mode; + ieee80211_reset_erp(ic); /* reset ERP state */ + ieee80211_wme_initparams(ic); /* reset WME stat */ + return 0; #undef N } /* * Return the phy mode for with the specified channel so the - * caller can select a rate set. This is problematic and the - * work here assumes how things work elsewhere in this code. + * caller can select a rate set. This is problematic for channels + * where multiple operating modes are possible (e.g. 11g+11b). + * In those cases we defer to the current operating mode when set. */ enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) { - /* - * NB: this assumes the channel would not be supplied to us - * unless it was already compatible with the current mode. - */ - if (ic->ic_curmode != IEEE80211_MODE_AUTO) - return ic->ic_curmode; - /* - * In autoselect mode; deduce a mode based on the channel - * characteristics. We assume that turbo-only channels - * are not considered when the channel set is constructed. - */ - if (IEEE80211_IS_CHAN_5GHZ(chan)) + if (IEEE80211_IS_CHAN_T(chan)) { + return IEEE80211_MODE_TURBO_A; + } else if (IEEE80211_IS_CHAN_5GHZ(chan)) { return IEEE80211_MODE_11A; - else if (IEEE80211_IS_CHAN_FHSS(chan)) + } else if (IEEE80211_IS_CHAN_FHSS(chan)) { return IEEE80211_MODE_FH; - else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) + } else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { + /* + * This assumes all 11g channels are also usable + * for 11b, which is currently true. + */ + if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) + return IEEE80211_MODE_TURBO_G; + if (ic->ic_curmode == IEEE80211_MODE_11B) + return IEEE80211_MODE_11B; return IEEE80211_MODE_11G; - else + } else { return IEEE80211_MODE_11B; + } } /* @@ -813,7 +991,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m mask = rate & IEEE80211_RATE_VAL; switch (mode) { case IEEE80211_MODE_11A: - case IEEE80211_MODE_TURBO: + case IEEE80211_MODE_TURBO_A: mask |= IFM_IEEE80211_11A; break; case IEEE80211_MODE_11B: @@ -831,6 +1009,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: + case IEEE80211_MODE_TURBO_G: mask |= IFM_IEEE80211_11G; break; } @@ -870,33 +1049,3 @@ ieee80211_media2rate(int mword) ieeerates[IFM_SUBTYPE(mword)] : 0; #undef N } - -/* - * Module glue. - * - * NB: the module name is "wlan" for compatibility with NetBSD. - */ - -static int -ieee80211_modevent(module_t mod, int type, void *unused) -{ - switch (type) { - case MOD_LOAD: - if (bootverbose) - printf("wlan: <802.11 Link Layer>\n"); - return 0; - case MOD_UNLOAD: - case MOD_SHUTDOWN: - return 0; - } - return EINVAL; -} - -static moduledata_t ieee80211_mod = { - "wlan", - ieee80211_modevent, - 0 -}; -DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan, 1); -MODULE_DEPEND(wlan, crypto, 1, 1, 1); diff --git a/sys/netproto/802_11/wlan/ieee80211_crypto.c b/sys/netproto/802_11/wlan/ieee80211_crypto.c index 329e958664..9b4bddbb8a 100644 --- a/sys/netproto/802_11/wlan/ieee80211_crypto.c +++ b/sys/netproto/802_11/wlan/ieee80211_crypto.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,294 +29,579 @@ * (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: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.2 2005/06/08 23:29:29 hsu Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.10.2.2 2005/09/03 22:40:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.3 2006/05/18 13:51:46 sephe Exp $ */ -#include "opt_inet.h" - +/* + * IEEE 802.11 generic crypto support. + */ #include -#include #include -#include -#include + #include -#include -#include -#include -#include -#include -#include - -#include - + #include -#include -#include #include -#include -#include +#include +#include /* XXX ETHER_HDR_LEN */ #include -#include +/* + * Table of registered cipher modules. + */ +static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX]; + +static int _ieee80211_crypto_delkey(struct ieee80211com *, + struct ieee80211_key *); + +/* + * Default "null" key management routines. + */ +static int +null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + if (!(&ic->ic_nw_keys[0] <= k && + k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) { + /* + * Not in the global key table, the driver should handle this + * by allocating a slot in the h/w key table/cache. In + * lieu of that return key slot 0 for any unicast key + * request. We disallow the request if this is a group key. + * This default policy does the right thing for legacy hardware + * with a 4 key table. It also handles devices that pass + * packets through untouched when marked with the WEP bit + * and key index 0. + */ + if (k->wk_flags & IEEE80211_KEY_GROUP) + return 0; + *keyix = 0; /* NB: use key index 0 for ucast key */ + } else { + *keyix = k - ic->ic_nw_keys; + } + *rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */ + return 1; +} -#ifdef INET -#include -#include -#endif +static int +null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + return 1; +} + +static int +null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return 1; +} + +static void null_key_update(struct ieee80211com *ic) +{ +} + +/* + * Write-arounds for common operations. + */ +static __inline void +cipher_detach(struct ieee80211_key *key) +{ + key->wk_cipher->ic_detach(key); +} + +static __inline void * +cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key) +{ + return key->wk_cipher->ic_attach(ic, key); +} + +/* + * Wrappers for driver key management methods. + */ +static __inline int +dev_key_alloc(struct ieee80211com *ic, + const struct ieee80211_key *key, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix); +} -#include -#define arc4_ctxlen() sizeof (struct rc4_state) -#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l) -#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l) +static __inline int +dev_key_delete(struct ieee80211com *ic, + const struct ieee80211_key *key) +{ + return ic->ic_crypto.cs_key_delete(ic, key); +} -static void ieee80211_crc_init(void); -static uint32_t ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len); +static __inline int +dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return ic->ic_crypto.cs_key_set(ic, key, mac); +} +/* + * Setup crypto support. + */ void -ieee80211_crypto_attach(struct ifnet *ifp) +ieee80211_crypto_attach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211_crypto_state *cs = &ic->ic_crypto; + int i; + /* NB: we assume everything is pre-zero'd */ + cs->cs_def_txkey = IEEE80211_KEYIX_NONE; + cs->cs_max_keyix = IEEE80211_WEP_NKID; + ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none; + for (i = 0; i < IEEE80211_WEP_NKID; i++) + ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i], + IEEE80211_KEYIX_NONE); /* - * Setup crypto support. + * Initialize the driver key support routines to noop entries. + * This is useful especially for the cipher test modules. */ - ieee80211_crc_init(); - ic->ic_iv = arc4random(); + cs->cs_key_alloc = null_key_alloc; + cs->cs_key_set = null_key_set; + cs->cs_key_delete = null_key_delete; + cs->cs_key_update_begin = null_key_update; + cs->cs_key_update_end = null_key_update; } +/* + * Teardown crypto support. + */ void -ieee80211_crypto_detach(struct ifnet *ifp) +ieee80211_crypto_detach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + ieee80211_crypto_delglobalkeys(ic); +} - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; +/* + * Register a crypto cipher module. + */ +void +ieee80211_crypto_register(const struct ieee80211_cipher *cip) +{ + if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { + printf("%s: cipher %s has an invalid cipher index %u\n", + __func__, cip->ic_name, cip->ic_cipher); + return; } + if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { + printf("%s: cipher %s registered with a different template\n", + __func__, cip->ic_name); + return; + } + ciphers[cip->ic_cipher] = cip; } -struct mbuf * -ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag) +/* + * Unregister a crypto cipher module. + */ +void +ieee80211_crypto_unregister(const struct ieee80211_cipher *cip) { - struct ieee80211com *ic = (void *)ifp; - struct mbuf *m, *n, *n0; - struct ieee80211_frame *wh; - int i, left, len, moff, noff, kid; - uint32_t iv, crc; - uint8_t *ivp; - void *ctx; - uint8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; - uint8_t crcbuf[IEEE80211_WEP_CRCLEN]; - - n0 = NULL; - if ((ctx = ic->ic_wep_ctx) == NULL) { - ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT | M_ZERO); /* XXX */ - if (ctx == NULL) { - ic->ic_stats.is_crypto_nomem++; - goto fail; - } - ic->ic_wep_ctx = ctx; + if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { + printf("%s: cipher %s has an invalid cipher index %u\n", + __func__, cip->ic_name, cip->ic_cipher); + return; } - m = m0; - left = m->m_pkthdr.len; - MGETHDR(n, MB_DONTWAIT, m->m_type); - n0 = n; - if (n == NULL) { - if (txflag) - ic->ic_stats.is_tx_nombuf++; - else - ic->ic_stats.is_rx_nombuf++; - goto fail; + if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { + printf("%s: cipher %s registered with a different template\n", + __func__, cip->ic_name); + return; } - M_MOVE_PKTHDR(n, m); - len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; - if (txflag) { - n->m_pkthdr.len += len; - } else { - n->m_pkthdr.len -= len; - left -= len; - } - n->m_len = MHLEN; - if (n->m_pkthdr.len >= MINCLSIZE) { - MCLGET(n, MB_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; + /* NB: don't complain about not being registered */ + /* XXX disallow if references */ + ciphers[cip->ic_cipher] = NULL; +} + +int +ieee80211_crypto_available(u_int cipher) +{ + return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL; +} + +/* XXX well-known names! */ +static const char *cipher_modnames[] = { + "wlan_wep", /* IEEE80211_CIPHER_WEP */ + "wlan_tkip", /* IEEE80211_CIPHER_TKIP */ + "wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */ + "wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */ + "wlan_ckip", /* IEEE80211_CIPHER_CKIP */ +}; + +/* + * Establish a relationship between the specified key and cipher + * and, if necessary, allocate a hardware index from the driver. + * Note that when a fixed key index is required it must be specified + * and we blindly assign it w/o consulting the driver (XXX). + * + * This must be the first call applied to a key; all the other key + * routines assume wk_cipher is setup. + * + * Locking must be handled by the caller using: + * ieee80211_key_update_begin(ic); + * ieee80211_key_update_end(ic); + */ +int +ieee80211_crypto_newkey(struct ieee80211com *ic, + int cipher, int flags, struct ieee80211_key *key) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + const struct ieee80211_cipher *cip; + ieee80211_keyix keyix, rxkeyix; + void *keyctx; + int oflags; + + /* + * Validate cipher and set reference to cipher routines. + */ + if (cipher >= IEEE80211_CIPHER_MAX) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: invalid cipher %u\n", __func__, cipher); + ic->ic_stats.is_crypto_badcipher++; + return 0; } - len = sizeof(struct ieee80211_frame); - memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len); - wh = mtod(n, struct ieee80211_frame *); - left -= len; - moff = len; - noff = len; - if (txflag) { - kid = ic->ic_wep_txkey; - wh->i_fc[1] |= IEEE80211_FC1_WEP; - iv = ic->ic_iv; + cip = ciphers[cipher]; + if (cip == NULL) { /* - * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: - * (B, 255, N) with 3 <= B < 8 + * Auto-load cipher module if we have a well-known name + * for it. It might be better to use string names rather + * than numbers and craft a module name based on the cipher + * name; e.g. wlan_cipher_. */ - if (iv >= 0x03ff00 && - (iv & 0xf8ff00) == 0x00ff00) - iv += 0x000100; - ic->ic_iv = iv + 1; - /* put iv in little endian to prepare 802.11i */ - ivp = mtod(n, uint8_t *) + noff; - for (i = 0; i < IEEE80211_WEP_IVLEN; i++) { - ivp[i] = iv & 0xff; - iv >>= 8; + if (cipher < N(cipher_modnames)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unregistered cipher %u, load module %s\n", + __func__, cipher, cipher_modnames[cipher]); + ieee80211_load_module(cipher_modnames[cipher]); + /* + * If cipher module loaded it should immediately + * call ieee80211_crypto_register which will fill + * in the entry in the ciphers array. + */ + cip = ciphers[cipher]; } - ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */ - noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } else { - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - ivp = mtod(m, uint8_t *) + moff; - kid = ivp[IEEE80211_WEP_IVLEN] >> 6; - moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } - memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN); - memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key, - ic->ic_nw_keys[kid].wk_len); - arc4_setkey(ctx, keybuf, - IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len); - - /* encrypt with calculating CRC */ - crc = ~0; - while (left > 0) { - len = m->m_len - moff; - if (len == 0) { - m = m->m_next; - moff = 0; - continue; + if (cip == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unable to load cipher %u, module %s\n", + __func__, cipher, + cipher < N(cipher_modnames) ? + cipher_modnames[cipher] : ""); + ic->ic_stats.is_crypto_nocipher++; + return 0; } - if (len > n->m_len - noff) { - len = n->m_len - noff; - if (len == 0) { - MGET(n->m_next, MB_DONTWAIT, n->m_type); - if (n->m_next == NULL) { - if (txflag) - ic->ic_stats.is_tx_nombuf++; - else - ic->ic_stats.is_rx_nombuf++; - goto fail; - } - n = n->m_next; - n->m_len = MLEN; - if (left >= MINCLSIZE) { - MCLGET(n, MB_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - noff = 0; - continue; - } + } + + oflags = key->wk_flags; + flags &= IEEE80211_KEY_COMMON; + /* + * If the hardware does not support the cipher then + * fallback to a host-based implementation. + */ + if ((ic->ic_caps & (1<ic_name); + flags |= IEEE80211_KEY_SWCRYPT; + } + /* + * Hardware TKIP with software MIC is an important + * combination; we handle it by flagging each key, + * the cipher modules honor it. + */ + if (cipher == IEEE80211_CIPHER_TKIP && + (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: no h/w support for TKIP MIC, falling back to s/w\n", + __func__); + flags |= IEEE80211_KEY_SWMIC; + } + + /* + * Bind cipher to key instance. Note we do this + * after checking the device capabilities so the + * cipher module can optimize space usage based on + * whether or not it needs to do the cipher work. + */ + if (key->wk_cipher != cip || key->wk_flags != flags) { +again: + /* + * Fillin the flags so cipher modules can see s/w + * crypto requirements and potentially allocate + * different state and/or attach different method + * pointers. + * + * XXX this is not right when s/w crypto fallback + * fails and we try to restore previous state. + */ + key->wk_flags = flags; + keyctx = cip->ic_attach(ic, key); /* attach new cipher */ + if (keyctx == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unable to attach cipher %s\n", + __func__, cip->ic_name); + key->wk_flags = oflags; /* restore old flags */ + ic->ic_stats.is_crypto_attachfail++; + return 0; } - if (len > left) - len = left; - arc4_encrypt(ctx, mtod(n, caddr_t) + noff, - mtod(m, caddr_t) + moff, len); - if (txflag) - crc = ieee80211_crc_update(crc, - mtod(m, uint8_t *) + moff, len); - else - crc = ieee80211_crc_update(crc, - mtod(n, uint8_t *) + noff, len); - left -= len; - moff += len; - noff += len; + cipher_detach(key); /* detach old cipher */ + key->wk_cipher = cip; /* XXX refcnt? */ + key->wk_private = keyctx; } - crc = ~crc; - if (txflag) { - *(uint32_t *)crcbuf = htole32(crc); - if (n->m_len >= noff + sizeof(crcbuf)) - n->m_len = noff + sizeof(crcbuf); - else { - n->m_len = noff; - MGET(n->m_next, MB_DONTWAIT, n->m_type); - if (n->m_next == NULL) { - ic->ic_stats.is_tx_nombuf++; - goto fail; + /* + * Commit to requested usage so driver can see the flags. + */ + key->wk_flags = flags; + + /* + * Ask the driver for a key index if we don't have one. + * Note that entries in the global key table always have + * an index; this means it's safe to call this routine + * for these entries just to setup the reference to the + * cipher template. Note also that when using software + * crypto we also call the driver to give us a key index. + */ + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + if (!dev_key_alloc(ic, key, &keyix, &rxkeyix)) { + /* + * Driver has no room; fallback to doing crypto + * in the host. We change the flags and start the + * procedure over. If we get back here then there's + * no hope and we bail. Note that this can leave + * the key in a inconsistent state if the caller + * continues to use it. + */ + if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { + ic->ic_stats.is_crypto_swfallback++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: no h/w resources for cipher %s, " + "falling back to s/w\n", __func__, + cip->ic_name); + oflags = key->wk_flags; + flags |= IEEE80211_KEY_SWCRYPT; + if (cipher == IEEE80211_CIPHER_TKIP) + flags |= IEEE80211_KEY_SWMIC; + goto again; } - n = n->m_next; - n->m_len = sizeof(crcbuf); - noff = 0; + ic->ic_stats.is_crypto_keyfail++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unable to setup cipher %s\n", + __func__, cip->ic_name); + return 0; } - arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf, - sizeof(crcbuf)); - } else { - n->m_len = noff; - for (noff = 0; noff < sizeof(crcbuf); noff += len) { - len = sizeof(crcbuf) - noff; - if (len > m->m_len - moff) - len = m->m_len - moff; - if (len > 0) - arc4_encrypt(ctx, crcbuf + noff, - mtod(m, caddr_t) + moff, len); - m = m->m_next; - moff = 0; - } - if (crc != le32toh(*(uint32_t *)crcbuf)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - if_printf(ifp, "decrypt CRC error\n"); - if (ieee80211_debug > 1) - ieee80211_dump_pkt(n0->m_data, - n0->m_len, -1, -1); - } -#endif - ic->ic_stats.is_rx_decryptcrc++; - goto fail; + key->wk_keyix = keyix; + key->wk_rxkeyix = rxkeyix; + } + return 1; +#undef N +} + +/* + * Remove the key (no locking, for internal use). + */ +static int +_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +{ + ieee80211_keyix keyix; + + KASSERT(key->wk_cipher != NULL, ("No cipher!")); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n", + __func__, key->wk_cipher->ic_name, + key->wk_keyix, key->wk_flags, + key->wk_keyrsc, key->wk_keytsc, key->wk_keylen); + + keyix = key->wk_keyix; + if (keyix != IEEE80211_KEYIX_NONE) { + /* + * Remove hardware entry. + */ + /* XXX key cache */ + if (!dev_key_delete(ic, key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: driver did not delete key index %u\n", + __func__, keyix); + ic->ic_stats.is_crypto_delkey++; + /* XXX recovery? */ } } - m_freem(m0); - return n0; + cipher_detach(key); + memset(key, 0, sizeof(*key)); + ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE); + return 1; +} + +/* + * Remove the specified key. + */ +int +ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +{ + int status; - fail: - m_freem(m0); - m_freem(n0); - return NULL; + ieee80211_key_update_begin(ic); + status = _ieee80211_crypto_delkey(ic, key); + ieee80211_key_update_end(ic); + return status; } /* - * CRC 32 -- routine from RFC 2083 + * Clear the global key table. */ +void +ieee80211_crypto_delglobalkeys(struct ieee80211com *ic) +{ + int i; -/* Table of CRCs of all 8-bit messages */ -static uint32_t ieee80211_crc_table[256]; + ieee80211_key_update_begin(ic); + for (i = 0; i < IEEE80211_WEP_NKID; i++) + _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]); + ieee80211_key_update_end(ic); +} -/* Make the table for a fast CRC. */ -static void -ieee80211_crc_init(void) +/* + * Set the contents of the specified key. + * + * Locking must be handled by the caller using: + * ieee80211_key_update_begin(ic); + * ieee80211_key_update_end(ic); + */ +int +ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { - uint32_t c; - int n, k; - - for (n = 0; n < 256; n++) { - c = (uint32_t)n; - for (k = 0; k < 8; k++) { - if (c & 1) - c = 0xedb88320UL ^ (c >> 1); - else - c = c >> 1; - } - ieee80211_crc_table[n] = c; + const struct ieee80211_cipher *cip = key->wk_cipher; + + KASSERT(cip != NULL, ("No cipher!")); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: %s keyix %u flags 0x%x mac %6D rsc %ju tsc %ju len %u\n", + __func__, cip->ic_name, key->wk_keyix, + key->wk_flags, macaddr, ":", + key->wk_keyrsc, key->wk_keytsc, key->wk_keylen); + + /* + * Give cipher a chance to validate key contents. + * XXX should happen before modifying state. + */ + if (!cip->ic_setkey(key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: cipher %s rejected key index %u len %u flags 0x%x\n", + __func__, cip->ic_name, key->wk_keyix, + key->wk_keylen, key->wk_flags); + ic->ic_stats.is_crypto_setkey_cipher++; + return 0; } + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + /* XXX nothing allocated, should not happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: no key index; should not happen!\n", __func__); + ic->ic_stats.is_crypto_setkey_nokey++; + return 0; + } + return dev_key_set(ic, key, macaddr); } /* - * Update a running CRC with the bytes buf[0..len-1]--the CRC - * should be initialized to all 1's, and the transmitted value - * is the 1's complement of the final running CRC + * Add privacy headers appropriate for the specified key. */ +struct ieee80211_key * +ieee80211_crypto_encap(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211_key *k; + struct ieee80211_frame *wh; + const struct ieee80211_cipher *cip; + uint8_t keyid; -static uint32_t -ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len) + /* + * Multicast traffic always uses the multicast key. + * Otherwise if a unicast key is set we use that and + * it is always key index 0. When no unicast key is + * set we fall back to the default transmit key. + */ + wh = mtod(m, struct ieee80211_frame *); + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] no default transmit key (%s) deftxkey %u\n", + wh->i_addr1, ":", __func__, + ic->ic_def_txkey); + ic->ic_stats.is_tx_nodefkey++; + return NULL; + } + keyid = ic->ic_def_txkey; + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + } else { + keyid = 0; + k = &ni->ni_ucastkey; + } + cip = k->wk_cipher; + return (cip->ic_encap(k, m, keyid<<6) ? k : NULL); +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame that has the WEP/Privacy bit set. + */ +struct ieee80211_key * +ieee80211_crypto_decap(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m, int hdrlen) { - uint8_t *endbuf; +#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) +#define IEEE80211_WEP_MINLEN \ + (sizeof(struct ieee80211_frame) + \ + IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN) + struct ieee80211_key *k; + struct ieee80211_frame *wh; + const struct ieee80211_cipher *cip; + const uint8_t *ivp; + uint8_t keyid; + + /* NB: this minimum size data frame could be bigger */ + if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: WEP data frame too short, len %u\n", + __func__, m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; /* XXX need unique stat? */ + return NULL; + } + + /* + * Locate the key. If unicast and there is no unicast + * key then we fall back to the key id in the header. + * This assumes unicast keys are only configured when + * the key id in the header is meaningless (typically 0). + */ + wh = mtod(m, struct ieee80211_frame *); + ivp = mtod(m, const uint8_t *) + hdrlen; /* XXX contig */ + keyid = ivp[IEEE80211_WEP_IVLEN]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) + k = &ic->ic_nw_keys[keyid >> 6]; + else + k = &ni->ni_ucastkey; + + /* + * Insure crypto header is contiguous for all decap work. + */ + cip = k->wk_cipher; + if (m->m_len < hdrlen + cip->ic_header && + (m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] unable to pullup %s header\n", + wh->i_addr2, ":", cip->ic_name); + ic->ic_stats.is_rx_wepfail++; /* XXX */ + return 0; + } - for (endbuf = buf + len; buf < endbuf; buf++) - crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); - return crc; + return (cip->ic_decap(k, m, hdrlen) ? k : NULL); +#undef IEEE80211_WEP_MINLEN +#undef IEEE80211_WEP_HDRLEN } diff --git a/sys/netproto/802_11/wlan/ieee80211_crypto_none.c b/sys/netproto/802_11/wlan/ieee80211_crypto_none.c new file mode 100644 index 0000000000..7b57d06dda --- /dev/null +++ b/sys/netproto/802_11/wlan/ieee80211_crypto_none.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2002-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/ieee80211_crypto_none.c,v 1.5 2005/06/10 16:11:24 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto_none.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * IEEE 802.11 NULL crypto support. + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static void *none_attach(struct ieee80211com *, struct ieee80211_key *); +static void none_detach(struct ieee80211_key *); +static int none_setkey(struct ieee80211_key *); +static int none_encap(struct ieee80211_key *, struct mbuf *, uint8_t); +static int none_decap(struct ieee80211_key *, struct mbuf *, int); +static int none_enmic(struct ieee80211_key *, struct mbuf *, int); +static int none_demic(struct ieee80211_key *, struct mbuf *, int); + +const struct ieee80211_cipher ieee80211_cipher_none = { + .ic_name = "NONE", + .ic_cipher = IEEE80211_CIPHER_NONE, + .ic_header = 0, + .ic_trailer = 0, + .ic_miclen = 0, + .ic_attach = none_attach, + .ic_detach = none_detach, + .ic_setkey = none_setkey, + .ic_encap = none_encap, + .ic_decap = none_decap, + .ic_enmic = none_enmic, + .ic_demic = none_demic, +}; + +static void * +none_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + return ic; /* for diagnostics+stats */ +} + +static void +none_detach(struct ieee80211_key *k) +{ + (void) k; +} + +static int +none_setkey(struct ieee80211_key *k) +{ + (void) k; + return 1; +} + +static int +none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) +{ + struct ieee80211com *ic = k->wk_private; +#ifdef IEEE80211_DEBUG + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); +#endif + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] key id %u is not set (encap)\n", + wh->i_addr1, ":", keyid>>6); + ic->ic_stats.is_tx_badcipher++; + return 0; +} + +static int +none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) +{ + struct ieee80211com *ic = k->wk_private; +#ifdef IEEE80211_DEBUG + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + const uint8_t *ivp = (const uint8_t *)&wh[1]; +#endif + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + /* XXX useful to know dst too */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] key id %u is not set (decap)\n", + wh->i_addr2, ":", ivp[IEEE80211_WEP_IVLEN] >> 6); + ic->ic_stats.is_rx_badkeyid++; + return 0; +} + +static int +none_enmic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + struct ieee80211com *ic = k->wk_private; + + ic->ic_stats.is_tx_badcipher++; + return 0; +} + +static int +none_demic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + struct ieee80211com *ic = k->wk_private; + + ic->ic_stats.is_rx_badkeyid++; + return 0; +} diff --git a/sys/netproto/802_11/wlan/ieee80211_dragonfly.c b/sys/netproto/802_11/wlan/ieee80211_dragonfly.c new file mode 100644 index 0000000000..df32af3373 --- /dev/null +++ b/sys/netproto/802_11/wlan/ieee80211_dragonfly.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2003-2005 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. + * 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 ``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: src/sys/net80211/ieee80211_freebsd.c,v 1.7.2.2 2005/12/22 19:22:51 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_dragonfly.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * IEEE 802.11 support (DragonFlyBSD-specific code) + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define if_link_state_change(ifp, state) ((void)0) + +SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters"); + +#ifdef IEEE80211_DEBUG +int ieee80211_debug = 0; +SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, + 0, "debugging printfs"); +#endif + +static int +ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) +{ + int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT; + int error; + + error = sysctl_handle_int(oidp, &inact, 0, req); + if (error || !req->newptr) + return error; + *(int *)arg1 = inact / IEEE80211_INACT_WAIT; + return 0; +} + +static int +ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211com *ic = arg1; + const char *name = ic->ic_ifp->if_xname; + + return SYSCTL_OUT(req, name, strlen(name)); +} + +void +ieee80211_sysctl_attach(struct ieee80211com *ic) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *oid; + char num[14]; /* sufficient for 32 bits */ + + ctx = malloc(sizeof(struct sysctl_ctx_list), M_DEVBUF, + M_WAITOK | M_ZERO); + sysctl_ctx_init(ctx); + + snprintf(num, sizeof(num), "%u", ic->ic_vap); + oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), + OID_AUTO, num, CTLFLAG_RD, NULL, ""); + if (oid == NULL) + return; + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A", + "parent device"); +#ifdef IEEE80211_DEBUG + ic->ic_debug = ieee80211_debug; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "debug", CTLFLAG_RW, &ic->ic_debug, 0, + "control debugging printfs"); +#endif + /* XXX inherit from tunables */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0, + ieee80211_sysctl_inact, "I", + "station inactivity timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0, + ieee80211_sysctl_inact, "I", + "station inactivity probe timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0, + ieee80211_sysctl_inact, "I", + "station authentication timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0, + ieee80211_sysctl_inact, "I", + "station initial state timeout (sec)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "driver_caps", CTLFLAG_RW, &ic->ic_caps, 0, + "driver capabilities"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0, + "consecutive beacon misses before scanning"); + + ic->ic_sysctl = ctx; +} + +void +ieee80211_sysctl_detach(struct ieee80211com *ic) +{ + if (ic->ic_sysctl != NULL) { + sysctl_ctx_free(ic->ic_sysctl); + free(ic->ic_sysctl, M_DEVBUF); + ic->ic_sysctl = NULL; + } +} + +int +ieee80211_node_dectestref(struct ieee80211_node *ni) +{ + /* XXX need equivalent of atomic_dec_and_test */ + atomic_subtract_int(&ni->ni_refcnt, 1); + return atomic_cmpset_int(&ni->ni_refcnt, 0, 1); +} + +/* + * Allocate and setup a management frame of the specified + * size. We return the mbuf and a pointer to the start + * of the contiguous data area that's been reserved based + * on the packet length. The data area is forced to 32-bit + * alignment and the buffer length to a multiple of 4 bytes. + * This is done mainly so beacon frames (that require this) + * can use this interface too. + */ +struct mbuf * +ieee80211_getmgtframe(uint8_t **frm, u_int pktlen) +{ + struct mbuf *m; + u_int len; + + /* + * NB: we know the mbuf routines will align the data area + * so we don't need to do anything special. + */ + /* XXX 4-address frame? */ + len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); + KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len)); + if (len < MINCLSIZE) { + m = m_gethdr(M_NOWAIT, MT_HEADER); + /* + * Align the data in case additional headers are added. + * This should only happen when a WEP header is added + * which only happens for shared key authentication mgt + * frames which all fit in MHLEN. + */ + if (m != NULL) + MH_ALIGN(m, len); + } else + m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR); + if (m != NULL) { + m->m_data += sizeof(struct ieee80211_frame); + *frm = m->m_data; + } + return m; +} + +#include + +void +get_random_bytes(void *p, size_t n) +{ + uint8_t *dp = p; + + while (n > 0) { + uint32_t v = arc4random(); + size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n; + + bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n); + dp += sizeof(uint32_t), n -= nb; + } +} + +void +ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, + int newassoc) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_join_event iev; + + memset(&iev, 0, sizeof(iev)); + if (ni == ic->ic_bss) { + IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid); + rt_ieee80211msg(ifp, newassoc ? + RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, + &iev, sizeof(iev)); + if_link_state_change(ifp, LINK_STATE_UP); + } else { + IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); + rt_ieee80211msg(ifp, newassoc ? + RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, + &iev, sizeof(iev)); + } +} + +void +ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_leave_event iev; + + if (ni == ic->ic_bss) { + rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); + if_link_state_change(ifp, LINK_STATE_DOWN); + } else { + /* fire off wireless event station leaving */ + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); + rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev)); + } +} + +void +ieee80211_notify_scan_done(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: notify scan done\n", ic->ic_ifp->if_xname); + + /* dispatch wireless event indicating scan completed */ + rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); +} + +void +ieee80211_notify_replay_failure(struct ieee80211com *ic, + const struct ieee80211_frame *wh, const struct ieee80211_key *k, + uint64_t rsc) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] %s replay detected \n", + wh->i_addr2, ":", k->wk_cipher->ic_name, + (intmax_t) rsc, (intmax_t) k->wk_keyrsc, + k->wk_keyix, k->wk_rxkeyix); + + if (ifp != NULL) { /* NB: for cipher test modules */ + struct ieee80211_replay_event iev; + + IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); + IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); + iev.iev_cipher = k->wk_cipher->ic_cipher; + if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE) + iev.iev_keyix = k->wk_rxkeyix; + else + iev.iev_keyix = k->wk_keyix; + iev.iev_keyrsc = k->wk_keyrsc; + iev.iev_rsc = rsc; + rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); + } +} + +void +ieee80211_notify_michael_failure(struct ieee80211com *ic, + const struct ieee80211_frame *wh, u_int keyix) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] michael MIC verification failed \n", + wh->i_addr2, ":", keyix); + ic->ic_stats.is_rx_tkipmic++; + + if (ifp != NULL) { /* NB: for cipher test modules */ + struct ieee80211_michael_event iev; + + IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); + IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); + iev.iev_cipher = IEEE80211_CIPHER_TKIP; + iev.iev_keyix = keyix; + rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev)); + } +} + +void +ieee80211_load_module(const char *modname) +{ +#ifdef notyet + struct thread *td = curthread; + + if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) { + crit_enter(); /* NB: need BGL here */ + linker_load_module(modname, NULL, NULL, NULL, NULL); + crit_exit(); + } +#else + printf("%s: load the %s module by hand for now.\n", __func__, modname); +#endif +} + +/* + * Append the specified data to the indicated mbuf chain, + * Extend the mbuf chain if the new data does not fit in + * existing space. + * + * Return 1 if able to complete the job; otherwise 0. + */ +int +ieee80211_mbuf_append(struct mbuf *m0, int len, const uint8_t *cp) +{ + struct mbuf *m, *n; + int remainder, space; + + for (m = m0; m->m_next != NULL; m = m->m_next) + ; + remainder = len; + space = M_TRAILINGSPACE(m); + if (space > 0) { + /* + * Copy into available space. + */ + if (space > remainder) + space = remainder; + bcopy(cp, mtod(m, caddr_t) + m->m_len, space); + m->m_len += space; + cp += space, remainder -= space; + } + while (remainder > 0) { + /* + * Allocate a new mbuf; could check space + * and allocate a cluster instead. + */ + n = m_get(MB_DONTWAIT, m->m_type); + if (n == NULL) + break; + n->m_len = min(MLEN, remainder); + bcopy(cp, mtod(n, caddr_t), n->m_len); + cp += n->m_len, remainder -= n->m_len; + m->m_next = n; + m = n; + } + if (m0->m_flags & M_PKTHDR) + m0->m_pkthdr.len += len - remainder; + return (remainder == 0); +} + +/* + * Create a writable copy of the mbuf chain. While doing this + * we compact the chain with a goal of producing a chain with + * at most two mbufs. The second mbuf in this chain is likely + * to be a cluster. The primary purpose of this work is to create + * a writable packet for encryption, compression, etc. The + * secondary goal is to linearize the data so the data can be + * passed to crypto hardware in the most efficient manner possible. + */ +struct mbuf * +ieee80211_mbuf_clone(struct mbuf *m0, int how) +{ + struct mbuf *m, *mprev; + struct mbuf *n, *mfirst, *mlast; + int len, off; + + mprev = NULL; + for (m = m0; m != NULL; m = mprev->m_next) { + /* + * Regular mbufs are ignored unless there's a cluster + * in front of it that we can use to coalesce. We do + * the latter mainly so later clusters can be coalesced + * also w/o having to handle them specially (i.e. convert + * mbuf+cluster -> cluster). This optimization is heavily + * influenced by the assumption that we're running over + * Ethernet where MCLBYTES is large enough that the max + * packet size will permit lots of coalescing into a + * single cluster. This in turn permits efficient + * crypto operations, especially when using hardware. + */ + if ((m->m_flags & M_EXT) == 0) { + if (mprev && (mprev->m_flags & M_EXT) && + m->m_len <= M_TRAILINGSPACE(mprev)) { + /* XXX: this ignores mbuf types */ + memcpy(mtod(mprev, caddr_t) + mprev->m_len, + mtod(m, caddr_t), m->m_len); + mprev->m_len += m->m_len; + mprev->m_next = m->m_next; /* unlink from chain */ + m_free(m); /* reclaim mbuf */ + } else { + mprev = m; + } + continue; + } + /* + * Writable mbufs are left alone (for now). + */ + if (M_WRITABLE(m)) { + mprev = m; + continue; + } + + /* + * Not writable, replace with a copy or coalesce with + * the previous mbuf if possible (since we have to copy + * it anyway, we try to reduce the number of mbufs and + * clusters so that future work is easier). + */ + KASSERT(m->m_flags & M_EXT, ("m_flags 0x%x", m->m_flags)); + /* NB: we only coalesce into a cluster or larger */ + if (mprev != NULL && (mprev->m_flags & M_EXT) && + m->m_len <= M_TRAILINGSPACE(mprev)) { + /* XXX: this ignores mbuf types */ + memcpy(mtod(mprev, caddr_t) + mprev->m_len, + mtod(m, caddr_t), m->m_len); + mprev->m_len += m->m_len; + mprev->m_next = m->m_next; /* unlink from chain */ + m_free(m); /* reclaim mbuf */ + continue; + } + + /* + * Allocate new space to hold the copy... + */ + /* XXX why can M_PKTHDR be set past the first mbuf? */ + if (mprev == NULL && (m->m_flags & M_PKTHDR)) { + /* + * NB: if a packet header is present we must + * allocate the mbuf separately from any cluster + * because M_MOVE_PKTHDR will smash the data + * pointer and drop the M_EXT marker. + */ + MGETHDR(n, how, m->m_type); + if (n == NULL) { + m_freem(m0); + return (NULL); + } + M_MOVE_PKTHDR(n, m); + MCLGET(n, how); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + m_freem(m0); + return (NULL); + } + } else { + n = m_getcl(how, m->m_type, m->m_flags); + if (n == NULL) { + m_freem(m0); + return (NULL); + } + } + /* + * ... and copy the data. We deal with jumbo mbufs + * (i.e. m_len > MCLBYTES) by splitting them into + * clusters. We could just malloc a buffer and make + * it external but too many device drivers don't know + * how to break up the non-contiguous memory when + * doing DMA. + */ + len = m->m_len; + off = 0; + mfirst = n; + mlast = NULL; + for (;;) { + int cc = min(len, MCLBYTES); + memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + off, cc); + n->m_len = cc; + if (mlast != NULL) + mlast->m_next = n; + mlast = n; + + len -= cc; + if (len <= 0) + break; + off += cc; + + n = m_getcl(how, m->m_type, m->m_flags); + if (n == NULL) { + m_freem(mfirst); + m_freem(m0); + return (NULL); + } + } + n->m_next = m->m_next; + if (mprev == NULL) + m0 = mfirst; /* new head of chain */ + else + mprev->m_next = mfirst; /* replace old mbuf */ + m_free(m); /* release old mbuf */ + mprev = mfirst; + } + return (m0); +} + +/* + * Module glue. + * + * NB: the module name is "wlan" for compatibility with NetBSD. + */ +static int +wlan_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("wlan: <802.11 Link Layer>\n"); + return 0; + case MOD_UNLOAD: + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_mod = { + "wlan", + wlan_modevent, + 0 +}; +DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan, 1); +MODULE_DEPEND(wlan, crypto, 1, 1, 1); diff --git a/sys/netproto/802_11/wlan/ieee80211_input.c b/sys/netproto/802_11/wlan/ieee80211_input.c index 7c8307b091..0d06ddfe23 100644 --- a/sys/netproto/802_11/wlan/ieee80211_input.c +++ b/sys/netproto/802_11/wlan/ieee80211_input.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,42 +29,97 @@ * (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: src/sys/net80211/ieee80211_input.c,v 1.21 2004/06/13 17:29:09 mlaier Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_input.c,v 1.3 2005/11/28 17:13:46 dillon Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.62.2.12 2006/03/14 23:24:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_input.c,v 1.4 2006/05/18 13:51:46 sephe Exp $ */ -#include "opt_inet.h" - #include -#include +#include #include #include +#include #include +#include + #include -#include -#include -#include -#include -#include -#include -#include - #include -#include -#include #include +#include +#include #include #include +#include #include #include -#ifdef INET -#include -#include -#endif +#ifdef IEEE80211_DEBUG +#include + +/* + * Decide if a received management frame should be + * printed when debugging is enabled. This filters some + * of the less interesting frames that come frequently + * (e.g. beacons). + */ +static __inline int +doprint(struct ieee80211com *ic, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return (ic->ic_flags & IEEE80211_F_SCAN); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return (ic->ic_opmode == IEEE80211_M_IBSS); + } + return 1; +} + +/* + * Emit a debug message about discarding a frame or information + * element. One format is for extracting the mac address from + * the frame header; the other is for when a header is not + * available or otherwise appropriate. + */ +#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \ + if ((_ic)->ic_debug & (_m)) \ + ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\ +} while (0) +#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \ + if ((_ic)->ic_debug & (_m)) \ + ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\ +} while (0) +#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \ + if ((_ic)->ic_debug & (_m)) \ + ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\ +} while (0) + +static const uint8_t *ieee80211_getbssid(struct ieee80211com *, + const struct ieee80211_frame *); +static void ieee80211_discard_frame(struct ieee80211com *, + const struct ieee80211_frame *, const char *type, const char *fmt, ...); +static void ieee80211_discard_ie(struct ieee80211com *, + const struct ieee80211_frame *, const char *type, const char *fmt, ...); +static void ieee80211_discard_mac(struct ieee80211com *, + const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, + const char *fmt, ...); +#else +#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) +#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) +#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) +#endif /* IEEE80211_DEBUG */ + +static struct mbuf *ieee80211_defrag(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *, int); +static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int); +static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *, + const uint8_t *mac, int subtype, int arg); +static void ieee80211_deliver_data(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); +static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); +static void ieee80211_recv_pspoll(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); /* * Process a received frame. The node associated with the sender @@ -76,20 +131,25 @@ * mean ``better signal''. The receive timestamp is currently not used * by the 802.11 layer. */ -void -ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, - int rssi, uint32_t rstamp) +int +ieee80211_input(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int rssi, uint32_t rstamp) { - struct ieee80211com *ic = (void *)ifp; +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_frame *wh; + struct ieee80211_key *key; struct ether_header *eh; - struct mbuf *m1; - int len; + int hdrspace; uint8_t dir, type, subtype; uint8_t *bssid; uint16_t rxseq; + ASSERT_SERIALIZED(ifp->if_serializer); + KASSERT(ni != NULL, ("null node")); + ni->ni_inact = ni->ni_inact_reload; #ifdef M_HASFCS /* trim CRC here so WEP can find its own CRC at the end of packet. */ @@ -98,9 +158,8 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, m->m_flags &= ~M_HASFCS; } #endif - KASSERT(m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min), - ("frame length too short: %u", m->m_pkthdr.len)); + type = -1; /* undefined */ /* * In monitor mode, send everything directly to bpf. * XXX may want to include the CRC @@ -108,38 +167,40 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, if (ic->ic_opmode == IEEE80211_M_MONITOR) goto out; + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; + goto out; + } + /* + * Bit of a cheat here, we use a pointer for a 3-address + * frame format but don't reference fields past outside + * ieee80211_frame_min w/o first validating the data is + * present. + */ wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "receive packet with wrong version: %x\n", - wh->i_fc[0]); + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); ic->ic_stats.is_rx_badversion++; goto err; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - /* - * NB: We are not yet prepared to handle control frames, - * but permitting drivers to send them to us allows - * them to go through bpf tapping at the 802.11 layer. - */ - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { - /* XXX statistic */ - IEEE80211_DPRINTF2(("%s: frame too short, len %u\n", - __func__, m->m_pkthdr.len)); - ic->ic_stats.is_rx_tooshort++; - goto out; /* XXX */ - } - if (ic->ic_state != IEEE80211_S_SCAN) { + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { switch (ic->ic_opmode) { case IEEE80211_M_STA: - if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { + bssid = wh->i_addr2; + if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { /* not interested in */ - IEEE80211_DPRINTF2(("%s: discard frame from " - "bss %6D\n", __func__, - wh->i_addr2, ":")); + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); ic->ic_stats.is_rx_wrongbss++; goto out; } @@ -147,46 +208,106 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: case IEEE80211_M_HOSTAP: - if (dir == IEEE80211_FC1_DIR_NODS) - bssid = wh->i_addr3; - else + if (dir != IEEE80211_FC1_DIR_NODS) + bssid = wh->i_addr1; + else if (type == IEEE80211_FC0_TYPE_CTL) bssid = wh->i_addr1; + else { + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_ANY, ni->ni_macaddr, + NULL, "too short (2): len %u", + m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; + goto out; + } + bssid = wh->i_addr3; + } + if (type != IEEE80211_FC0_TYPE_DATA) + break; + /* + * Data frame, validate the bssid. + */ if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { /* not interested in */ - IEEE80211_DPRINTF2(("%s: discard frame from " - "bss %6D\n", __func__, - bssid, ":")); + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); ic->ic_stats.is_rx_wrongbss++; goto out; } + /* + * For adhoc mode we cons up a node when it doesn't + * exist. This should probably done after an ACL check. + */ + if (ni == ic->ic_bss && + ic->ic_opmode != IEEE80211_M_HOSTAP && + !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { + /* + * Fake up a node for this newly + * discovered member of the IBSS. + */ + ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, + wh->i_addr2); + if (ni == NULL) { + /* NB: stat kept for alloc failure */ + goto err; + } + } break; - case IEEE80211_M_MONITOR: - goto out; default: - /* XXX catch bad values */ - break; + goto out; } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; - rxseq = ni->ni_rxseq; - ni->ni_rxseq = - le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; - /* TODO: fragment */ - if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && - rxseq == ni->ni_rxseq) { - /* duplicate, silently discarded */ - ic->ic_stats.is_rx_dup++; /* XXX per-station stat */ - goto out; + if (HAS_SEQ(type)) { + uint8_t tid; + if (IEEE80211_QOS_HAS_SEQ(wh)) { + tid = ((struct ieee80211_qosframe *)wh)-> + i_qos[0] & IEEE80211_QOS_TID; + if (TID_TO_WME_AC(tid) >= WME_AC_VI) + ic->ic_wme.wme_hipri_traffic++; + tid++; + } else + tid = 0; + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { + /* duplicate, discard */ + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + bssid, "duplicate", + "seqno <%u,%u> fragno <%u,%u> tid %u", + rxseq >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseqs[tid] >> + IEEE80211_SEQ_SEQ_SHIFT, + rxseq & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxseqs[tid] & + IEEE80211_SEQ_FRAG_MASK, + tid); + ic->ic_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + goto out; + } + ni->ni_rxseqs[tid] = rxseq; } - ni->ni_inact = 0; } switch (type) { case IEEE80211_FC0_TYPE_DATA: + hdrspace = ieee80211_hdrspace(ic, wh); + if (m->m_len < hdrspace && + (m = m_pullup(m, hdrspace)) == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "data too short: expecting %u", hdrspace); + ic->ic_stats.is_rx_tooshort++; + goto out; /* XXX */ + } switch (ic->ic_opmode) { case IEEE80211_M_STA: if (dir != IEEE80211_FC1_DIR_FROMDS) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } @@ -199,6 +320,8 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, * It should be silently discarded for * SIMPLEX interface. */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "multicast echo"); ic->ic_stats.is_rx_mcastecho++; goto out; } @@ -206,199 +329,436 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } + /* XXX no power-save support */ break; case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_TODS) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto out; } /* check if source STA is associated */ if (ni == ic->ic_bss) { - IEEE80211_DPRINTF(("%s: data from unknown src " - "%6D\n", __func__, - wh->i_addr2, ":")); - /* NB: caller deals with reference */ - ni = ieee80211_dup_bss(ic, wh->i_addr2); - if (ni != NULL) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_NOT_AUTHED); - ieee80211_free_node(ic, ni); - } + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown src"); + ieee80211_send_error(ic, ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_AUTHED); ic->ic_stats.is_rx_notassoc++; goto err; } if (ni->ni_associd == 0) { - IEEE80211_DPRINTF(("ieee80211_input: " - "data from unassoc src %6D\n", - wh->i_addr2, ":")); + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unassoc src"); IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_NOT_ASSOCED); - ieee80211_unref_node(&ni); ic->ic_stats.is_rx_notassoc++; goto err; } + + /* + * Check for power save state change. + */ + if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ + (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) + ieee80211_node_pwrsave(ni, + wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); break; - case IEEE80211_M_MONITOR: - break; + default: + /* XXX here to keep compiler happy */ + goto out; } + + /* + * Handle privacy requirements. Note that we + * must not be preempted from here until after + * we (potentially) call ieee80211_crypto_demic; + * otherwise we may violate assumptions in the + * crypto cipher modules used to do delayed update + * of replay sequence numbers. + */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - if (ic->ic_flags & IEEE80211_F_WEPON) { - m = ieee80211_wep_crypt(ifp, m, 0); - if (m == NULL) { - ic->ic_stats.is_rx_wepfail++; - goto err; - } - wh = mtod(m, struct ieee80211_frame *); - } else { - ic->ic_stats.is_rx_nowep++; + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "WEP", "%s", "PRIVACY off"); + ic->ic_stats.is_rx_noprivacy++; + IEEE80211_NODE_STAT(ni, rx_noprivacy); + goto out; + } + key = ieee80211_crypto_decap(ic, ni, m, hdrspace); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + IEEE80211_NODE_STAT(ni, rx_wepfail); + goto out; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } else { + key = NULL; + } + + /* + * Next up, any fragmentation. + */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + m = ieee80211_defrag(ic, ni, m, hdrspace); + if (m == NULL) { + /* Fragment dropped or frame not complete yet */ goto out; } } + wh = NULL; /* no longer valid, catch any uses */ + + /* + * Next strip any MSDU crypto bits. + */ + if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "demic error"); + IEEE80211_NODE_STAT(ni, rx_demicfail); + goto out; + } + /* copy to listener after decrypt */ - if (ic->ic_rawbpf != NULL) + if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); - m = ieee80211_decap(ifp, m); + /* + * Finally, strip the 802.11 header. + */ + m = ieee80211_decap(ic, m, hdrspace); if (m == NULL) { + /* don't count Null data frames as errors */ + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) + goto out; + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "decap error"); ic->ic_stats.is_rx_decap++; + IEEE80211_NODE_STAT(ni, rx_decap); goto err; } - ifp->if_ipackets++; - - /* perform as a bridge within the AP */ - m1 = NULL; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - eh = mtod(m, struct ether_header *); - if (ETHER_IS_MULTICAST(eh->ether_dhost)) { - m1 = m_copypacket(m, MB_DONTWAIT); - if (m1 == NULL) - ifp->if_oerrors++; - else - m1->m_flags |= M_MCAST; - } else { - ni = ieee80211_find_node(ic, eh->ether_dhost); - if (ni != NULL) { - if (ni->ni_associd != 0) { - m1 = m; - m = NULL; - } - ieee80211_free_node(ic, ni); - } + eh = mtod(m, struct ether_header *); + if (!ieee80211_node_is_authorized(ni)) { + /* + * Deny any non-PAE frames received prior to + * authorization. For open/shared-key + * authentication the port is mark authorized + * after authentication completes. For 802.1x + * the port is not marked authorized by the + * authenticator until the handshake has completed. + */ + if (eh->ether_type != htons(ETHERTYPE_PAE)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + eh->ether_shost, "data", + "unauthorized port: ether type 0x%x len %u", + eh->ether_type, m->m_pkthdr.len); + ic->ic_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; } - if (m1 != NULL) { - len = m1->m_pkthdr.len; - IF_ENQUEUE(&ifp->if_snd, m1); - if (m != NULL) - ifp->if_omcasts++; - ifp->if_obytes += len; + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && + key == NULL && + eh->ether_type != htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + ic->ic_stats.is_rx_unencrypted++; + IEEE80211_NODE_STAT(ni, rx_unencrypted); + goto out; } } - if (m != NULL) - ifp->if_input(ifp, m); - return; + ifp->if_ipackets++; + IEEE80211_NODE_STAT(ni, rx_data); + IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); + + ieee80211_deliver_data(ic, ni, m); + return IEEE80211_FC0_TYPE_DATA; case IEEE80211_FC0_TYPE_MGT: + IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown dir 0x%x", dir); ic->ic_stats.is_rx_wrongdir++; goto err; } - if (ic->ic_opmode == IEEE80211_M_AHDEMO) { - ic->ic_stats.is_rx_ahdemo_mgt++; + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "mgt", "too short: len %u", + m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; goto out; } - subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - /* drop frames without interest */ - if (ic->ic_state == IEEE80211_S_SCAN) { - if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && - subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) { - ic->ic_stats.is_rx_mgtdiscard++; +#ifdef IEEE80211_DEBUG + if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || + ieee80211_msg_dumppkts(ic)) { + if_printf(ic->ic_ifp, "received %s from %6D rssi %d\n", + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + wh->i_addr2, ":", rssi); + } +#endif + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { + /* + * Only shared key auth frames with a challenge + * should be encrypted, discard all others. + */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "WEP set but not permitted"); + ic->ic_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } - } else { - if (ic->ic_opmode != IEEE80211_M_IBSS && - subtype == IEEE80211_FC0_SUBTYPE_BEACON) { - ic->ic_stats.is_rx_mgtdiscard++; + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "mgt", "%s", "WEP set but PRIVACY off"); + ic->ic_stats.is_rx_noprivacy++; goto out; } - } - - if (ifp->if_flags & IFF_DEBUG) { - /* avoid to print too many frames */ - int doprint = 0; - - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_BEACON: - if (ic->ic_state == IEEE80211_S_SCAN) - doprint = 1; - break; - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - if (ic->ic_opmode == IEEE80211_M_IBSS) - doprint = 1; - break; - default: - doprint = 1; - break; + hdrspace = ieee80211_hdrspace(ic, wh); + key = ieee80211_crypto_decap(ic, ni, m, hdrspace); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; } -#ifdef IEEE80211_DEBUG - doprint += ieee80211_debug; -#endif - if (doprint) - if_printf(ifp, "received %s from %6D rssi %d\n", - ieee80211_mgt_subtype_name[subtype - >> IEEE80211_FC0_SUBTYPE_SHIFT], - wh->i_addr2, ":", rssi); + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; } - if (ic->ic_rawbpf != NULL) + if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); m_freem(m); - return; + return type; case IEEE80211_FC0_TYPE_CTL: + IEEE80211_NODE_STAT(ni, rx_ctrl); ic->ic_stats.is_rx_ctl++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PS_POLL: + ieee80211_recv_pspoll(ic, ni, m); + break; + } + } goto out; default: - IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, NULL, "bad frame type 0x%x", type); /* should not come here */ break; } - err: +err: ifp->if_ierrors++; +out: + if (m != NULL) { + if (ic->ic_rawbpf) + bpf_mtap(ic->ic_rawbpf, m); + m_freem(m); + } + return type; +#undef SEQ_LEQ +} + +/* + * This function reassemble fragments. + */ +static struct mbuf * +ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m, int hdrspace) +{ + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + struct ieee80211_frame *lwh; + uint16_t rxseq; + uint8_t fragno; + uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + struct mbuf *mfrag; + + KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); + + rxseq = le16toh(*(uint16_t *)wh->i_seq); + fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; + + /* Quick way out, if there's nothing to defragment */ + if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL) + return m; + + /* + * Remove frag to insure it doesn't get reaped by timer. + */ + if (ni->ni_table == NULL) { + /* + * Should never happen. If the node is orphaned (not in + * the table) then input packets should not reach here. + * Otherwise, a concurrent request that yanks the table + * should be blocked by other interlocking and/or by first + * shutting the driver down. Regardless, be defensive + * here and just bail + */ + /* XXX need msg+stat */ + m_freem(m); + return NULL; + } + mfrag = ni->ni_rxfrag[0]; + ni->ni_rxfrag[0] = NULL; + + /* + * Validate new fragment is in order and + * related to the previous ones. + */ + if (mfrag != NULL) { + uint16_t last_rxseq; + + lwh = mtod(mfrag, struct ieee80211_frame *); + last_rxseq = le16toh(*(uint16_t *)lwh->i_seq); + /* NB: check seq # and frag together */ + if (rxseq != last_rxseq+1 || + !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || + !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) { + /* + * Unrelated fragment or no space for it, + * clear current fragments. + */ + m_freem(mfrag); + mfrag = NULL; + } + } + + if (mfrag == NULL) { + if (fragno != 0) { /* !first fragment, discard */ + IEEE80211_NODE_STAT(ni, rx_defrag); + m_freem(m); + return NULL; + } + mfrag = m; + } else { /* concatenate */ + m_adj(m, hdrspace); /* strip header */ + m_cat(mfrag, m); + /* NB: m_cat doesn't update the packet header */ + mfrag->m_pkthdr.len += m->m_pkthdr.len; + /* track last seqnum and fragno */ + lwh = mtod(mfrag, struct ieee80211_frame *); + *(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq; + } + if (more_frag) { /* more to come, save */ + ni->ni_rxfragstamp = ticks; + ni->ni_rxfrag[0] = mfrag; + mfrag = NULL; + } + return mfrag; +} + +static void +ieee80211_deliver_data(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) +{ + struct ether_header *eh = mtod(m, struct ether_header *); + struct ifnet *ifp = ic->ic_ifp; + + /* perform as a bridge within the AP */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) { + struct mbuf *m1 = NULL; + + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + m1 = m_dup(m, MB_DONTWAIT); + if (m1 == NULL) + ifp->if_oerrors++; + else + m1->m_flags |= M_MCAST; + } else { + /* + * Check if the destination is known; if so + * and the port is authorized dispatch directly. + */ + struct ieee80211_node *sta = + ieee80211_find_node(&ic->ic_sta, eh->ether_dhost); + if (sta != NULL) { + if (ieee80211_node_is_authorized(sta)) { + /* + * Beware of sending to ourself; this + * needs to happen via the normal + * input path. + */ + if (sta != ic->ic_bss) { + m1 = m; + m = NULL; + } + } else { + ic->ic_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(sta, rx_unauth); + } + ieee80211_free_node(sta); + } + } + if (m1 != NULL) { + /* XXX bypasses ALTQ */ + ifq_handoff(ifp, m1, NULL); + } + } + if (m != NULL) { +#ifdef FREEBSD_VLAN + if (ni->ni_vlan != 0) { + /* attach vlan tag */ + VLAN_INPUT_TAG_NEW(ifp, m, ni->ni_vlan); + if (m == NULL) + goto out; /* XXX goto err? */ + } +#endif + ifp->if_input(ifp, m); + } + return; + +#ifdef FREEBSD_VLAN out: if (m != NULL) { - if (ic->ic_rawbpf != NULL) + if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); m_freem(m); } +#endif } -struct mbuf * -ieee80211_decap(struct ifnet *ifp, struct mbuf *m) +static struct mbuf * +ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) { + struct ieee80211_qosframe_addr4 wh; /* Max size address frames */ struct ether_header *eh; - struct ieee80211_frame wh; struct llc *llc; - if (m->m_len < sizeof(wh) + sizeof(*llc)) { - m = m_pullup(m, sizeof(wh) + sizeof(*llc)); - if (m == NULL) - return NULL; + if (m->m_len < hdrlen + sizeof(*llc) && + (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { + /* XXX stat, msg */ + return NULL; } - memcpy(&wh, mtod(m, caddr_t), sizeof(wh)); - llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh)); + memcpy(&wh, mtod(m, caddr_t), hdrlen); + llc = (struct llc *)(mtod(m, caddr_t) + hdrlen); if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { - m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh)); + m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); llc = NULL; } else { - m_adj(m, sizeof(wh) - sizeof(*eh)); + m_adj(m, hdrlen - sizeof(*eh)); } eh = mtod(m, struct ether_header *); switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { @@ -415,10 +775,9 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m) IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); break; case IEEE80211_FC1_DIR_DSTODS: - /* not yet supported */ - IEEE80211_DPRINTF(("%s: DS to DS\n", __func__)); - m_freem(m); - return NULL; + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4); + break; } #ifdef ALIGNED_POINTER if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) { @@ -432,7 +791,7 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m) pktlen = m->m_pkthdr.len; while (pktlen > off) { if (n0 == NULL) { - MGETHDR(n, MB_DONTWAIT, MT_DATA); + MGETHDR(n, M_DONTWAIT, MT_DATA); if (n == NULL) { m_freem(m); return NULL; @@ -440,7 +799,7 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m) M_MOVE_PKTHDR(n, m); n->m_len = MHLEN; } else { - MGET(n, MB_DONTWAIT, MT_DATA); + MGET(n, M_DONTWAIT, MT_DATA); if (n == NULL) { m_freem(m); m_freem(n0); @@ -449,7 +808,7 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m) n->m_len = MLEN; } if (pktlen - off >= MINCLSIZE) { - MCLGET(n, MB_DONTWAIT); + MCLGET(n, M_DONTWAIT); if (n->m_flags & M_EXT) n->m_len = n->m_ext.ext_size; } @@ -481,10 +840,11 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m) /* * Install received rate set information in the node's state block. */ -static int -ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t *rates, uint8_t *xrates, int flags) +int +ieee80211_setup_rates(struct ieee80211_node *ni, + const uint8_t *rates, const uint8_t *xrates, int flags) { + struct ieee80211com *ic = ni->ni_ic; struct ieee80211_rateset *rs = &ni->ni_rates; memset(rs, 0, sizeof(*rs)); @@ -498,81 +858,958 @@ ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, nxrates = xrates[1]; if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; - IEEE80211_DPRINTF(("%s: extended rate set too large;" - " only using %u of %u rates\n", - __func__, nxrates, xrates[1])); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, + "[%6D] extended rate set too large;" + " only using %u of %u rates\n", + ni->ni_macaddr, ":", nxrates, xrates[1]); ic->ic_stats.is_rx_rstoobig++; } memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); rs->rs_nrates += nxrates; } - return ieee80211_fix_rate(ic, ni, flags); + return ieee80211_fix_rate(ni, flags); } -/* Verify the existence and length of __elem or get out. */ -#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ - if ((__elem) == NULL) { \ - IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n", \ - __func__, ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT])); \ - ic->ic_stats.is_rx_elem_missing++; \ - return; \ - } \ - if ((__elem)[1] > (__maxlen)) { \ - IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s " \ - "frame from %6D\n", __func__, (__elem)[1], \ - ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - wh->i_addr2, ":")); \ - ic->ic_stats.is_rx_elem_toobig++; \ - return; \ - } \ -} while (0) - -#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ - if ((_len) < (_minlen)) { \ - IEEE80211_DPRINTF(("%s: %s frame too short from %6D\n", \ - __func__, \ - ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - wh->i_addr2, ":")); \ - ic->ic_stats.is_rx_elem_toosmall++; \ - return; \ - } \ -} while (0) - -void -ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_node *ni, - int subtype, int rssi, uint32_t rstamp) +static void +ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, + struct ieee80211_node *ni, int rssi, uint32_t rstamp, uint16_t seq, + uint16_t status) { - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - uint8_t *frm, *efrm; - uint8_t *ssid, *rates, *xrates; - int reassoc, resp, newassoc, allocbs; - wh = mtod(m0, struct ieee80211_frame *); - frm = (uint8_t *)&wh[1]; - efrm = mtod(m0, uint8_t *) + m0->m_len; - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - case IEEE80211_FC0_SUBTYPE_BEACON: { - uint8_t *tstamp, *bintval, *capinfo, *country; - uint8_t chan, bchan, fhindex, erp; - uint16_t fhdwell; - int isprobe; - - if (ic->ic_opmode != IEEE80211_M_IBSS && - ic->ic_state != IEEE80211_S_SCAN) { - /* XXX: may be useful for background scan */ + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "open auth", + "bad sta auth mode %u", ni->ni_authmode); + ic->ic_stats.is_rx_bad_auth++; /* XXX */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX hack to workaround calling convention */ + ieee80211_send_error(ic, ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (IEEE80211_STATUS_ALG<<16)); + } + return; + } + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_MONITOR: + /* should not come here */ + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "open auth", + "bad operating mode %u", ic->ic_opmode); + break; + + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN || + seq != IEEE80211_AUTH_OPEN_REQUEST) { + ic->ic_stats.is_rx_bad_auth++; return; } - isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP); + /* always accept open authentication requests */ + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); + if (ni == NULL) + return; + } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) + (void) ieee80211_ref_node(ni); + /* + * Mark the node as referenced to reflect that it's + * reference count has been bumped to insure it remains + * after the transaction completes. + */ + ni->ni_flags |= IEEE80211_NODE_AREF; + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%6D] station authenticated (open)\n", + ni->ni_macaddr, ":"); /* - * beacon/probe response frame format - * [8] time stamp + * When 802.1x is not in use mark the port + * authorized at this point so traffic can flow. + */ + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ni); + break; + + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH || + seq != IEEE80211_AUTH_OPEN_RESPONSE) { + ic->ic_stats.is_rx_bad_auth++; + return; + } + if (status != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%6D] open auth failed (reason %d)\n", + ni->ni_macaddr, ":", status); + /* XXX can this happen? */ + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + } else + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + } +} + +/* + * Send a management frame error response to the specified + * station. If ni is associated with the station then use + * it; otherwise allocate a temporary node suitable for + * transmitting the frame and then free the reference so + * it will go away as soon as the frame has been transmitted. + */ +static void +ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, + const uint8_t *mac, int subtype, int arg) +{ + int istmp; + + if (ni == ic->ic_bss) { + ni = ieee80211_tmp_node(ic, mac); + if (ni == NULL) { + /* XXX msg */ + return; + } + istmp = 1; + } else + istmp = 0; + IEEE80211_SEND_MGMT(ic, ni, subtype, arg); + if (istmp) + ieee80211_free_node(ni); +} + +static int +alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (ni->ni_challenge == NULL) + MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN, + M_DEVBUF, M_NOWAIT); + if (ni->ni_challenge == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%6D] shared key challenge alloc failed\n", + ni->ni_macaddr, ":"); + /* XXX statistic */ + } + return (ni->ni_challenge != NULL); +} + +/* XXX TODO: add statistics */ +static void +ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, + uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni, int rssi, + uint32_t rstamp, uint16_t seq, uint16_t status) +{ + uint8_t *challenge; + int allocbs, estatus; + + /* + * NB: this can happen as we allow pre-shared key + * authentication to be enabled w/o wep being turned + * on so that configuration of these can be done + * in any order. It may be better to enforce the + * ordering in which case this check would just be + * for sanity/consistency. + */ + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", " PRIVACY is disabled"); + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + /* + * Pre-shared key authentication is evil; accept + * it only if explicitly configured (it is supported + * mainly for compatibility with clients like OS X). + */ + if (ni->ni_authmode != IEEE80211_AUTH_AUTO && + ni->ni_authmode != IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad sta auth mode %u", ni->ni_authmode); + ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + + challenge = NULL; + if (frm + 1 < efrm) { + if ((frm[1] + 2) > (efrm - frm)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "ie %d/%d too long", + frm[0], (frm[1] + 2) - (efrm - frm)); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (*frm == IEEE80211_ELEMID_CHALLENGE) + challenge = frm; + frm += frm[1] + 2; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_CHALLENGE: + case IEEE80211_AUTH_SHARED_RESPONSE: + if (challenge == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", "no challenge"); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad challenge len %d", challenge[1]); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + switch (ic->ic_opmode) { + case IEEE80211_M_MONITOR: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad operating mode %u", ic->ic_opmode); + return; + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad state %u", ic->ic_state); + estatus = IEEE80211_STATUS_ALG; /* XXX */ + goto bad; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_REQUEST: + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); + if (ni == NULL) { + /* NB: no way to return an error */ + return; + } + allocbs = 1; + } else { + if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) + (void) ieee80211_ref_node(ni); + allocbs = 0; + } + /* + * Mark the node as referenced to reflect that it's + * reference count has been bumped to insure it remains + * after the transaction completes. + */ + ni->ni_flags |= IEEE80211_NODE_AREF; + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + if (!alloc_challenge(ic, ni)) { + /* NB: don't return error so they rexmit */ + return; + } + get_random_bytes(ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%6D] shared key %sauth request\n", + ni->ni_macaddr, ":", + allocbs ? "" : "re"); + break; + case IEEE80211_AUTH_SHARED_RESPONSE: + if (ni == ic->ic_bss) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "unknown station"); + /* NB: don't send a response */ + return; + } + if (ni->ni_challenge == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "no challenge recorded"); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (memcmp(ni->ni_challenge, &challenge[2], + challenge[1]) != 0) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "challenge mismatch"); + ic->ic_stats.is_rx_auth_fail++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%6D] station authenticated (shared key)\n", + ni->ni_macaddr, ":"); + ieee80211_node_authorize(ni); + break; + default: + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad seq %d", seq); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_SEQUENCE; + goto bad; + } + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH) + return; + switch (seq) { + case IEEE80211_AUTH_SHARED_PASS: + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + if (status != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%6D] shared key auth failed (reason %d)\n", + ieee80211_getbssid(ic, wh), ":", status); + /* XXX can this happen? */ + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_AUTH_SHARED_CHALLENGE: + if (!alloc_challenge(ic, ni)) + return; + /* XXX could optimize by passing recvd challenge */ + memcpy(ni->ni_challenge, &challenge[2], challenge[1]); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + default: + IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH, + wh, "shared key auth", "bad seq %d", seq); + ic->ic_stats.is_rx_bad_auth++; + return; + } + break; + } + return; +bad: + /* + * Send an error response; but only when operating as an AP. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX hack to workaround calling convention */ + ieee80211_send_error(ic, ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (estatus<<16)); + } else if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Kick the state machine. This short-circuits + * using the mgt frame timeout to trigger the + * state transition. + */ + if (ic->ic_state == IEEE80211_S_AUTH) + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + } +} + +/* Verify the existence and length of __elem or get out. */ +#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ + if ((__elem) == NULL) { \ + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ + wh, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + "%s", "no " #__elem ); \ + ic->ic_stats.is_rx_elem_missing++; \ + return; \ + } \ + if ((__elem)[1] > (__maxlen)) { \ + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ + wh, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + "bad " #__elem " len %d", (__elem)[1]); \ + ic->ic_stats.is_rx_elem_toobig++; \ + return; \ + } \ +} while (0) + +#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ + if ((_len) < (_minlen)) { \ + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ + wh, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + "%s", "ie too short"); \ + ic->ic_stats.is_rx_elem_toosmall++; \ + return; \ + } \ +} while (0) + +#ifdef IEEE80211_DEBUG +static void +ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, + uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) +{ + printf("[%6D] discard %s frame, ssid mismatch: ", mac, ":", tag); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf("\n"); +} + +#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ + if ((_ssid)[1] != 0 && \ + ((_ssid)[1] != (_ni)->ni_esslen || \ + memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ + if (ieee80211_msg_input(ic)) \ + ieee80211_ssid_mismatch(ic, \ + ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + wh->i_addr2, _ssid); \ + ic->ic_stats.is_rx_ssidmismatch++; \ + return; \ + } \ +} while (0) +#else /* !IEEE80211_DEBUG */ +#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \ + if ((_ssid)[1] != 0 && \ + ((_ssid)[1] != (_ni)->ni_esslen || \ + memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ + ic->ic_stats.is_rx_ssidmismatch++; \ + return; \ + } \ +} while (0) +#endif /* !IEEE80211_DEBUG */ + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) +#define LE_READ_4(p) \ + ((uint32_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8) | \ + (((const uint8_t *)(p))[2] << 16) | \ + (((const uint8_t *)(p))[3] << 24))) + +static int __inline +iswpaoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int __inline +iswmeoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); +} + +static int __inline +iswmeparam(const uint8_t *frm) +{ + return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && + frm[6] == WME_PARAM_OUI_SUBTYPE; +} + +static int __inline +iswmeinfo(const uint8_t *frm) +{ + return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && + frm[6] == WME_INFO_OUI_SUBTYPE; +} + +static int __inline +isatherosoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); +} + +/* + * Convert a WPA cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +wpa_cipher(uint8_t *sel, uint8_t *keylen) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_CSE_NULL): + return IEEE80211_CIPHER_NONE; + case WPA_SEL(WPA_CSE_WEP40): + if (keylen) + *keylen = 40 / NBBY; + return IEEE80211_CIPHER_WEP; + case WPA_SEL(WPA_CSE_WEP104): + if (keylen) + *keylen = 104 / NBBY; + return IEEE80211_CIPHER_WEP; + case WPA_SEL(WPA_CSE_TKIP): + return IEEE80211_CIPHER_TKIP; + case WPA_SEL(WPA_CSE_CCMP): + return IEEE80211_CIPHER_AES_CCM; + } + return 32; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +/* + * Convert a WPA key management/authentication algorithm + * to an internal code. + */ +static int +wpa_keymgmt(uint8_t *sel) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_ASE_8021X_UNSPEC): + return WPA_ASE_8021X_UNSPEC; + case WPA_SEL(WPA_ASE_8021X_PSK): + return WPA_ASE_8021X_PSK; + case WPA_SEL(WPA_ASE_NONE): + return WPA_ASE_NONE; + } + return 0; /* NB: so is discarded */ +#undef WPA_SEL +} + +/* + * Parse a WPA information element to collect parameters + * and validate the parameters against what has been + * configured for the system. + */ +static int +ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm, + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) +{ + uint8_t len = frm[1]; + uint32_t w; + int n; + + /* + * Check the length once for fixed parts: OUI, type, + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ + if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags); + return IEEE80211_REASON_IE_INVALID; + } + if (len < 14) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "too short, len %u", len); + return IEEE80211_REASON_IE_INVALID; + } + frm += 6, len -= 4; /* NB: len is payload only */ + /* NB: iswapoui already validated the OUI and type */ + w = LE_READ_2(frm); + if (w != WPA_VERSION) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + /* multicast/group cipher */ + w = wpa_cipher(frm, &rsn->rsn_mcastkeylen); + if (w != rsn->rsn_mcastcipher) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "mcast cipher mismatch; got %u, expected %u", + w, rsn->rsn_mcastcipher); + return IEEE80211_REASON_IE_INVALID; + } + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "ucast cipher data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<rsn_ucastkeylen); + frm += 4, len -= 4; + } + w &= rsn->rsn_ucastcipherset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "%s", "ucast cipher set empty"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & (1<rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "key mgmt alg data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= wpa_keymgmt(frm); + frm += 4, len -= 4; + } + w &= rsn->rsn_keymgmtset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "%s", "no acceptable key mgmt alg"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & WPA_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + if (len > 2) /* optional capabilities */ + rsn->rsn_caps = LE_READ_2(frm); + + return 0; +} + +/* + * Convert an RSN cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +rsn_cipher(uint8_t *sel, uint8_t *keylen) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_CSE_NULL): + return IEEE80211_CIPHER_NONE; + case RSN_SEL(RSN_CSE_WEP40): + if (keylen) + *keylen = 40 / NBBY; + return IEEE80211_CIPHER_WEP; + case RSN_SEL(RSN_CSE_WEP104): + if (keylen) + *keylen = 104 / NBBY; + return IEEE80211_CIPHER_WEP; + case RSN_SEL(RSN_CSE_TKIP): + return IEEE80211_CIPHER_TKIP; + case RSN_SEL(RSN_CSE_CCMP): + return IEEE80211_CIPHER_AES_CCM; + case RSN_SEL(RSN_CSE_WRAP): + return IEEE80211_CIPHER_AES_OCB; + } + return 32; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +/* + * Convert an RSN key management/authentication algorithm + * to an internal code. + */ +static int +rsn_keymgmt(uint8_t *sel) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_ASE_8021X_UNSPEC): + return RSN_ASE_8021X_UNSPEC; + case RSN_SEL(RSN_ASE_8021X_PSK): + return RSN_ASE_8021X_PSK; + case RSN_SEL(RSN_ASE_NONE): + return RSN_ASE_NONE; + } + return 0; /* NB: so is discarded */ +#undef RSN_SEL +} + +/* + * Parse a WPA/RSN information element to collect parameters + * and validate the parameters against what has been + * configured for the system. + */ +static int +ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm, + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) +{ + uint8_t len = frm[1]; + uint32_t w; + int n; + + /* + * Check the length once for fixed parts: + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ + if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags); + return IEEE80211_REASON_IE_INVALID; + } + if (len < 10) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "too short, len %u", len); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2; + w = LE_READ_2(frm); + if (w != RSN_VERSION) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + /* multicast/group cipher */ + w = rsn_cipher(frm, &rsn->rsn_mcastkeylen); + if (w != rsn->rsn_mcastcipher) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "mcast cipher mismatch; got %u, expected %u", + w, rsn->rsn_mcastcipher); + return IEEE80211_REASON_IE_INVALID; + } + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "ucast cipher data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<rsn_ucastkeylen); + frm += 4, len -= 4; + } + w &= rsn->rsn_ucastcipherset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "%s", "ucast cipher set empty"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & (1<rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "key mgmt alg data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= rsn_keymgmt(frm); + frm += 4, len -= 4; + } + w &= rsn->rsn_keymgmtset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "%s", "no acceptable key mgmt alg"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & RSN_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; + + /* optional RSN capabilities */ + if (len > 2) + rsn->rsn_caps = LE_READ_2(frm); + /* XXXPMKID */ + + return 0; +} + +static int +ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm, + const struct ieee80211_frame *wh) +{ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct ieee80211_wme_state *wme = &ic->ic_wme; + u_int len = frm[1], qosinfo; + int i; + + if (len < sizeof(struct ieee80211_wme_param)-2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, + wh, "WME", "too short, len %u", len); + return -1; + } + qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; + qosinfo &= WME_QOSINFO_COUNT; + /* XXX do proper check for wraparound */ + if (qosinfo == wme->wme_wmeChanParams.cap_info) + return 0; + frm += __offsetof(struct ieee80211_wme_param, params_acParams); + for (i = 0; i < WME_NUM_AC; i++) { + struct wmeParams *wmep = + &wme->wme_wmeChanParams.cap_wmeParams[i]; + /* NB: ACI not used */ + wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); + wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); + wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); + wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); + wmep->wmep_txopLimit = LE_READ_2(frm+2); + frm += 4; + } + wme->wme_wmeChanParams.cap_info = qosinfo; + return 1; +#undef MS +} + +void +ieee80211_saveie(uint8_t **iep, const uint8_t *ie) +{ + u_int ielen = ie[1]+2; + /* + * Record information element for later use. + */ + if (*iep == NULL || (*iep)[1] != ie[1]) { + if (*iep != NULL) + FREE(*iep, M_DEVBUF); + MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); + } + if (*iep != NULL) + memcpy(*iep, ie, ielen); + /* XXX note failure */ +} + +/* XXX find a better place for definition */ +struct l2_update_frame { + struct ether_header eh; + uint8_t dsap; + uint8_t ssap; + uint8_t control; + uint8_t xid[3]; +} __packed; + +/* + * Deliver a TGf L2UF frame on behalf of a station. + * This primes any bridge when the station is roaming + * between ap's on the same wired network. + */ +static void +ieee80211_deliver_l2uf(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct mbuf *m; + struct l2_update_frame *l2uf; + struct ether_header *eh; + + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, + "%s", "no mbuf for l2uf frame"); + ic->ic_stats.is_rx_nobuf++; /* XXX not right */ + return; + } + l2uf = mtod(m, struct l2_update_frame *); + eh = &l2uf->eh; + /* dst: Broadcast address */ + IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr); + /* src: associated STA */ + IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr); + eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh)); + + l2uf->dsap = 0; + l2uf->ssap = 0; + l2uf->control = 0xf5; + l2uf->xid[0] = 0x81; + l2uf->xid[1] = 0x80; + l2uf->xid[2] = 0x00; + + m->m_pkthdr.len = m->m_len = sizeof(*l2uf); + m->m_pkthdr.rcvif = ifp; + ieee80211_deliver_data(ic, ni, m); +} + +void +ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_node *ni, + int subtype, int rssi, uint32_t rstamp) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) + struct ieee80211_frame *wh; + uint8_t *frm, *efrm; + uint8_t *ssid, *rates, *xrates, *wpa, *wme; + int reassoc, resp, allocbs; + uint8_t rate; + + wh = mtod(m0, struct ieee80211_frame *); + frm = (uint8_t *)&wh[1]; + efrm = mtod(m0, uint8_t *) + m0->m_len; + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: { + struct ieee80211_scanparams scan; + + /* + * We process beacon/probe response frames: + * o when scanning, or + * o station mode when associated (to collect state + * updates such as 802.11g slot time), or + * o adhoc mode (to discover neighbors) + * Frames otherwise received are discarded. + */ + if (!((ic->ic_flags & IEEE80211_F_SCAN) || + (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) || + ic->ic_opmode == IEEE80211_M_IBSS)) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + /* + * beacon/probe response frame format + * [8] time stamp * [2] beacon interval * [2] capability information * [tlv] ssid @@ -581,33 +1818,34 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] parameter set (FH/DS) * [tlv] erp information * [tlv] extended supported rates + * [tlv] WME + * [tlv] WPA or RSN */ IEEE80211_VERIFY_LENGTH(efrm - frm, 12); - tstamp = frm; frm += 8; - bintval = frm; frm += 2; - capinfo = frm; frm += 2; - ssid = rates = xrates = country = NULL; - bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); - chan = bchan; - fhdwell = 0; - fhindex = 0; - erp = 0; + memset(&scan, 0, sizeof(scan)); + scan.tstamp = frm; frm += 8; + scan.bintval = le16toh(*(uint16_t *)frm); frm += 2; + scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2; + scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan); + scan.chan = scan.bchan; + while (frm < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); switch (*frm) { case IEEE80211_ELEMID_SSID: - ssid = frm; + scan.ssid = frm; break; case IEEE80211_ELEMID_RATES: - rates = frm; + scan.rates = frm; break; case IEEE80211_ELEMID_COUNTRY: - country = frm; + scan.country = frm; break; case IEEE80211_ELEMID_FHPARMS: if (ic->ic_phytype == IEEE80211_T_FH) { - fhdwell = (frm[3] << 8) | frm[2]; - chan = IEEE80211_FH_CHAN(frm[4], frm[5]); - fhindex = frm[6]; + scan.fhdwell = LE_READ_2(&frm[2]); + scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]); + scan.fhindex = frm[6]; } break; case IEEE80211_ELEMID_DSPARMS: @@ -616,48 +1854,64 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * is problematic for multi-mode devices. */ if (ic->ic_phytype != IEEE80211_T_FH) - chan = frm[2]; + scan.chan = frm[2]; break; case IEEE80211_ELEMID_TIM: + /* XXX ATIM? */ + scan.tim = frm; + scan.timoff = frm - mtod(m0, uint8_t *); break; case IEEE80211_ELEMID_IBSSPARMS: break; case IEEE80211_ELEMID_XRATES: - xrates = frm; + scan.xrates = frm; break; case IEEE80211_ELEMID_ERP: if (frm[1] != 1) { - IEEE80211_DPRINTF(("%s: invalid ERP " - "element; length %u, expecting " - "1\n", __func__, frm[1])); + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID, wh, "ERP", + "bad len %u", frm[1]); ic->ic_stats.is_rx_elem_toobig++; break; } - erp = frm[2]; + scan.erp = frm[2]; + break; + case IEEE80211_ELEMID_RSN: + scan.wpa = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + scan.wpa = frm; + else if (iswmeparam(frm) || iswmeinfo(frm)) + scan.wme = frm; + /* XXX Atheros OUI support */ break; default: - IEEE80211_DPRINTF2(("%s: element id %u/len %u " - "ignored\n", __func__, *frm, frm[1])); + IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, + wh, "unhandled", + "id %u, len %u", *frm, frm[1]); ic->ic_stats.is_rx_elem_unknown++; break; } frm += frm[1] + 2; } - IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); - IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN); if ( #if IEEE80211_CHAN_MAX < 255 - chan > IEEE80211_CHAN_MAX || + scan.chan > IEEE80211_CHAN_MAX || #endif - isclr(ic->ic_chan_active, chan)) { - IEEE80211_DPRINTF(("%s: ignore %s with invalid channel " - "%u\n", __func__, - isprobe ? "probe response" : "beacon", - chan)); + isclr(ic->ic_chan_active, scan.chan)) { + IEEE80211_DISCARD(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "invalid channel %u", scan.chan); ic->ic_stats.is_rx_badchan++; return; } - if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) { + if (scan.chan != scan.bchan && + ic->ic_phytype != IEEE80211_T_FH) { /* * Frame was received on a channel different from the * one indicated in the DS params element id; @@ -668,108 +1922,151 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * the rssi value should be correct even for * different hop pattern in FH. */ - IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked " - "for channel %u\n", __func__, - isprobe ? "probe response" : "beacon", - bchan, chan)); + IEEE80211_DISCARD(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "for off-channel %u", scan.chan); ic->ic_stats.is_rx_chanmismatch++; return; } + if (!(IEEE80211_BINTVAL_MIN <= scan.bintval && + scan.bintval <= IEEE80211_BINTVAL_MAX)) { + IEEE80211_DISCARD(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "bogus beacon interval", scan.bintval); + ic->ic_stats.is_rx_badbintval++; + return; + } /* - * Use mac and channel for lookup so we collect all - * potential AP's when scanning. Otherwise we may - * see the same AP on multiple channels and will only - * record the last one. We could filter APs here based - * on rssi, etc. but leave that to the end of the scan - * so we can keep the selection criteria in one spot. - * This may result in a bloat of the scanned AP list but - * it shouldn't be too much. + * Count frame now that we know it's to be processed. */ - ni = ieee80211_lookup_node(ic, wh->i_addr2, - &ic->ic_channels[chan]); -#ifdef IEEE80211_DEBUG - if (ieee80211_debug && - (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) { - printf("%s: %s%s on chan %u (bss chan %u) ", - __func__, (ni == NULL ? "new " : ""), - isprobe ? "probe response" : "beacon", - chan, bchan); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %6D\n", wh->i_addr2, ":"); - printf("%s: caps 0x%x bintval %u erp 0x%x\n", - __func__, le16toh(*(uint16_t *)capinfo), - le16toh(*(uint16_t *)bintval), erp); - if (country) - printf("%s: country info %*D\n", - __func__, country[1], country+2, " "); - } -#endif - if (ni == NULL) { - ni = ieee80211_alloc_node(ic, wh->i_addr2); - if (ni == NULL) - return; - ni->ni_esslen = ssid[1]; - memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); - memcpy(ni->ni_essid, ssid + 2, ssid[1]); - allocbs = 1; - } else if (ssid[1] != 0 && isprobe) { - /* - * Update ESSID at probe response to adopt hidden AP by - * Lucent/Cisco, which announces null ESSID in beacon. - */ - ni->ni_esslen = ssid[1]; - memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); - memcpy(ni->ni_essid, ssid + 2, ssid[1]); - allocbs = 0; + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + ic->ic_stats.is_rx_beacon++; /* XXX remove */ + IEEE80211_NODE_STAT(ni, rx_beacons); } else - allocbs = 0; - IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp)); - ni->ni_intval = le16toh(*(uint16_t *)bintval); - ni->ni_capinfo = le16toh(*(uint16_t *)capinfo); - /* XXX validate channel # */ - ni->ni_chan = &ic->ic_channels[chan]; - ni->ni_fhdwell = fhdwell; - ni->ni_fhindex = fhindex; - ni->ni_erp = erp; - /* NB: must be after ni_chan is setup */ - ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); + IEEE80211_NODE_STAT(ni, rx_proberesp); + /* - * When scanning we record results (nodes) with a zero - * refcnt. Otherwise we want to hold the reference for - * ibss neighbors so the nodes don't get released prematurely. - * Anything else can be discarded (XXX and should be handled - * above so we don't do so much work). + * When operating in station mode, check for state updates. + * Be careful to ignore beacons received while doing a + * background scan. We consider only 11g/WMM stuff right now. */ - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_unref_node(&ni); /* NB: do not free */ - else if (ic->ic_opmode == IEEE80211_M_IBSS && - allocbs && isprobe) { - /* - * Fake an association so the driver can setup it's - * private state. The rate set has been setup above; - * there is no handshake as in ap/station operation. - */ - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, 1); - /* NB: hold reference */ - } else { - /* XXX optimize to avoid work done above */ - ieee80211_free_node(ic, ni); + if (ic->ic_opmode == IEEE80211_M_STA && + ni->ni_associd != 0 && + ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || + IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { + /* record tsf of last beacon */ + memcpy(ni->ni_tstamp.data, scan.tstamp, + sizeof(ni->ni_tstamp)); + /* count beacon frame for s/w bmiss handling */ + ic->ic_swbmiss_count++; + ic->ic_bmiss_count = 0; + if (ni->ni_erp != scan.erp) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] erp change: was 0x%x, now 0x%x\n", + wh->i_addr2, ":", ni->ni_erp, scan.erp); + if (ic->ic_curmode == IEEE80211_MODE_11G && + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ni->ni_erp = scan.erp; + /* XXX statistic */ + } + if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] capabilities change: before 0x%x," + " now 0x%x\n", + wh->i_addr2, ":", + ni->ni_capinfo, scan.capinfo); + /* + * NB: we assume short preamble doesn't + * change dynamically + */ + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + ni->ni_capinfo = scan.capinfo; + /* XXX statistic */ + } + if (scan.wme != NULL && + (ni->ni_flags & IEEE80211_NODE_QOS) && + ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0) + ieee80211_wme_updateparams(ic); + if (scan.tim != NULL) { + struct ieee80211_tim_ie *ie = + (struct ieee80211_tim_ie *) scan.tim; + + ni->ni_dtim_count = ie->tim_count; + ni->ni_dtim_period = ie->tim_period; + } + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_add_scan(ic, &scan, wh, + subtype, rssi, rstamp); + return; + } + /* + * If scanning, just pass information to the scan module. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { + if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { + /* + * Actively scanning a channel marked passive; + * send a probe request now that we know there + * is 802.11 traffic present. + * + * XXX check if the beacon we recv'd gives + * us what we need and suppress the probe req + */ + ieee80211_probe_curchan(ic, 1); + ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; + } + ieee80211_add_scan(ic, &scan, wh, + subtype, rssi, rstamp); + return; + } + if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { + if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { + /* + * Create a new entry in the neighbor table. + */ + ni = ieee80211_add_neighbor(ic, wh, &scan); + } else if (ni->ni_capinfo == 0) { + /* + * Update faked node created on transmit. + * Note this also updates the tsf. + */ + ieee80211_init_neighbor(ni, wh, &scan); + } else { + /* + * Record tsf for potential resync. + */ + memcpy(ni->ni_tstamp.data, scan.tstamp, + sizeof(ni->ni_tstamp)); + } + if (ni != NULL) { + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + } } break; } - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { - uint8_t rate; - - if (ic->ic_opmode == IEEE80211_M_STA) + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_state != IEEE80211_S_RUN) { + ic->ic_stats.is_rx_mgtdiscard++; return; - if (ic->ic_state != IEEE80211_S_RUN) + } + if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { + /* frame must be directed */ + ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ return; + } /* * prreq frame format @@ -779,6 +2076,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, */ ssid = rates = xrates = NULL; while (frm < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -794,45 +2092,58 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - if (ssid[1] != 0 && - (ssid[1] != ic->ic_bss->ni_esslen || - memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("%s: ssid unmatch ", __func__); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %6D\n", wh->i_addr2, ":"); - } -#endif - ic->ic_stats.is_rx_ssidmismatch++; + IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); + if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "no ssid with ssid suppression enabled"); + ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/ return; } + allocbs = 0; if (ni == ic->ic_bss) { - ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ic->ic_opmode != IEEE80211_M_IBSS) { + ni = ieee80211_tmp_node(ic, wh->i_addr2); + allocbs = 1; + } else if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { + /* + * XXX Cannot tell if the sender is operating + * in ibss mode. But we need a new node to + * send the response so blindly add them to the + * neighbor table. + */ + ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, + wh->i_addr2); + } if (ni == NULL) return; - IEEE80211_DPRINTF(("%s: new req from %6D\n", - __func__, wh->i_addr2, ":")); - allocbs = 1; - } else - allocbs = 0; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] recv probe req\n", wh->i_addr2, ":"); ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; - rate = ieee80211_setup_rates(ic, ni, rates, xrates, - IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE - | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + rate = ieee80211_setup_rates(ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE + | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (rate & IEEE80211_RATE_BASIC) { - IEEE80211_DPRINTF(("%s: rate negotiation failed: %6D\n", - __func__, wh->i_addr2, ":")); + IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "recv'd rate set invalid"); } else { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); } - if (allocbs) - ieee80211_free_node(ic, ni); + if (allocbs) { + /* + * Temporary node created just to send a + * response, reclaim immediately. + */ + ieee80211_free_node(ni); + } break; - } case IEEE80211_FC0_SUBTYPE_AUTH: { uint16_t algo, seq, status; @@ -847,94 +2158,68 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, algo = le16toh(*(uint16_t *)frm); seq = le16toh(*(uint16_t *)(frm + 2)); status = le16toh(*(uint16_t *)(frm + 4)); - if (algo != IEEE80211_AUTH_ALG_OPEN) { - /* TODO: shared key auth */ - IEEE80211_DPRINTF(("%s: unsupported auth %d from %6D\n", - __func__, algo, wh->i_addr2, ":")); - ic->ic_stats.is_rx_auth_unsupported++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "[%6D] recv auth frame with algorithm %d seq %d\n", + wh->i_addr2, ":", algo, seq); + /* + * Consult the ACL policy module if setup. + */ + if (ic->ic_acl != NULL && + !ic->ic_acl->iac_check(ic, wh->i_addr2)) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL, + wh, "auth", "%s", "disallowed by ACL"); + ic->ic_stats.is_rx_acl++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); + } return; } - switch (ic->ic_opmode) { - case IEEE80211_M_IBSS: - if (ic->ic_state != IEEE80211_S_RUN || seq != 1) { - IEEE80211_DPRINTF(("%s: discard auth from %6D; " - "state %u, seq %u\n", __func__, - wh->i_addr2, ":", - ic->ic_state, seq)); - ic->ic_stats.is_rx_bad_auth++; - break; - } - ieee80211_new_state(ic, IEEE80211_S_AUTH, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - - case IEEE80211_M_AHDEMO: - /* should not come here */ - break; - - case IEEE80211_M_HOSTAP: - if (ic->ic_state != IEEE80211_S_RUN || seq != 1) { - IEEE80211_DPRINTF(("%s: discard auth from %6D; " - "state %u, seq %u\n", __func__, - wh->i_addr2, ":", - ic->ic_state, seq)); - ic->ic_stats.is_rx_bad_auth++; - break; - } - if (ni == ic->ic_bss) { - ni = ieee80211_alloc_node(ic, wh->i_addr2); - if (ni == NULL) - return; - IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - ni->ni_chan = ic->ic_bss->ni_chan; - allocbs = 1; - } else - allocbs = 0; - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 2); - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %6D %s authenticated\n", - ni->ni_macaddr, ":", - (allocbs ? "newly" : "already")); - break; - - case IEEE80211_M_STA: - if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) { - IEEE80211_DPRINTF(("%s: discard auth from %6D; " - "state %u, seq %u\n", __func__, - wh->i_addr2, ":", - ic->ic_state, seq)); - ic->ic_stats.is_rx_bad_auth++; - break; + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DISCARD(ic, + IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, + wh, "auth", "%s", "TKIP countermeasures enabled"); + ic->ic_stats.is_rx_auth_countermeasures++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_REASON_MIC_FAILURE); } - if (status != 0) { - if_printf(&ic->ic_if, - "authentication failed (reason %d) for %6D\n", - status, - wh->i_addr3, ":"); - if (ni != ic->ic_bss) - ni->ni_fails++; - ic->ic_stats.is_rx_auth_fail++; - return; + return; + } + if (algo == IEEE80211_AUTH_ALG_SHARED) + ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, + rstamp, seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, + status); + else { + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, "auth", "unsupported alg %d", algo); + ic->ic_stats.is_rx_auth_unsupported++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX not right */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq+1) | (IEEE80211_STATUS_ALG<<16)); } - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - case IEEE80211_M_MONITOR: - break; + return; } break; } case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { - uint16_t capinfo, bintval; + uint16_t capinfo, lintval; + struct ieee80211_rsnparms rsn; + uint8_t reason; if (ic->ic_opmode != IEEE80211_M_HOSTAP || - (ic->ic_state != IEEE80211_S_RUN)) + ic->ic_state != IEEE80211_S_RUN) { + ic->ic_stats.is_rx_mgtdiscard++; return; + } if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { reassoc = 1; @@ -951,20 +2236,24 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WPA or RSN */ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { - IEEE80211_DPRINTF(("%s: ignore other bss from %6D\n", - __func__, wh->i_addr2, ":")); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "wrong bssid"); ic->ic_stats.is_rx_assoc_bss++; return; } capinfo = le16toh(*(uint16_t *)frm); frm += 2; - bintval = le16toh(*(uint16_t *)frm); frm += 2; + lintval = le16toh(*(uint16_t *)frm); frm += 2; if (reassoc) frm += 6; /* ignore current AP info */ - ssid = rates = xrates = NULL; + ssid = rates = xrates = wpa = wme = NULL; while (frm < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -975,97 +2264,169 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + /* XXX verify only one of RSN and WPA ie's? */ + case IEEE80211_ELEMID_RSN: + wpa = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + wpa = frm; + else if (iswmeinfo(frm)) + wme = frm; + /* XXX Atheros OUI support */ + break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - if (ssid[1] != ic->ic_bss->ni_esslen || - memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("%s: ssid unmatch ", __func__); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %6D\n", wh->i_addr2, ":"); - } -#endif - ic->ic_stats.is_rx_ssidmismatch++; + IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); + + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%6D] deny %s request, sta not authenticated\n", + wh->i_addr2, ":", reassoc ? "reassoc" : "assoc"); + ieee80211_send_error(ic, ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_NOT_AUTHED); + ic->ic_stats.is_rx_assoc_notauth++; return; } - if (ni == ic->ic_bss) { - IEEE80211_DPRINTF(("%s: not authenticated for %6D\n", - __func__, wh->i_addr2, ":")); - ni = ieee80211_dup_bss(ic, wh->i_addr2); - if (ni != NULL) { + /* assert right associstion security credentials */ + if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + "[%6D] no WPA/RSN IE in association request\n", + wh->i_addr2, ":"); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_RSN_REQUIRED); + ieee80211_node_leave(ic, ni); + /* XXX distinguish WPA/RSN? */ + ic->ic_stats.is_rx_assoc_badwpaie++; + return; + } + if (wpa != NULL) { + /* + * Parse WPA information element. Note that + * we initialize the param block from the node + * state so that information in the IE overrides + * our defaults. The resulting parameters are + * installed below after the association is assured. + */ + rsn = ni->ni_rsn; + if (wpa[0] != IEEE80211_ELEMID_RSN) + reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); + else + reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); + if (reason != 0) { IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_ASSOC_NOT_AUTHED); - ieee80211_free_node(ic, ni); + IEEE80211_FC0_SUBTYPE_DEAUTH, reason); + ieee80211_node_leave(ic, ni); + /* XXX distinguish WPA/RSN? */ + ic->ic_stats.is_rx_assoc_badwpaie++; + return; } - ic->ic_stats.is_rx_assoc_notauth++; - return; + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + "[%6D] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", + wh->i_addr2, ":", + wpa[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN", + rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, + rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, + rsn.rsn_keymgmt, rsn.rsn_caps); + } + /* discard challenge after association */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; } - /* XXX per-node cipher suite */ - /* XXX some stations use the privacy bit for handling APs - that suport both encrypted and unencrypted traffic */ - if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || - (capinfo & IEEE80211_CAPINFO_PRIVACY) != - ((ic->ic_flags & IEEE80211_F_WEPON) ? - IEEE80211_CAPINFO_PRIVACY : 0)) { - IEEE80211_DPRINTF(("%s: capability mismatch %x for %6D\n", - __func__, capinfo, wh->i_addr2, ":")); - ni->ni_associd = 0; + /* NB: 802.11 spec says to ignore station's privacy bit */ + if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%6D] deny %s request, capability mismatch 0x%x\n", + wh->i_addr2, ":", + reassoc ? "reassoc" : "assoc", capinfo); IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO); + ieee80211_node_leave(ic, ni); ic->ic_stats.is_rx_assoc_capmismatch++; return; } - ieee80211_setup_rates(ic, ni, rates, xrates, + rate = ieee80211_setup_rates(ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ni->ni_rates.rs_nrates == 0) { - IEEE80211_DPRINTF(("%s: rate unmatch for %6D\n", - __func__, wh->i_addr2, ":")); - ni->ni_associd = 0; + /* + * If constrained to 11g-only stations reject an + * 11b-only station. We cheat a bit here by looking + * at the max negotiated xmit rate and assuming anyone + * with a best rate <24Mb/s is an 11b station. + */ + if ((rate & IEEE80211_RATE_BASIC) || + ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%6D] deny %s request, rate set mismatch\n", + wh->i_addr2, ":", + reassoc ? "reassoc" : "assoc"); IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); + ieee80211_node_leave(ic, ni); ic->ic_stats.is_rx_assoc_norate++; return; } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; - ni->ni_intval = bintval; + ni->ni_intval = lintval; ni->ni_capinfo = capinfo; ni->ni_chan = ic->ic_bss->ni_chan; ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; ni->ni_fhindex = ic->ic_bss->ni_fhindex; - if (ni->ni_associd == 0) { - /* XXX handle rollover at 2007 */ - /* XXX guarantee uniqueness */ - ni->ni_associd = 0xc000 | ic->ic_bss->ni_associd++; - newassoc = 1; - } else - newassoc = 0; - /* XXX for 11g must turn off short slot time if long - slot time sta associates */ - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %6D %s associated\n", - ni->ni_macaddr, ":", - (newassoc ? "newly" : "already")); - /* give driver a chance to setup state like ni_txrate */ - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, newassoc); + if (wpa != NULL) { + /* + * Record WPA/RSN parameters for station, mark + * node as using WPA and record information element + * for applications that require it. + */ + ni->ni_rsn = rsn; + ieee80211_saveie(&ni->ni_wpa_ie, wpa); + } else if (ni->ni_wpa_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_wpa_ie, M_DEVBUF); + ni->ni_wpa_ie = NULL; + } + if (wme != NULL) { + /* + * Record WME parameters for station, mark node + * as capable of QoS and record information + * element for applications that require it. + */ + ieee80211_saveie(&ni->ni_wme_ie, wme); + ni->ni_flags |= IEEE80211_NODE_QOS; + } else if (ni->ni_wme_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_wme_ie, M_DEVBUF); + ni->ni_wme_ie = NULL; + ni->ni_flags &= ~IEEE80211_NODE_QOS; + } + ieee80211_deliver_l2uf(ni); + ieee80211_node_join(ic, ni, resp); break; } case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { + uint16_t capinfo, associd; uint16_t status; if (ic->ic_opmode != IEEE80211_M_STA || - ic->ic_state != IEEE80211_S_ASSOC) + ic->ic_state != IEEE80211_S_ASSOC) { + ic->ic_stats.is_rx_mgtdiscard++; return; + } /* * asresp frame format @@ -1074,27 +2435,30 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [2] association ID * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME */ IEEE80211_VERIFY_LENGTH(efrm - frm, 6); ni = ic->ic_bss; - ni->ni_capinfo = le16toh(*(uint16_t *)frm); + capinfo = le16toh(*(uint16_t *)frm); frm += 2; - status = le16toh(*(uint16_t *)frm); frm += 2; if (status != 0) { - if_printf(ifp, "association failed (reason %d) for %6D\n", - status, wh->i_addr3, ":"); - if (ni != ic->ic_bss) + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] %sassoc failed (reason %d)\n", + wh->i_addr2, ":", + ISREASSOC(subtype) ? "re" : "", status); + if (ni != ic->ic_bss) /* XXX never true? */ ni->ni_fails++; - ic->ic_stats.is_rx_auth_fail++; + ic->ic_stats.is_rx_auth_fail++; /* XXX */ return; } - ni->ni_associd = le16toh(*(uint16_t *)frm); + associd = le16toh(*(uint16_t *)frm); frm += 2; - rates = xrates = NULL; + rates = xrates = wpa = wme = NULL; while (frm < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]); switch (*frm) { case IEEE80211_ELEMID_RATES: rates = frm; @@ -1102,22 +2466,86 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + case IEEE80211_ELEMID_VENDOR: + if (iswmeoui(frm)) + wme = frm; + /* XXX Atheros OUI support */ + break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); - ieee80211_setup_rates(ic, ni, rates, xrates, + rate = ieee80211_setup_rates(ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ni->ni_rates.rs_nrates != 0) - ieee80211_new_state(ic, IEEE80211_S_RUN, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + if (rate & IEEE80211_RATE_BASIC) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] %sassoc failed (rate set mismatch)\n", + wh->i_addr2, ":", + ISREASSOC(subtype) ? "re" : ""); + if (ni != ic->ic_bss) /* XXX never true? */ + ni->ni_fails++; + ic->ic_stats.is_rx_assoc_norate++; + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + return; + } + + ni->ni_capinfo = capinfo; + ni->ni_associd = associd; + if (wme != NULL && + ieee80211_parse_wmeparams(ic, wme, wh) >= 0) { + ni->ni_flags |= IEEE80211_NODE_QOS; + ieee80211_wme_updateparams(ic); + } else + ni->ni_flags &= ~IEEE80211_NODE_QOS; + /* + * Configure state now that we are associated. + * + * XXX may need different/additional driver callbacks? + */ + if (ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + /* + * Honor ERP protection. + * + * NB: ni_erp should zero for non-11g operation. + * XXX check ic_curmode anyway? + */ + if (ic->ic_curmode == IEEE80211_MODE_11G && + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] %sassoc success: %s preamble, %s slot time%s%s\n", + wh->i_addr2, ":", + ISREASSOC(subtype) ? "re" : "", + ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" + ); + ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); break; } case IEEE80211_FC0_SUBTYPE_DEAUTH: { uint16_t reason; + + if (ic->ic_state == IEEE80211_S_SCAN) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } /* * deauth frame format * [2] reason @@ -1125,22 +2553,22 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_VERIFY_LENGTH(efrm - frm, 2); reason = le16toh(*(uint16_t *)frm); ic->ic_stats.is_rx_deauth++; + IEEE80211_NODE_STAT(ni, rx_deauth); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "[%6D] recv deauthenticate (reason %d)\n", + ni->ni_macaddr, ":", reason); switch (ic->ic_opmode) { case IEEE80211_M_STA: ieee80211_new_state(ic, IEEE80211_S_AUTH, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; case IEEE80211_M_HOSTAP: - if (ni != ic->ic_bss) { - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %6D deauthenticated" - " by peer (reason %d)\n", - ni->ni_macaddr, ":", reason); - /* node will be free'd on return */ - ieee80211_unref_node(&ni); - } + if (ni != ic->ic_bss) + ieee80211_node_leave(ic, ni); break; default: + ic->ic_stats.is_rx_mgtdiscard++; break; } break; @@ -1148,6 +2576,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_FC0_SUBTYPE_DISASSOC: { uint16_t reason; + + if (ic->ic_state != IEEE80211_S_RUN && + ic->ic_state != IEEE80211_S_ASSOC && + ic->ic_state != IEEE80211_S_AUTH) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } /* * disassoc frame format * [2] reason @@ -1155,32 +2590,286 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_VERIFY_LENGTH(efrm - frm, 2); reason = le16toh(*(uint16_t *)frm); ic->ic_stats.is_rx_disassoc++; + IEEE80211_NODE_STAT(ni, rx_disassoc); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] recv disassociate (reason %d)\n", + ni->ni_macaddr, ":", reason); switch (ic->ic_opmode) { case IEEE80211_M_STA: ieee80211_new_state(ic, IEEE80211_S_ASSOC, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; case IEEE80211_M_HOSTAP: - if (ni != ic->ic_bss) { - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %6D disassociated" - " by peer (reason %d)\n", - ni->ni_macaddr, ":", reason); - ni->ni_associd = 0; - /* XXX node reclaimed how? */ - } + if (ni != ic->ic_bss) + ieee80211_node_leave(ic, ni); break; default: + ic->ic_stats.is_rx_mgtdiscard++; break; } break; } default: - IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not " - "handled\n", __func__, subtype)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); ic->ic_stats.is_rx_badsubtype++; break; } +#undef ISREASSOC +#undef ISPROBE } #undef IEEE80211_VERIFY_LENGTH #undef IEEE80211_VERIFY_ELEMENT + +/* + * Handle station power-save state change. + */ +static void +ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + if (enable) { + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) + ic->ic_ps_sta++; + ni->ni_flags |= IEEE80211_NODE_PWR_MGT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] power save mode on, %u sta's in ps mode\n", + ni->ni_macaddr, ":", ic->ic_ps_sta); + return; + } + + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) + ic->ic_ps_sta--; + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] power save mode off, %u sta's in ps mode\n", + ni->ni_macaddr, ":", ic->ic_ps_sta); + /* XXX if no stations in ps mode, flush mc frames */ + + /* + * Flush queued unicast frames. + */ + if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { + if (ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); /* just in case */ + return; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] flush ps queue, %u packets queued\n", + ni->ni_macaddr, ":", IEEE80211_NODE_SAVEQ_QLEN(ni)); + for (;;) { + int qlen; + + IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); + if (m == NULL) + break; + /* + * If this is the last packet, turn off the TIM bit. + * If there are more packets, set the more packets bit + * in the mbuf so ieee80211_encap will mark the 802.11 + * head to indicate more data frames will follow. + */ + if (qlen != 0) + m->m_flags |= M_MORE_DATA; + /* XXX need different driver interface */ + /* XXX bypasses q max */ + /* XXX bypasses ALTQ */ + ifq_enqueue(&ic->ic_ifp->if_snd, m, NULL); + } + if (ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); +} + +/* + * Process a received ps-poll frame. + */ +static void +ieee80211_recv_pspoll(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m0) +{ + struct ieee80211_frame_min *wh; + struct mbuf *m; + uint16_t aid; + int qlen; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + wh = mtod(m0, struct ieee80211_frame_min *); + if (ni->ni_associd == 0) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + (struct ieee80211_frame *) wh, "ps-poll", + "%s", "unassociated station"); + ic->ic_stats.is_ps_unassoc++; + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_ASSOCED); + return; + } + + aid = le16toh(*(uint16_t *)wh->i_dur); + if (aid != ni->ni_associd) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + (struct ieee80211_frame *) wh, "ps-poll", + "aid mismatch: sta aid 0x%x poll aid 0x%x", + ni->ni_associd, aid); + ic->ic_stats.is_ps_badaid++; + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_ASSOCED); + return; + } + + /* Okay, take the first queued packet and put it out... */ + IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); + if (m == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] recv ps-poll, but queue empty\n", + wh->i_addr2, ":"); + ieee80211_send_nulldata(ieee80211_ref_node(ni)); + ic->ic_stats.is_ps_qempty++; /* XXX node stat */ + if (ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); /* just in case */ + return; + } + /* + * If there are more packets, set the more packets bit + * in the packet dispatched to the station; otherwise + * turn off the TIM bit. + */ + if (qlen != 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] recv ps-poll, send packet, %u still queued\n", + ni->ni_macaddr, ":", qlen); + m->m_flags |= M_MORE_DATA; + } else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] recv ps-poll, send packet, queue empty\n", + ni->ni_macaddr, ":"); + if (ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); + } + m->m_flags |= M_PWR_SAV; /* bypass PS handling */ + ifq_enqueue(&ic->ic_ifp->if_snd, m, NULL); /* XXX bypasses ALTQ */ +} + +#ifdef IEEE80211_DEBUG +/* + * Debugging support. + */ + +/* + * Return the bssid of a frame. + */ +static const uint8_t * +ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) +{ + if (ic->ic_opmode == IEEE80211_M_STA) + return wh->i_addr2; + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) + return wh->i_addr1; + if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) + return wh->i_addr1; + return wh->i_addr3; +} + +void +ieee80211_note(struct ieee80211com *ic, const char *fmt, ...) +{ + char buf[128]; /* XXX */ + __va_list ap; + + __va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + __va_end(ap); + + if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */ +} + +void +ieee80211_note_frame(struct ieee80211com *ic, + const struct ieee80211_frame *wh, + const char *fmt, ...) +{ + char buf[128]; /* XXX */ + __va_list ap; + + __va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + __va_end(ap); + if_printf(ic->ic_ifp, "[%6D] %s\n", + ieee80211_getbssid(ic, wh), ":", buf); +} + +void +ieee80211_note_mac(struct ieee80211com *ic, + const uint8_t mac[IEEE80211_ADDR_LEN], + const char *fmt, ...) +{ + char buf[128]; /* XXX */ + __va_list ap; + + __va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + __va_end(ap); + if_printf(ic->ic_ifp, "[%6D] %s\n", mac, ":", buf); +} + +static void +ieee80211_discard_frame(struct ieee80211com *ic, + const struct ieee80211_frame *wh, + const char *type, const char *fmt, ...) +{ + __va_list ap; + + printf("[%s:%6D] discard ", ic->ic_ifp->if_xname, + ieee80211_getbssid(ic, wh), ":"); + if (type != NULL) + printf("%s frame, ", type); + else + printf("frame, "); + __va_start(ap, fmt); + vprintf(fmt, ap); + __va_end(ap); + printf("\n"); +} + +static void +ieee80211_discard_ie(struct ieee80211com *ic, + const struct ieee80211_frame *wh, + const char *type, const char *fmt, ...) +{ + __va_list ap; + + printf("[%s:%6D] discard ", ic->ic_ifp->if_xname, + ieee80211_getbssid(ic, wh), ":"); + if (type != NULL) + printf("%s information element, ", type); + else + printf("information element, "); + __va_start(ap, fmt); + vprintf(fmt, ap); + __va_end(ap); + printf("\n"); +} + +static void +ieee80211_discard_mac(struct ieee80211com *ic, + const uint8_t mac[IEEE80211_ADDR_LEN], + const char *type, const char *fmt, ...) +{ + __va_list ap; + + printf("[%s:%6D] discard ", ic->ic_ifp->if_xname, mac, ":"); + if (type != NULL) + printf("%s frame, ", type); + else + printf("frame, "); + __va_start(ap, fmt); + vprintf(fmt, ap); + __va_end(ap); + printf("\n"); +} +#endif /* IEEE80211_DEBUG */ diff --git a/sys/netproto/802_11/wlan/ieee80211_ioctl.c b/sys/netproto/802_11/wlan/ieee80211_ioctl.c index 1b2a434e73..dcb0ef963b 100644 --- a/sys/netproto/802_11/wlan/ieee80211_ioctl.c +++ b/sys/netproto/802_11/wlan/ieee80211_ioctl.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,12 @@ * (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: src/sys/net80211/ieee80211_ioctl.c,v 1.13 2004/03/30 22:57:57 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_ioctl.c,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.25.2.11 2006/02/28 02:02:43 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_ioctl.c,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ /* - * IEEE 802.11 ioctl support (FreeBSD-specific) + * IEEE 802.11 ioctl support (DragonFlyBSD-specific) */ #include "opt_inet.h" @@ -47,10 +47,10 @@ #include #include #include -#include #include #include +#include #include #include @@ -69,26 +69,132 @@ #include +#define IS_UP(_ic) \ + (((_ic)->ic_ifp->if_flags & (IFF_UP | IFF_RUNNING)) == \ + (IFF_UP | IFF_RUNNING)) + +#define IS_UP_AUTO(_ic) \ + (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) + /* * XXX * Wireless LAN specific configuration interface, which is compatible * with wicontrol(8). */ +struct wi_read_ap_args { + int i; /* result count */ + struct wi_apinfo *ap; /* current entry in result buffer */ + caddr_t max; /* result buffer bound */ +}; + +static void +wi_read_ap_result(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_read_ap_args *sa = arg; + struct wi_apinfo *ap = sa->ap; + struct ieee80211_rateset *rs; + int j; + + if ((caddr_t)(ap + 1) > sa->max) + return; + memset(ap, 0, sizeof(struct wi_apinfo)); + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); + ap->namelen = ic->ic_des_esslen; + if (ic->ic_des_esslen) + memcpy(ap->name, ic->ic_des_essid, + ic->ic_des_esslen); + } else { + IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); + ap->namelen = ni->ni_esslen; + if (ni->ni_esslen) + memcpy(ap->name, ni->ni_essid, + ni->ni_esslen); + } + ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); + ap->signal = ic->ic_node_getrssi(ni); + ap->capinfo = ni->ni_capinfo; + ap->interval = ni->ni_intval; + rs = &ni->ni_rates; + for (j = 0; j < rs->rs_nrates; j++) { + if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { + ap->rate = (rs->rs_rates[j] & + IEEE80211_RATE_VAL) * 5; /* XXX */ + } + } + sa->i++; + sa->ap++; +} + +struct wi_read_prism2_args { + int i; /* result count */ + struct wi_scan_res *res;/* current entry in result buffer */ + caddr_t max; /* result buffer bound */ +}; + +static void +wi_read_prism2_result(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_read_prism2_args *sa = arg; + struct wi_scan_res *res = sa->res; + + if ((caddr_t)(res + 1) > sa->max) + return; + res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); + res->wi_noise = 0; + res->wi_signal = ic->ic_node_getrssi(ni); + IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); + res->wi_interval = ni->ni_intval; + res->wi_capinfo = ni->ni_capinfo; + res->wi_ssid_len = ni->ni_esslen; + memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); + /* NB: assumes wi_srates holds <= ni->ni_rates */ + memcpy(res->wi_srates, ni->ni_rates.rs_rates, + sizeof(res->wi_srates)); + if (ni->ni_rates.rs_nrates < 10) + res->wi_srates[ni->ni_rates.rs_nrates] = 0; + res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; + res->wi_rsvd = 0; + + sa->i++; + sa->res++; +} + +struct wi_read_sigcache_args { + int i; /* result count */ + struct wi_sigcache *wsc;/* current entry in result buffer */ + caddr_t max; /* result buffer bound */ +}; + +static void +wi_read_sigcache(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_read_sigcache_args *sa = arg; + struct wi_sigcache *wsc = sa->wsc; + + if ((caddr_t)(wsc + 1) > sa->max) + return; + memset(wsc, 0, sizeof(struct wi_sigcache)); + IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); + wsc->signal = ic->ic_node_getrssi(ni); + + sa->wsc++; + sa->i++; +} + int -ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) +ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data, + struct ucred *cr) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; int i, j, error; struct ifreq *ifr = (struct ifreq *)data; struct wi_req wreq; struct wi_ltv_keys *keys; - struct wi_apinfo *ap; - struct ieee80211_node *ni; - struct ieee80211_rateset *rs; - struct wi_sigcache wsc; - struct wi_scan_p2_hdr *p2; - struct wi_scan_res *res; error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); if (error) @@ -150,13 +256,12 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) break; case WI_RID_CURRENT_CHAN: wreq.wi_val[0] = htole16( - ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); + ieee80211_chan2ieee(ic, ic->ic_curchan)); wreq.wi_len = 1; break; case WI_RID_COMMS_QUALITY: wreq.wi_val[0] = 0; /* quality */ - wreq.wi_val[1] = - htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss)); + wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); wreq.wi_val[2] = 0; /* noise */ wreq.wi_len = 3; break; @@ -173,7 +278,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) wreq.wi_len = IEEE80211_ADDR_LEN / 2; break; case WI_RID_TX_RATE: - if (ic->ic_fixed_rate == -1) + if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) wreq.wi_val[0] = 0; /* auto */ else wreq.wi_val[0] = htole16( @@ -201,7 +306,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) wreq.wi_len = 1; break; case WI_RID_ROAMING_MODE: - wreq.wi_val[0] = htole16(1); /* enabled ... not supported */ + wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ wreq.wi_len = 1; break; case WI_RID_SYSTEM_SCALE: @@ -222,8 +327,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) wreq.wi_len = 1; break; case WI_RID_WEP_AVAIL: - wreq.wi_val[0] = - htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0); + wreq.wi_val[0] = htole16(1); /* always available */ wreq.wi_len = 1; break; case WI_RID_CNFAUTHMODE: @@ -232,11 +336,11 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) break; case WI_RID_ENCRYPTION: wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0); + htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_TX_CRYPT_KEY: - wreq.wi_val[0] = htole16(ic->ic_wep_txkey); + wreq.wi_val[0] = htole16(ic->ic_def_txkey); wreq.wi_len = 1; break; case WI_RID_DEFLT_CRYPT_KEYS: @@ -250,124 +354,71 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) } for (i = 0; i < IEEE80211_WEP_NKID; i++) { keys->wi_keys[i].wi_keylen = - htole16(ic->ic_nw_keys[i].wk_len); + htole16(ic->ic_nw_keys[i].wk_keylen); memcpy(keys->wi_keys[i].wi_keydat, - ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); + ic->ic_nw_keys[i].wk_key, + ic->ic_nw_keys[i].wk_keylen); } wreq.wi_len = sizeof(*keys) / 2; break; case WI_RID_MAX_DATALEN: - wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ + wreq.wi_val[0] = htole16(ic->ic_fragthreshold); wreq.wi_len = 1; break; case WI_RID_IFACE_STATS: /* XXX: should be implemented in lower drivers */ break; case WI_RID_READ_APS: - if (ic->ic_opmode != IEEE80211_M_HOSTAP) { - /* - * Don't return results until active scan completes. - */ - if (ic->ic_state == IEEE80211_S_SCAN && - (ic->ic_flags & IEEE80211_F_ASCAN)) { - error = EINPROGRESS; - break; - } - } - i = 0; - ap = (void *)((char *)wreq.wi_val + sizeof(i)); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) - break; - memset(ap, 0, sizeof(*ap)); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); - ap->namelen = ic->ic_des_esslen; - if (ic->ic_des_esslen) - memcpy(ap->name, ic->ic_des_essid, - ic->ic_des_esslen); - } else { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); - ap->namelen = ni->ni_esslen; - if (ni->ni_esslen) - memcpy(ap->name, ni->ni_essid, - ni->ni_esslen); - } - ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); - ap->signal = (*ic->ic_node_getrssi)(ic, ni); - ap->capinfo = ni->ni_capinfo; - ap->interval = ni->ni_intval; - rs = &ni->ni_rates; - for (j = 0; j < rs->rs_nrates; j++) { - if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { - ap->rate = (rs->rs_rates[j] & - IEEE80211_RATE_VAL) * 5; /* XXX */ - } - } - i++; - ap++; - } - memcpy(wreq.wi_val, &i, sizeof(i)); - wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; + /* + * Don't return results until active scan completes. + */ + if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { + struct wi_read_ap_args args; + + args.i = 0; + args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); + args.max = (void *)(&wreq + 1); + ieee80211_iterate_nodes(&ic->ic_scan, + wi_read_ap_result, &args); + memcpy(wreq.wi_val, &args.i, sizeof(args.i)); + wreq.wi_len = (sizeof(int) + + sizeof(struct wi_apinfo) * args.i) / 2; + } else + error = EINPROGRESS; break; case WI_RID_PRISM2: - wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */ + /* NB: we lie so WI_RID_SCAN_RES can include rates */ + wreq.wi_val[0] = 1; wreq.wi_len = sizeof(uint16_t) / 2; break; case WI_RID_SCAN_RES: /* compatibility interface */ - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_state == IEEE80211_S_SCAN && - (ic->ic_flags & IEEE80211_F_ASCAN)) { + if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { + struct wi_read_prism2_args args; + struct wi_scan_p2_hdr *p2; + + /* NB: use Prism2 format so we can include rate info */ + p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; + args.i = 0; + args.res = (void *)&p2[1]; + args.max = (void *)(&wreq + 1); + ieee80211_iterate_nodes(&ic->ic_scan, + wi_read_prism2_result, &args); + p2->wi_rsvd = 0; + p2->wi_reason = args.i; + wreq.wi_len = (sizeof(*p2) + + sizeof(struct wi_scan_res) * args.i) / 2; + } else error = EINPROGRESS; - break; - } - /* NB: we use the Prism2 format so we can return rate info */ - p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; - res = (void *)&p2[1]; - i = 0; - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1)) - break; - res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); - res->wi_noise = 0; - res->wi_signal = (*ic->ic_node_getrssi)(ic, ni); - IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); - res->wi_interval = ni->ni_intval; - res->wi_capinfo = ni->ni_capinfo; - res->wi_ssid_len = ni->ni_esslen; - memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); - /* NB: assumes wi_srates holds <= ni->ni_rates */ - memcpy(res->wi_srates, ni->ni_rates.rs_rates, - sizeof(res->wi_srates)); - if (ni->ni_rates.rs_nrates < 10) - res->wi_srates[ni->ni_rates.rs_nrates] = 0; - res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; - res->wi_rsvd = 0; - res++, i++; - } - p2->wi_rsvd = 0; - p2->wi_reason = i; - wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2; - break; - case WI_RID_READ_CACHE: - i = 0; - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1) - break; - IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); - memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); - wsc.signal = (*ic->ic_node_getrssi)(ic, ni); - wsc.noise = 0; - wsc.quality = 0; - memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, - &wsc, sizeof(wsc)); - i++; - } - wreq.wi_len = sizeof(wsc) * i / 2; break; - case WI_RID_SCAN_APS: - error = EINVAL; + case WI_RID_READ_CACHE: { + struct wi_read_sigcache_args args; + args.i = 0; + args.wsc = (struct wi_sigcache *) wreq.wi_val; + args.max = (void *)(&wreq + 1); + ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); + wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; break; + } default: error = EINVAL; break; @@ -399,25 +450,9 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) * the active list as the place to start the scan. */ static int -ieee80211_setupscan(struct ieee80211com *ic) +ieee80211_setupscan(struct ieee80211com *ic, const uint8_t chanlist[]) { - u_char *chanlist = ic->ic_chan_active; - int i; - if (ic->ic_ibss_chan == NULL || - isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) - if (isset(chanlist, i)) { - ic->ic_ibss_chan = &ic->ic_channels[i]; - goto found; - } - return EINVAL; /* no active channels */ -found: - ; - } - if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || - isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) - ic->ic_bss->ni_chan = ic->ic_ibss_chan; /* * XXX don't permit a scan to be started unless we * know the device is ready. For the moment this means @@ -426,13 +461,23 @@ found: * scanning prior to being up but that'll require some * changes to the infrastructure. */ - return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET; + if (!IS_UP(ic)) + return EINVAL; + memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); + /* + * We force the state to INIT before calling ieee80211_new_state + * to get ieee80211_begin_scan called. We really want to scan w/o + * altering the current state but that's not possible right now. + */ + /* XXX handle proberequest case */ + ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ + return 0; } int -ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) +ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; int i, j, len, error, rate; struct ifreq *ifr = (struct ifreq *)data; struct wi_ltv_keys *keys; @@ -472,7 +517,9 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) isclr(ic->ic_chan_active, i)) return EINVAL; ic->ic_ibss_chan = &ic->ic_channels[i]; - if (ic->ic_flags & IEEE80211_F_SIBSS) + if (ic->ic_opmode == IEEE80211_M_MONITOR) + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + else error = ENETRESET; break; case WI_RID_CURRENT_CHAN: @@ -518,7 +565,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) } if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { ic->ic_opmode = le16toh(wreq.wi_val[0]); - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } break; #if 0 @@ -535,7 +582,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; if (wreq.wi_val[0] == 0) { /* auto */ - ic->ic_fixed_rate = -1; + ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; break; } rate = 2 * le16toh(wreq.wi_val[0]); @@ -566,7 +613,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; setrate: ic->ic_fixed_rate = i; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_CUR_TX_RATE: return EPERM; @@ -586,14 +633,14 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) ic->ic_flags |= IEEE80211_F_IBSSON; if (ic->ic_opmode == IEEE80211_M_IBSS && ic->ic_state == IEEE80211_S_SCAN) - error = ENETRESET; + error = IS_UP_AUTO(ic) ? ENETRESET : 0; } } else { if (ic->ic_flags & IEEE80211_F_IBSSON) { ic->ic_flags &= ~IEEE80211_F_IBSSON; if (ic->ic_flags & IEEE80211_F_SIBSS) { ic->ic_flags &= ~IEEE80211_F_SIBSS; - error = ENETRESET; + error = IS_UP_AUTO(ic) ? ENETRESET : 0; } } } @@ -607,8 +654,10 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_ROAMING_MODE: if (len != 2) return EINVAL; - if (le16toh(wreq.wi_val[0]) != 1) + i = le16toh(wreq.wi_val[0]); + if (i > IEEE80211_ROAMING_MANUAL) return EINVAL; /* not supported */ + ic->ic_roaming = i; break; case WI_RID_SYSTEM_SCALE: if (len != 2) @@ -624,12 +673,12 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { ic->ic_flags |= IEEE80211_F_PMGTON; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } } else { if (ic->ic_flags & IEEE80211_F_PMGTON) { ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } } break; @@ -638,7 +687,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; ic->ic_lintval = le16toh(wreq.wi_val[0]); if (ic->ic_flags & IEEE80211_F_PMGTON) - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_CUR_BEACON_INT: return EPERM; @@ -647,8 +696,11 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_CNFAUTHMODE: if (len != 2) return EINVAL; - if (le16toh(wreq.wi_val[0]) != 1) - return EINVAL; /* TODO: shared key auth */ + i = le16toh(wreq.wi_val[0]); + if (i > IEEE80211_AUTH_WPA) + return EINVAL; + ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ + error = ENETRESET; break; case WI_RID_ENCRYPTION: if (len != 2) @@ -656,13 +708,13 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) if (wreq.wi_val[0] != 0) { if ((ic->ic_caps & IEEE80211_C_WEP) == 0) return EINVAL; - if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { - ic->ic_flags |= IEEE80211_F_WEPON; + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + ic->ic_flags |= IEEE80211_F_PRIVACY; error = ENETRESET; } } else { - if (ic->ic_flags & IEEE80211_F_WEPON) { - ic->ic_flags &= ~IEEE80211_F_WEPON; + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + ic->ic_flags &= ~IEEE80211_F_PRIVACY; error = ENETRESET; } } @@ -673,7 +725,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) i = le16toh(wreq.wi_val[0]); if (i >= IEEE80211_WEP_NKID) return EINVAL; - ic->ic_wep_txkey = i; + ic->ic_def_txkey = i; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_DEFLT_CRYPT_KEYS: if (len != sizeof(struct wi_ltv_keys)) @@ -683,15 +736,20 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) len = le16toh(keys->wi_keys[i].wi_keylen); if (len != 0 && len < IEEE80211_WEP_KEYLEN) return EINVAL; - if (len > sizeof(ic->ic_nw_keys[i].wk_key)) + if (len > IEEE80211_KEYBUF_SIZE) return EINVAL; } - memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); for (i = 0; i < IEEE80211_WEP_NKID; i++) { + struct ieee80211_key *k = &ic->ic_nw_keys[i]; + len = le16toh(keys->wi_keys[i].wi_keylen); - ic->ic_nw_keys[i].wk_len = len; - memcpy(ic->ic_nw_keys[i].wk_key, - keys->wi_keys[i].wi_keydat, len); + k->wk_keylen = len; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + memset(k->wk_key, 0, sizeof(k->wk_key)); + memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); +#if 0 + k->wk_type = IEEE80211_CIPHER_WEP; +#endif } error = ENETRESET; break; @@ -701,10 +759,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) len = le16toh(wreq.wi_val[0]); if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) return EINVAL; - if (len != IEEE80211_MAX_LEN) - return EINVAL; /* TODO: fragment */ ic->ic_fragthreshold = len; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_IFACE_STATS: error = EPERM; @@ -712,7 +768,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_SCAN_REQ: /* XXX wicontrol */ if (ic->ic_opmode == IEEE80211_M_HOSTAP) break; - error = ieee80211_setupscan(ic); + error = ieee80211_setupscan(ic, ic->ic_chan_avail); if (error == 0) error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; @@ -744,9 +800,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) } setbit(chanlist, i); } - memcpy(ic->ic_chan_active, chanlist, - sizeof(ic->ic_chan_active)); - error = ieee80211_setupscan(ic); + error = ieee80211_setupscan(ic, chanlist); if (wreq.wi_type == WI_RID_CHANNEL_LIST) { /* NB: ignore error from ieee80211_setupscan */ error = ENETRESET; @@ -757,311 +811,1665 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) error = EINVAL; break; } + if (error == ENETRESET && !IS_UP_AUTO(ic)) + error = 0; return error; } -int -ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) +static int +cap2cipher(int flag) +{ + switch (flag) { + case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; + case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; + case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; + case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; + case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; + } + return -1; +} + +static int +ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq, + struct ucred *cr) +{ + struct ieee80211_node *ni; + struct ieee80211req_key ik; + struct ieee80211_key *wk; + const struct ieee80211_cipher *cip; + u_int kid; + int error; + + if (ireq->i_len != sizeof(ik)) + return EINVAL; + error = copyin(ireq->i_data, &ik, sizeof(ik)); + if (error) + return error; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + wk = &ic->ic_nw_keys[kid]; + IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); + ni = NULL; + } + cip = wk->wk_cipher; + ik.ik_type = cip->ic_cipher; + ik.ik_keylen = wk->wk_keylen; + ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); + if (wk->wk_keyix == ic->ic_def_txkey) + ik.ik_flags |= IEEE80211_KEY_DEFAULT; + if (suser_cred(cr, NULL_CRED_OKAY) == 0) { + /* NB: only root can read key data */ + ik.ik_keyrsc = wk->wk_keyrsc; + ik.ik_keytsc = wk->wk_keytsc; + memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { + memcpy(ik.ik_keydata+wk->wk_keylen, + wk->wk_key + IEEE80211_KEYBUF_SIZE, + IEEE80211_MICBUF_SIZE); + ik.ik_keylen += IEEE80211_MICBUF_SIZE; + } + } else { + ik.ik_keyrsc = 0; + ik.ik_keytsc = 0; + memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); + } + if (ni != NULL) + ieee80211_free_node(ni); + return copyout(&ik, ireq->i_data, sizeof(ik)); +} + +static int +ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + + if (sizeof(ic->ic_chan_active) < ireq->i_len) + ireq->i_len = sizeof(ic->ic_chan_active); + return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); +} + +static int +ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_chaninfo chans; /* XXX off stack? */ + int i, space; + + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + memset(&chans, 0, sizeof(chans)); + for (; i <= IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_avail, i)) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; + chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; + chans.ic_nchans++; + } + space = __offsetof(struct ieee80211req_chaninfo, + ic_chans[chans.ic_nchans]); + if (space > ireq->i_len) + space = ireq->i_len; + return copyout(&chans, ireq->i_data, space); +} + +static int +ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_wpaie wpaie; + int error; + + if (ireq->i_len < IEEE80211_ADDR_LEN) + return EINVAL; + error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); + if (ni->ni_wpa_ie != NULL) { + int ielen = ni->ni_wpa_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); + } + ieee80211_free_node(ni); + if (ireq->i_len > sizeof(wpaie)) + ireq->i_len = sizeof(wpaie); + return copyout(&wpaie, ireq->i_data, ireq->i_len); +} + +static int +ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); + int error; + + if (ireq->i_len < off) + return EINVAL; + error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + ni = ieee80211_find_node(&ic->ic_sta, macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) + ireq->i_len = sizeof(struct ieee80211req_sta_stats); + /* NB: copy out only the statistics */ + error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off, + ireq->i_len - off); + ieee80211_free_node(ni); + return error; +} + +static void +get_scan_result(struct ieee80211req_scan_result *sr, + const struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + u_int ielen = 0; + + memset(sr, 0, sizeof(*sr)); + sr->isr_ssid_len = ni->ni_esslen; + if (ni->ni_wpa_ie != NULL) + ielen += 2+ni->ni_wpa_ie[1]; + if (ni->ni_wme_ie != NULL) + ielen += 2+ni->ni_wme_ie[1]; + + /* + * The value sr->isr_ie_len is defined as a uint8_t, so we + * need to be careful to avoid an integer overflow. If the + * value would overflow, we will set isr_ie_len to zero, and + * ieee80211_ioctl_getscanresults (below) will avoid copying + * the (overflowing) data. + */ + if (ielen > 255) + ielen = 0; + sr->isr_ie_len = ielen; + sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; + sr->isr_len = roundup(sr->isr_len, sizeof(uint32_t)); + if (ni->ni_chan != IEEE80211_CHAN_ANYC) { + sr->isr_freq = ni->ni_chan->ic_freq; + sr->isr_flags = ni->ni_chan->ic_flags; + } + sr->isr_rssi = ic->ic_node_getrssi(ni); + sr->isr_intval = ni->ni_intval; + sr->isr_capinfo = ni->ni_capinfo; + sr->isr_erp = ni->ni_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); + sr->isr_nrates = ni->ni_rates.rs_nrates; + if (sr->isr_nrates > 15) + sr->isr_nrates = 15; + memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); +} + +static int +ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + union { + struct ieee80211req_scan_result res; + char data[512]; /* XXX shrink? */ + } u; + struct ieee80211req_scan_result *sr = &u.res; + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + int error, space; + uint8_t *p, *cp; + + p = ireq->i_data; + space = ireq->i_len; + error = 0; + /* XXX locking */ + nt = &ic->ic_scan; + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + /* NB: skip pre-scan node state */ + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + continue; + get_scan_result(sr, ni); + if (sr->isr_len > sizeof(u)) + continue; /* XXX */ + if (space < sr->isr_len) + break; + cp = (uint8_t *)(sr+1); + memcpy(cp, ni->ni_essid, ni->ni_esslen); + cp += ni->ni_esslen; + if (sr->isr_ie_len > 0 && ni->ni_wpa_ie != NULL) { + memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); + cp += 2+ni->ni_wpa_ie[1]; + } + if (sr->isr_ie_len > 0 && ni->ni_wme_ie != NULL) { + memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); + cp += 2+ni->ni_wme_ie[1]; + } + error = copyout(sr, p, sr->isr_len); + if (error) + break; + p += sr->isr_len; + space -= sr->isr_len; + } + ireq->i_len -= space; + return error; +} + +struct stainforeq { + struct ieee80211com *ic; + struct ieee80211req_sta_info *si; + size_t space; +}; + +static size_t +sta_space(const struct ieee80211_node *ni, size_t *ielen) +{ + *ielen = 0; + if (ni->ni_wpa_ie != NULL) + *ielen += 2+ni->ni_wpa_ie[1]; + if (ni->ni_wme_ie != NULL) + *ielen += 2+ni->ni_wme_ie[1]; + return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, + sizeof(uint32_t)); +} + +static void +get_sta_space(void *arg, struct ieee80211_node *ni) +{ + struct stainforeq *req = arg; + struct ieee80211com *ic = ni->ni_ic; + size_t ielen; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ni->ni_associd == 0) /* only associated stations */ + return; + req->space += sta_space(ni, &ielen); +} + +static void +get_sta_info(void *arg, struct ieee80211_node *ni) +{ + struct stainforeq *req = arg; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211req_sta_info *si; + size_t ielen, len; + uint8_t *cp; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ni->ni_associd == 0) /* only associated stations */ + return; + if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ + return; + len = sta_space(ni, &ielen); + if (len > req->space) + return; + si = req->si; + si->isi_len = len; + si->isi_ie_len = ielen; + si->isi_freq = ni->ni_chan->ic_freq; + si->isi_flags = ni->ni_chan->ic_flags; + si->isi_state = ni->ni_flags; + si->isi_authmode = ni->ni_authmode; + si->isi_rssi = ic->ic_node_getrssi(ni); + si->isi_capinfo = ni->ni_capinfo; + si->isi_erp = ni->ni_erp; + IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); + si->isi_nrates = ni->ni_rates.rs_nrates; + if (si->isi_nrates > 15) + si->isi_nrates = 15; + memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); + si->isi_txrate = ni->ni_txrate; + si->isi_associd = ni->ni_associd; + si->isi_txpower = ni->ni_txpower; + si->isi_vlan = ni->ni_vlan; + if (ni->ni_flags & IEEE80211_NODE_QOS) { + memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); + memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); + } else { + si->isi_txseqs[0] = ni->ni_txseqs[0]; + si->isi_rxseqs[0] = ni->ni_rxseqs[0]; + } + /* NB: leave all cases in case we relax ni_associd == 0 check */ + if (ieee80211_node_is_authorized(ni)) + si->isi_inact = ic->ic_inact_run; + else if (ni->ni_associd != 0) + si->isi_inact = ic->ic_inact_auth; + else + si->isi_inact = ic->ic_inact_init; + si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; + + cp = (uint8_t *)(si+1); + if (ni->ni_wpa_ie != NULL) { + memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); + cp += 2+ni->ni_wpa_ie[1]; + } + if (ni->ni_wme_ie != NULL) { + memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); + cp += 2+ni->ni_wme_ie[1]; + } + + req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len); + req->space -= len; +} + +static int +ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct stainforeq req; + int error; + + if (ireq->i_len < sizeof(struct stainforeq)) + return EFAULT; + + error = 0; + req.space = 0; + ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req); + if (req.space > ireq->i_len) + req.space = ireq->i_len; + if (req.space > 0) { + size_t space; + void *p; + + space = req.space; + /* XXX M_WAITOK after driver lock released */ + p = malloc(space, M_TEMP, M_NOWAIT); + if (p == NULL) + return ENOMEM; + req.si = p; + ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req); + ireq->i_len = space - req.space; + error = copyout(p, ireq->i_data, ireq->i_len); + free(p, M_TEMP); + } else + ireq->i_len = 0; + + return error; +} + +static int +ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_txpow txpow; + int error; + + if (ireq->i_len != sizeof(txpow)) + return EINVAL; + error = copyin(ireq->i_data, &txpow, sizeof(txpow)); + if (error != 0) + return error; + ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + txpow.it_txpow = ni->ni_txpower; + error = copyout(&txpow, ireq->i_data, sizeof(txpow)); + ieee80211_free_node(ni); + return error; +} + +static int +ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_wme_state *wme = &ic->ic_wme; + struct wmeParams *wmep; + int ac; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + + ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); + if (ac >= WME_NUM_AC) + ac = WME_AC_BE; + if (ireq->i_len & IEEE80211_WMEPARAM_BSS) + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + else + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + switch (ireq->i_type) { + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + ireq->i_val = wmep->wmep_logcwmin; + break; + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + ireq->i_val = wmep->wmep_logcwmax; + break; + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + ireq->i_val = wmep->wmep_aifsn; + break; + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + ireq->i_val = wmep->wmep_txopLimit; + break; + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + ireq->i_val = wmep->wmep_acm; + break; + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + ireq->i_val = !wmep->wmep_noackPolicy; + break; + } + return 0; +} + +static int +ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + const struct ieee80211_aclator *acl = ic->ic_acl; + + return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq)); +} + +/* + * When building the kernel with -O2 on the i386 architecture, gcc + * seems to want to inline this function into ieee80211_ioctl() + * (which is the only routine that calls it). When this happens, + * ieee80211_ioctl() ends up consuming an additional 2K of stack + * space. (Exactly why it needs so much is unclear.) The problem + * is that it's possible for ieee80211_ioctl() to invoke other + * routines (including driver init functions) which could then find + * themselves perilously close to exhausting the stack. + * + * To avoid this, we deliberately prevent gcc from inlining this + * routine. Another way to avoid this is to use less agressive + * optimization when compiling this file (i.e. -O instead of -O2) + * but special-casing the compilation of this one module in the + * build system would be awkward. + */ +#ifdef __GNUC__ +__attribute__ ((noinline)) +#endif +static int +ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, + struct ieee80211req *ireq, struct ucred *cr) { - struct ieee80211com *ic = (void *)ifp; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; int error = 0; - u_int kid, len; - struct ieee80211req *ireq; - struct ifreq *ifr; + u_int kid, len, m; uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; - struct ieee80211_channel *chan; - struct ifaddr *ifa; /* XXX */ - switch (cmd) { - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, (struct ifreq *) data, - &ic->ic_media, cmd); - break; - case SIOCG80211: - ireq = (struct ieee80211req *) data; - switch (ireq->i_type) { - case IEEE80211_IOC_SSID: - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - ireq->i_len = ic->ic_des_esslen; - memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); - break; - default: - ireq->i_len = ic->ic_bss->ni_esslen; - memcpy(tmpssid, ic->ic_bss->ni_essid, - ireq->i_len); - break; - } - error = copyout(tmpssid, ireq->i_data, ireq->i_len); + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + ireq->i_len = ic->ic_des_esslen; + memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); + break; + default: + ireq->i_len = ic->ic_bss->ni_esslen; + memcpy(tmpssid, ic->ic_bss->ni_essid, + ireq->i_len); break; - case IEEE80211_IOC_NUMSSIDS: + } + error = copyout(tmpssid, ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + case IEEE80211_IOC_WEP: + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) + ireq->i_val = IEEE80211_WEP_OFF; + else if (ic->ic_flags & IEEE80211_F_DROPUNENC) + ireq->i_val = IEEE80211_WEP_ON; + else + ireq->i_val = IEEE80211_WEP_MIXED; + break; + case IEEE80211_IOC_WEPKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + len = (u_int) ic->ic_nw_keys[kid].wk_keylen; + /* NB: only root can read WEP keys */ + if (suser_cred(cr, NULL_CRED_OKAY) == 0) { + bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); + } else { + bzero(tmpkey, len); + } + ireq->i_len = len; + error = copyout(tmpkey, ireq->i_data, len); + break; + case IEEE80211_IOC_NUMWEPKEYS: + ireq->i_val = IEEE80211_WEP_NKID; + break; + case IEEE80211_IOC_WEPTXKEY: + ireq->i_val = ic->ic_def_txkey; + break; + case IEEE80211_IOC_AUTHMODE: + if (ic->ic_flags & IEEE80211_F_WPA) + ireq->i_val = IEEE80211_AUTH_WPA; + else + ireq->i_val = ic->ic_bss->ni_authmode; + break; + case IEEE80211_IOC_CHANNEL: + ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan); + break; + case IEEE80211_IOC_POWERSAVE: + if (ic->ic_flags & IEEE80211_F_PMGTON) + ireq->i_val = IEEE80211_POWERSAVE_ON; + else + ireq->i_val = IEEE80211_POWERSAVE_OFF; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ireq->i_val = ic->ic_lintval; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + ireq->i_val = ic->ic_rtsthreshold; + break; + case IEEE80211_IOC_PROTMODE: + ireq->i_val = ic->ic_protmode; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return EINVAL; + ireq->i_val = ic->ic_txpowlimit; + break; + case IEEE80211_IOC_MCASTCIPHER: + ireq->i_val = rsn->rsn_mcastcipher; + break; + case IEEE80211_IOC_MCASTKEYLEN: + ireq->i_val = rsn->rsn_mcastkeylen; + break; + case IEEE80211_IOC_UCASTCIPHERS: + ireq->i_val = 0; + for (m = 0x1; m != 0; m <<= 1) + if (rsn->rsn_ucastcipherset & m) + ireq->i_val |= 1<i_val = rsn->rsn_ucastcipher; + break; + case IEEE80211_IOC_UCASTKEYLEN: + ireq->i_val = rsn->rsn_ucastkeylen; + break; + case IEEE80211_IOC_KEYMGTALGS: + ireq->i_val = rsn->rsn_keymgmtset; + break; + case IEEE80211_IOC_RSNCAPS: + ireq->i_val = rsn->rsn_caps; + break; + case IEEE80211_IOC_WPA: + switch (ic->ic_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: ireq->i_val = 1; break; - case IEEE80211_IOC_WEP: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { - ireq->i_val = IEEE80211_WEP_NOSUP; - } else { - if (ic->ic_flags & IEEE80211_F_WEPON) { - ireq->i_val = - IEEE80211_WEP_MIXED; - } else { - ireq->i_val = - IEEE80211_WEP_OFF; - } - } + case IEEE80211_F_WPA2: + ireq->i_val = 2; break; - case IEEE80211_IOC_WEPKEY: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { - error = EINVAL; - break; + case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: + ireq->i_val = 3; + break; + default: + ireq->i_val = 0; + break; + } + break; + case IEEE80211_IOC_CHANLIST: + error = ieee80211_ioctl_getchanlist(ic, ireq); + break; + case IEEE80211_IOC_ROAMING: + ireq->i_val = ic->ic_roaming; + break; + case IEEE80211_IOC_PRIVACY: + ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; + break; + case IEEE80211_IOC_DROPUNENCRYPTED: + ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; + break; + case IEEE80211_IOC_COUNTERMEASURES: + ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; + break; + case IEEE80211_IOC_DRIVER_CAPS: + ireq->i_val = ic->ic_caps>>16; + ireq->i_len = ic->ic_caps&0xffff; + break; + case IEEE80211_IOC_WME: + ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; + break; + case IEEE80211_IOC_HIDESSID: + ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; + break; + case IEEE80211_IOC_APBRIDGE: + ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; + break; + case IEEE80211_IOC_OPTIE: + if (ic->ic_opt_ie == NULL) + return EINVAL; + /* NB: truncate, caller can check length */ + if (ireq->i_len > ic->ic_opt_ie_len) + ireq->i_len = ic->ic_opt_ie_len; + error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_WPAKEY: + error = ieee80211_ioctl_getkey(ic, ireq, cr); + break; + case IEEE80211_IOC_CHANINFO: + error = ieee80211_ioctl_getchaninfo(ic, ireq); + break; + case IEEE80211_IOC_BSSID: + if (ireq->i_len != IEEE80211_ADDR_LEN) + return EINVAL; + error = copyout(ic->ic_state == IEEE80211_S_RUN ? + ic->ic_bss->ni_bssid : + ic->ic_des_bssid, + ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_WPAIE: + error = ieee80211_ioctl_getwpaie(ic, ireq); + break; + case IEEE80211_IOC_SCAN_RESULTS: + error = ieee80211_ioctl_getscanresults(ic, ireq); + break; + case IEEE80211_IOC_STA_STATS: + error = ieee80211_ioctl_getstastats(ic, ireq); + break; + case IEEE80211_IOC_TXPOWMAX: + ireq->i_val = ic->ic_bss->ni_txpower; + break; + case IEEE80211_IOC_STA_TXPOW: + error = ieee80211_ioctl_getstatxpow(ic, ireq); + break; + case IEEE80211_IOC_STA_INFO: + error = ieee80211_ioctl_getstainfo(ic, ireq); + break; + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ + error = ieee80211_ioctl_getwmeparam(ic, ireq); + break; + case IEEE80211_IOC_DTIM_PERIOD: + ireq->i_val = ic->ic_dtim_period; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + /* NB: get from ic_bss for station mode */ + ireq->i_val = ic->ic_bss->ni_intval; + break; + case IEEE80211_IOC_PUREG: + ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; + break; + case IEEE80211_IOC_MCAST_RATE: + ireq->i_val = ic->ic_mcast_rate; + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + ireq->i_val = ic->ic_fragthreshold; + break; + case IEEE80211_IOC_MACCMD: + error = ieee80211_ioctl_getmaccmd(ic, ireq); + break; + case IEEE80211_IOC_BURST: + ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0; + break; + default: + error = EINVAL; + break; + } + return error; +} + +static int +ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + int error; + void *ie, *oie; + + /* + * NB: Doing this for ap operation could be useful (e.g. for + * WPA and/or WME) except that it typically is worthless + * without being able to intervene when processing + * association response frames--so disallow it for now. + */ + if (ic->ic_opmode != IEEE80211_M_STA) + return EINVAL; + if (ireq->i_len > IEEE80211_MAX_OPT_IE) + return EINVAL; + if (ireq->i_len > 0) { + ie = malloc(ireq->i_len, M_DEVBUF, M_NOWAIT); + if (ie == NULL) + return ENOMEM; + error = copyin(ireq->i_data, ie, ireq->i_len); + if (error) { + free(ie, M_DEVBUF); + return error; + } + } else { + ie = NULL; + ireq->i_len = 0; + } + /* XXX sanity check data? */ + oie = ic->ic_opt_ie; + ic->ic_opt_ie = ie; + ic->ic_opt_ie_len = ireq->i_len; + if (oie != NULL) + free(oie, M_DEVBUF); + return 0; +} + +static int +ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_key ik; + struct ieee80211_node *ni; + struct ieee80211_key *wk; + uint16_t kid; + int error; + + if (ireq->i_len != sizeof(ik)) + return EINVAL; + error = copyin(ireq->i_data, &ik, sizeof(ik)); + if (error) + return error; + /* NB: cipher support is verified by ieee80211_crypt_newkey */ + /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ + if (ik.ik_keylen > sizeof(ik.ik_keydata)) + return E2BIG; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + /* XXX unicast keys currently must be tx/rx */ + if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) + return EINVAL; + if (ic->ic_opmode == IEEE80211_M_STA) { + ni = ieee80211_ref_node(ic->ic_bss); + if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) { + ieee80211_free_node(ni); + return EADDRNOTAVAIL; } - kid = (u_int) ireq->i_val; - if (kid >= IEEE80211_WEP_NKID) { - error = EINVAL; - break; + } else { + ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); + if (ni == NULL) + return ENOENT; + } + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + wk = &ic->ic_nw_keys[kid]; + /* + * Global slots start off w/o any assigned key index. + * Force one here for consistency with IEEE80211_IOC_WEPKEY. + */ + if (wk->wk_keyix == IEEE80211_KEYIX_NONE) + wk->wk_keyix = kid; + ni = NULL; + } + error = 0; + ieee80211_key_update_begin(ic); + if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) { + wk->wk_keylen = ik.ik_keylen; + /* NB: MIC presence is implied by cipher type */ + if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) + wk->wk_keylen = IEEE80211_KEYBUF_SIZE; + wk->wk_keyrsc = ik.ik_keyrsc; + wk->wk_keytsc = 0; /* new key, reset */ + memset(wk->wk_key, 0, sizeof(wk->wk_key)); + memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); + if (!ieee80211_crypto_setkey(ic, wk, + ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) + error = EIO; + else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) + ic->ic_def_txkey = kid; + } else + error = ENXIO; + ieee80211_key_update_end(ic); + if (ni != NULL) + ieee80211_free_node(ni); + return error; +} + +static int +ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_del_key dk; + int kid, error; + + if (ireq->i_len != sizeof(dk)) + return EINVAL; + error = copyin(ireq->i_data, &dk, sizeof(dk)); + if (error) + return error; + kid = dk.idk_keyix; + /* XXX uint8_t -> uint16_t */ + if (dk.idk_keyix == (uint8_t)IEEE80211_KEYIX_NONE) { + struct ieee80211_node *ni; + + if (ic->ic_opmode == IEEE80211_M_STA) { + ni = ieee80211_ref_node(ic->ic_bss); + if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) { + ieee80211_free_node(ni); + return EADDRNOTAVAIL; } - len = (u_int) ic->ic_nw_keys[kid].wk_len; - /* NB: only root can read WEP keys */ - if (suser_cred(cr, NULL_CRED_OKAY) == 0) { - bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); + } else { + ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr); + if (ni == NULL) + return ENOENT; + } + /* XXX error return */ + ieee80211_node_delucastkey(ni); + ieee80211_free_node(ni); + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + /* XXX error return */ + ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); + } + return 0; +} + +static void +domlme(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211req_mlme *mlme = arg; + + if (ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ic, ni, + mlme->im_op == IEEE80211_MLME_DEAUTH ? + IEEE80211_FC0_SUBTYPE_DEAUTH : + IEEE80211_FC0_SUBTYPE_DISASSOC, + mlme->im_reason); + } + ieee80211_node_leave(ic, ni); +} + +static int +ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_mlme mlme; + struct ieee80211_node *ni; + int error; + + if (ireq->i_len != sizeof(mlme)) + return EINVAL; + error = copyin(ireq->i_data, &mlme, sizeof(mlme)); + if (error) + return error; + switch (mlme.im_op) { + case IEEE80211_MLME_ASSOC: + if (ic->ic_opmode != IEEE80211_M_STA) + return EINVAL; + /* XXX must be in S_SCAN state? */ + + if (mlme.im_ssid_len != 0) { + /* + * Desired ssid specified; must match both bssid and + * ssid to distinguish ap advertising multiple ssid's. + */ + ni = ieee80211_find_node_with_ssid(&ic->ic_scan, + mlme.im_macaddr, + mlme.im_ssid_len, mlme.im_ssid); + } else { + /* + * Normal case; just match bssid. + */ + ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); + } + if (ni == NULL) + return EINVAL; + if (!ieee80211_sta_join(ic, ni)) { + ieee80211_free_node(ni); + return EINVAL; + } + break; + case IEEE80211_MLME_DISASSOC: + case IEEE80211_MLME_DEAUTH: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + /* XXX not quite right */ + ieee80211_new_state(ic, IEEE80211_S_INIT, + mlme.im_reason); + break; + case IEEE80211_M_HOSTAP: + /* NB: the broadcast address means do 'em all */ + if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { + if ((ni = ieee80211_find_node(&ic->ic_sta, + mlme.im_macaddr)) == NULL) + return EINVAL; + domlme(&mlme, ni); + ieee80211_free_node(ni); } else { - bzero(tmpkey, len); + ieee80211_iterate_nodes(&ic->ic_sta, + domlme, &mlme); } - ireq->i_len = len; - error = copyout(tmpkey, ireq->i_data, len); break; - case IEEE80211_IOC_NUMWEPKEYS: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) - error = EINVAL; - else - ireq->i_val = IEEE80211_WEP_NKID; - break; - case IEEE80211_IOC_WEPTXKEY: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) - error = EINVAL; - else - ireq->i_val = ic->ic_wep_txkey; - break; - case IEEE80211_IOC_AUTHMODE: - ireq->i_val = IEEE80211_AUTH_OPEN; - break; - case IEEE80211_IOC_CHANNEL: - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - if (ic->ic_opmode == IEEE80211_M_STA) - chan = ic->ic_des_chan; - else - chan = ic->ic_ibss_chan; - break; - default: - chan = ic->ic_bss->ni_chan; - break; + default: + return EINVAL; + } + break; + case IEEE80211_MLME_AUTHORIZE: + case IEEE80211_MLME_UNAUTHORIZE: + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + return EINVAL; + ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr); + if (ni == NULL) + return EINVAL; + if (mlme.im_op == IEEE80211_MLME_AUTHORIZE) + ieee80211_node_authorize(ni); + else + ieee80211_node_unauthorize(ni); + ieee80211_free_node(ni); + break; + default: + return EINVAL; + } + return 0; +} + +static int +ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + uint8_t mac[IEEE80211_ADDR_LEN]; + const struct ieee80211_aclator *acl = ic->ic_acl; + int error; + + if (ireq->i_len != sizeof(mac)) + return EINVAL; + error = copyin(ireq->i_data, mac, ireq->i_len); + if (error) + return error; + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return EINVAL; + ic->ic_acl = acl; + } + if (ireq->i_type == IEEE80211_IOC_ADDMAC) + acl->iac_add(ic, mac); + else + acl->iac_remove(ic, mac); + return 0; +} + +static int +ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + const struct ieee80211_aclator *acl = ic->ic_acl; + + switch (ireq->i_val) { + case IEEE80211_MACCMD_POLICY_OPEN: + case IEEE80211_MACCMD_POLICY_ALLOW: + case IEEE80211_MACCMD_POLICY_DENY: + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return EINVAL; + ic->ic_acl = acl; + } + acl->iac_setpolicy(ic, ireq->i_val); + break; + case IEEE80211_MACCMD_FLUSH: + if (acl != NULL) + acl->iac_flush(ic); + /* NB: silently ignore when not in use */ + break; + case IEEE80211_MACCMD_DETACH: + if (acl != NULL) { + ic->ic_acl = NULL; + acl->iac_detach(ic); + } + break; + default: + if (acl == NULL) + return EINVAL; + else + return acl->iac_setioctl(ic, ireq); + } + return 0; +} + +static int +ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_chanlist list; + u_char chanlist[IEEE80211_CHAN_BYTES]; + int i, j, error; + + if (ireq->i_len != sizeof(list)) + return EINVAL; + error = copyin(ireq->i_data, &list, sizeof(list)); + if (error) + return error; + memset(chanlist, 0, sizeof(chanlist)); + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { + /* + * NB: silently discard unavailable channels so users + * can specify 1-255 to get all available channels. + */ + if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) + setbit(chanlist, i); + } + if (ic->ic_ibss_chan == NULL || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(chanlist, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + goto found; } - ireq->i_val = ieee80211_chan2ieee(ic, chan); - break; - case IEEE80211_IOC_POWERSAVE: - if (ic->ic_flags & IEEE80211_F_PMGTON) - ireq->i_val = IEEE80211_POWERSAVE_ON; - else - ireq->i_val = IEEE80211_POWERSAVE_OFF; - break; - case IEEE80211_IOC_POWERSAVESLEEP: - ireq->i_val = ic->ic_lintval; - break; - case IEEE80211_IOC_RTSTHRESHOLD: - ireq->i_val = ic->ic_rtsthreshold; + return EINVAL; /* no active channels */ +found: + ; + } + memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); + return IS_UP_AUTO(ic) ? ENETRESET : 0; +} + +static int +ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_txpow txpow; + int error; + + if (ireq->i_len != sizeof(txpow)) + return EINVAL; + error = copyin(ireq->i_data, &txpow, sizeof(txpow)); + if (error != 0) + return error; + ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + ni->ni_txpower = txpow.it_txpow; + ieee80211_free_node(ni); + return error; +} + +static int +ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_wme_state *wme = &ic->ic_wme; + struct wmeParams *wmep, *chanp; + int isbss, ac; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + + isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); + ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); + if (ac >= WME_NUM_AC) + ac = WME_AC_BE; + if (isbss) { + chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + } else { + chanp = &wme->wme_chanParams.cap_wmeParams[ac]; + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + } + switch (ireq->i_type) { + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + if (isbss) { + wmep->wmep_logcwmin = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_logcwmin = ireq->i_val; + } else { + wmep->wmep_logcwmin = chanp->wmep_logcwmin = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + if (isbss) { + wmep->wmep_logcwmax = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_logcwmax = ireq->i_val; + } else { + wmep->wmep_logcwmax = chanp->wmep_logcwmax = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + if (isbss) { + wmep->wmep_aifsn = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_aifsn = ireq->i_val; + } else { + wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; + } + break; + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + if (isbss) { + wmep->wmep_txopLimit = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_txopLimit = ireq->i_val; + } else { + wmep->wmep_txopLimit = chanp->wmep_txopLimit = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + wmep->wmep_acm = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_acm = ireq->i_val; + break; + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ + wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = + (ireq->i_val) == 0; + break; + } + ieee80211_wme_updateparams(ic); + return 0; +} + +static int +cipher2cap(int cipher) +{ + switch (cipher) { + case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; + case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; + case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; + case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; + case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; + } + return 0; +} + +static int +ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) +{ + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + int error; + const struct ieee80211_authenticator *auth; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + char tmpssid[IEEE80211_NWID_LEN]; + uint8_t tmpbssid[IEEE80211_ADDR_LEN]; + struct ieee80211_key *k; + int j, caps; + u_int kid; + + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) + return EINVAL; + error = copyin(ireq->i_data, tmpssid, ireq->i_len); + if (error) break; - case IEEE80211_IOC_PROTMODE: - ireq->i_val = ic->ic_protmode; + memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); + ic->ic_des_esslen = ireq->i_len; + memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); + error = ENETRESET; + break; + case IEEE80211_IOC_WEP: + switch (ireq->i_val) { + case IEEE80211_WEP_OFF: + ic->ic_flags &= ~IEEE80211_F_PRIVACY; + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; - case IEEE80211_IOC_TXPOWER: - if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) - error = EINVAL; - else - ireq->i_val = ic->ic_txpower; + case IEEE80211_WEP_ON: + ic->ic_flags |= IEEE80211_F_PRIVACY; + ic->ic_flags |= IEEE80211_F_DROPUNENC; break; - default: - error = EINVAL; + case IEEE80211_WEP_MIXED: + ic->ic_flags |= IEEE80211_F_PRIVACY; + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; } + error = ENETRESET; break; - case SIOCS80211: - error = suser_cred(cr, NULL_CRED_OKAY); + case IEEE80211_IOC_WEPKEY: + kid = (u_int)ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + k = &ic->ic_nw_keys[kid]; + if (ireq->i_len == 0) { + /* zero-len =>'s delete any existing key */ + ieee80211_crypto_delkey(ic, k); + break; + } + if (ireq->i_len > sizeof(tmpkey)) + return EINVAL; + memset(tmpkey, 0, sizeof(tmpkey)); + error = copyin(ireq->i_data, tmpkey, ireq->i_len); if (error) break; - ireq = (struct ieee80211req *) data; - switch (ireq->i_type) { - case IEEE80211_IOC_SSID: - if (ireq->i_val != 0 || - ireq->i_len > IEEE80211_NWID_LEN) { + ieee80211_key_update_begin(ic); + k->wk_keyix = kid; /* NB: force fixed key id */ + if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, + IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) { + k->wk_keylen = ireq->i_len; + memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); + if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) error = EINVAL; - break; - } - error = copyin(ireq->i_data, tmpssid, ireq->i_len); - if (error) - break; - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = ireq->i_len; - memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); + } else + error = EINVAL; + ieee80211_key_update_end(ic); + if (!error) /* NB: for compatibility */ error = ENETRESET; + break; + case IEEE80211_IOC_WEPTXKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID && + (uint16_t) kid != IEEE80211_KEYIX_NONE) + return EINVAL; + ic->ic_def_txkey = kid; + error = ENETRESET; /* push to hardware */ + break; + case IEEE80211_IOC_AUTHMODE: + switch (ireq->i_val) { + case IEEE80211_AUTH_WPA: + case IEEE80211_AUTH_8021X: /* 802.1x */ + case IEEE80211_AUTH_OPEN: /* open */ + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_AUTO: /* auto */ + auth = ieee80211_authenticator_get(ireq->i_val); + if (auth == NULL) + return EINVAL; break; - case IEEE80211_IOC_WEP: - /* - * These cards only support one mode so - * we just turn wep on if what ever is - * passed in is not OFF. - */ - if (ireq->i_val == IEEE80211_WEP_OFF) { - ic->ic_flags &= ~IEEE80211_F_WEPON; - } else { - ic->ic_flags |= IEEE80211_F_WEPON; - } - error = ENETRESET; + default: + return EINVAL; + } + switch (ireq->i_val) { + case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + ireq->i_val = IEEE80211_AUTH_8021X; break; - case IEEE80211_IOC_WEPKEY: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { - error = EINVAL; - break; - } - kid = (u_int) ireq->i_val; - if (kid >= IEEE80211_WEP_NKID) { - error = EINVAL; - break; - } - if (ireq->i_len > sizeof(tmpkey)) { - error = EINVAL; - break; - } - memset(tmpkey, 0, sizeof(tmpkey)); - error = copyin(ireq->i_data, tmpkey, ireq->i_len); - if (error) - break; - memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey, - sizeof(tmpkey)); - ic->ic_nw_keys[kid].wk_len = ireq->i_len; - error = ENETRESET; + case IEEE80211_AUTH_OPEN: /* open */ + ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); break; - case IEEE80211_IOC_WEPTXKEY: - kid = (u_int) ireq->i_val; - if (kid >= IEEE80211_WEP_NKID) { - error = EINVAL; - break; - } - ic->ic_wep_txkey = kid; + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_8021X: /* 802.1x */ + ic->ic_flags &= ~IEEE80211_F_WPA; + /* both require a key so mark the PRIVACY capability */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + break; + case IEEE80211_AUTH_AUTO: /* auto */ + ic->ic_flags &= ~IEEE80211_F_WPA; + /* XXX PRIVACY handling? */ + /* XXX what's the right way to do this? */ + break; + } + /* NB: authenticator attach/detach happens on state change */ + ic->ic_bss->ni_authmode = ireq->i_val; + /* XXX mixed/mode/usage? */ + ic->ic_auth = auth; + error = ENETRESET; + break; + case IEEE80211_IOC_CHANNEL: + /* XXX 0xffff overflows 16-bit signed */ + if (ireq->i_val == 0 || + ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || + isclr(ic->ic_chan_active, ireq->i_val)) { + return EINVAL; + } else + ic->ic_ibss_chan = ic->ic_des_chan = + &ic->ic_channels[ireq->i_val]; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: error = ENETRESET; break; -#if 0 - case IEEE80211_IOC_AUTHMODE: - sc->wi_authmode = ireq->i_val; + default: + /* + * If the desired channel has changed (to something + * other than any) and we're not already scanning, + * then kick the state machine. + */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + ic->ic_bss->ni_chan != ic->ic_des_chan && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) + error = ENETRESET; break; -#endif - case IEEE80211_IOC_CHANNEL: - /* XXX 0xffff overflows 16-bit signed */ - if (ireq->i_val == 0 || - ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) - ic->ic_des_chan = IEEE80211_CHAN_ANYC; - else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, ireq->i_val)) { - error = EINVAL; - break; + } + if (error == ENETRESET && + ic->ic_opmode == IEEE80211_M_MONITOR) { + if (IS_UP(ic)) { + /* + * Monitor mode can switch directly. + */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) + ic->ic_curchan = ic->ic_des_chan; + error = ic->ic_reset(ic->ic_ifp); } else - ic->ic_ibss_chan = ic->ic_des_chan = - &ic->ic_channels[ireq->i_val]; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: + error = 0; + } + break; + case IEEE80211_IOC_POWERSAVE: + switch (ireq->i_val) { + case IEEE80211_POWERSAVE_OFF: + if (ic->ic_flags & IEEE80211_F_PMGTON) { + ic->ic_flags &= ~IEEE80211_F_PMGTON; error = ENETRESET; - break; - default: - if (ic->ic_opmode == IEEE80211_M_STA) { - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - ic->ic_bss->ni_chan != ic->ic_des_chan) - error = ENETRESET; - } else { - if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) - error = ENETRESET; - } - break; - } - break; - case IEEE80211_IOC_POWERSAVE: - switch (ireq->i_val) { - case IEEE80211_POWERSAVE_OFF: - if (ic->ic_flags & IEEE80211_F_PMGTON) { - ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = ENETRESET; - } - break; - case IEEE80211_POWERSAVE_ON: - if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) - error = EINVAL; - else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { - ic->ic_flags |= IEEE80211_F_PMGTON; - error = ENETRESET; - } - break; - default: - error = EINVAL; - break; - } - break; - case IEEE80211_IOC_POWERSAVESLEEP: - if (ireq->i_val < 0) { - error = EINVAL; - break; } - ic->ic_lintval = ireq->i_val; - error = ENETRESET; break; - case IEEE80211_IOC_RTSTHRESHOLD: - if (!(IEEE80211_RTS_MIN < ireq->i_val && - ireq->i_val < IEEE80211_RTS_MAX)) { - error = EINVAL; - break; - } - ic->ic_rtsthreshold = ireq->i_val; - error = ENETRESET; - break; - case IEEE80211_IOC_PROTMODE: - if (ireq->i_val > IEEE80211_PROT_RTSCTS) { + case IEEE80211_POWERSAVE_ON: + if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) error = EINVAL; - break; - } - ic->ic_protmode = ireq->i_val; - /* NB: if not operating in 11g this can wait */ - if (ic->ic_curmode == IEEE80211_MODE_11G) + else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { + ic->ic_flags |= IEEE80211_F_PMGTON; error = ENETRESET; - break; - case IEEE80211_IOC_TXPOWER: - if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) { - error = EINVAL; - break; } - if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && - ireq->i_val < IEEE80211_TXPOWER_MAX)) { - error = EINVAL; - break; - } - ic->ic_txpower = ireq->i_val; - error = ENETRESET; break; default: error = EINVAL; break; } break; + case IEEE80211_IOC_POWERSAVESLEEP: + if (ireq->i_val < 0) + return EINVAL; + ic->ic_lintval = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + if (!(IEEE80211_RTS_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_RTS_MAX)) + return EINVAL; + ic->ic_rtsthreshold = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_PROTMODE: + if (ireq->i_val > IEEE80211_PROT_RTSCTS) + return EINVAL; + ic->ic_protmode = ireq->i_val; + /* NB: if not operating in 11g this can wait */ + if (ic->ic_curmode == IEEE80211_MODE_11G) + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return EINVAL; + if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && + ireq->i_val < IEEE80211_TXPOWER_MAX)) + return EINVAL; + ic->ic_txpowlimit = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_ROAMING: + if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && + ireq->i_val <= IEEE80211_ROAMING_MANUAL)) + return EINVAL; + ic->ic_roaming = ireq->i_val; + /* XXXX reset? */ + break; + case IEEE80211_IOC_PRIVACY: + if (ireq->i_val) { + /* XXX check for key state? */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + } else + ic->ic_flags &= ~IEEE80211_F_PRIVACY; + break; + case IEEE80211_IOC_DROPUNENCRYPTED: + if (ireq->i_val) + ic->ic_flags |= IEEE80211_F_DROPUNENC; + else + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + break; + case IEEE80211_IOC_WPAKEY: + error = ieee80211_ioctl_setkey(ic, ireq); + break; + case IEEE80211_IOC_DELKEY: + error = ieee80211_ioctl_delkey(ic, ireq); + break; + case IEEE80211_IOC_MLME: + error = ieee80211_ioctl_setmlme(ic, ireq); + break; + case IEEE80211_IOC_OPTIE: + error = ieee80211_ioctl_setoptie(ic, ireq); + break; + case IEEE80211_IOC_COUNTERMEASURES: + if (ireq->i_val) { + if ((ic->ic_flags & IEEE80211_F_WPA) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_COUNTERM; + } else + ic->ic_flags &= ~IEEE80211_F_COUNTERM; + break; + case IEEE80211_IOC_WPA: + if (ireq->i_val > 3) + return EINVAL; + /* XXX verify ciphers available */ + ic->ic_flags &= ~IEEE80211_F_WPA; + switch (ireq->i_val) { + case 1: + ic->ic_flags |= IEEE80211_F_WPA1; + break; + case 2: + ic->ic_flags |= IEEE80211_F_WPA2; + break; + case 3: + ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; + break; + } + error = ENETRESET; /* XXX? */ + break; + case IEEE80211_IOC_WME: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_WME; + } else + ic->ic_flags &= ~IEEE80211_F_WME; + error = ENETRESET; /* XXX maybe not for station? */ + break; + case IEEE80211_IOC_HIDESSID: + if (ireq->i_val) + ic->ic_flags |= IEEE80211_F_HIDESSID; + else + ic->ic_flags &= ~IEEE80211_F_HIDESSID; + error = ENETRESET; + break; + case IEEE80211_IOC_APBRIDGE: + if (ireq->i_val == 0) + ic->ic_flags |= IEEE80211_F_NOBRIDGE; + else + ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; + break; + case IEEE80211_IOC_MCASTCIPHER: + if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 && + !ieee80211_crypto_available(ireq->i_val)) + return EINVAL; + rsn->rsn_mcastcipher = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_MCASTKEYLEN: + if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) + return EINVAL; + /* XXX no way to verify driver capability */ + rsn->rsn_mcastkeylen = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_UCASTCIPHERS: + /* + * Convert user-specified cipher set to the set + * we can support (via hardware or software). + * NB: this logic intentionally ignores unknown and + * unsupported ciphers so folks can specify 0xff or + * similar and get all available ciphers. + */ + caps = 0; + for (j = 1; j < 32; j++) /* NB: skip WEP */ + if ((ireq->i_val & (1<ic_caps & cipher2cap(j)) || + ieee80211_crypto_available(j))) + caps |= 1<rsn_ucastcipherset = caps; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_UCASTCIPHER: + if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0) + return EINVAL; + rsn->rsn_ucastcipher = ireq->i_val; + break; + case IEEE80211_IOC_UCASTKEYLEN: + if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) + return EINVAL; + /* XXX no way to verify driver capability */ + rsn->rsn_ucastkeylen = ireq->i_val; + break; + case IEEE80211_IOC_DRIVER_CAPS: + /* NB: for testing */ + ic->ic_caps = (((uint16_t)ireq->i_val) << 16) | + ((uint16_t)ireq->i_len); + break; + case IEEE80211_IOC_KEYMGTALGS: + /* XXX check */ + rsn->rsn_keymgmtset = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_RSNCAPS: + /* XXX check */ + rsn->rsn_caps = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_BSSID: + if (ireq->i_len != sizeof(tmpbssid)) + return EINVAL; + error = copyin(ireq->i_data, tmpbssid, ireq->i_len); + if (error) + break; + IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid); + if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid)) + ic->ic_flags &= ~IEEE80211_F_DESBSSID; + else + ic->ic_flags |= IEEE80211_F_DESBSSID; + error = ENETRESET; + break; + case IEEE80211_IOC_CHANLIST: + error = ieee80211_ioctl_setchanlist(ic, ireq); + break; + case IEEE80211_IOC_SCAN_REQ: + if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ + break; + error = ieee80211_setupscan(ic, ic->ic_chan_avail); + if (error == 0) /* XXX background scan */ + error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case IEEE80211_IOC_ADDMAC: + case IEEE80211_IOC_DELMAC: + error = ieee80211_ioctl_macmac(ic, ireq); + break; + case IEEE80211_IOC_MACCMD: + error = ieee80211_ioctl_setmaccmd(ic, ireq); + break; + case IEEE80211_IOC_STA_TXPOW: + error = ieee80211_ioctl_setstatxpow(ic, ireq); + break; + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ + error = ieee80211_ioctl_setwmeparam(ic, ireq); + break; + case IEEE80211_IOC_DTIM_PERIOD: + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return EINVAL; + if (IEEE80211_DTIM_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_DTIM_MAX) { + ic->ic_dtim_period = ireq->i_val; + error = ENETRESET; /* requires restart */ + } else + error = EINVAL; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return EINVAL; + if (IEEE80211_BINTVAL_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_BINTVAL_MAX) { + ic->ic_bintval = ireq->i_val; + error = ENETRESET; /* requires restart */ + } else + error = EINVAL; + break; + case IEEE80211_IOC_PUREG: + if (ireq->i_val) + ic->ic_flags |= IEEE80211_F_PUREG; + else + ic->ic_flags &= ~IEEE80211_F_PUREG; + /* NB: reset only if we're operating on an 11g channel */ + if (ic->ic_curmode == IEEE80211_MODE_11G) + error = ENETRESET; + break; + case IEEE80211_IOC_MCAST_RATE: + ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL; + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 && + ireq->i_val != IEEE80211_FRAG_MAX) + return EINVAL; + if (!(IEEE80211_FRAG_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_FRAG_MAX)) + return EINVAL; + ic->ic_fragthreshold = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_BURST: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_BURST) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_BURST; + } else + ic->ic_flags &= ~IEEE80211_F_BURST; + error = ENETRESET; /* XXX maybe not for station? */ + break; + default: + error = EINVAL; + break; + } + if (error == ENETRESET && !IS_UP_AUTO(ic)) + error = 0; + return error; +} + +int +ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data, + struct ucred *cr) +{ + struct ifnet *ifp = ic->ic_ifp; + int error = 0; + struct ifreq *ifr; + struct ifaddr *ifa; /* XXX */ + + switch (cmd) { + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, (struct ifreq *) data, + &ic->ic_media, cmd); + break; + case SIOCG80211: + error = ieee80211_ioctl_get80211(ic, cmd, + (struct ieee80211req *) data, cr); + break; + case SIOCS80211: + error = suser_cred(cr, NULL_CRED_OKAY); + if (error == 0) + error = ieee80211_ioctl_set80211(ic, cmd, + (struct ieee80211req *) data); + break; case SIOCGIFGENERIC: - error = ieee80211_cfgget(ifp, cmd, data, cr); + error = ieee80211_cfgget(ic, cmd, data, cr); break; case SIOCSIFGENERIC: error = suser_cred(cr, NULL_CRED_OKAY); if (error) break; - error = ieee80211_cfgset(ifp, cmd, data); + error = ieee80211_cfgset(ic, cmd, data); break; case SIOCG80211STATS: ifr = (struct ifreq *)data; @@ -1101,14 +2509,14 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) */ case AF_IPX: { struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); - struct arpcom *ac = (struct arpcom *)ifp; - - if (ipx_nullhost(*ina)) - ina->x_host = *(union ipx_host *) ac->ac_enaddr; - else - bcopy((caddr_t) ina->x_host.c_host, - (caddr_t) ac->ac_enaddr, - sizeof(ac->ac_enaddr)); + + if (ipx_nullhost(*ina)) { + ina->x_host = *(union ipx_host *) + IF_LLADDR(ifp); + } else { + bcopy(ina->x_host.c_host, IF_LLADDR(ifp), + ETHER_ADDR_LEN); + } /* fall thru... */ } #endif diff --git a/sys/netproto/802_11/wlan/ieee80211_node.c b/sys/netproto/802_11/wlan/ieee80211_node.c index 1838231b0f..fa21eb8f42 100644 --- a/sys/netproto/802_11/wlan/ieee80211_node.c +++ b/sys/netproto/802_11/wlan/ieee80211_node.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,124 +29,248 @@ * (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: src/sys/net80211/ieee80211_node.c,v 1.22 2004/04/05 04:15:55 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_node.c,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.48.2.10 2006/03/13 03:05:47 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_node.c,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ -#include "opt_inet.h" - #include #include #include #include #include + #include -#include -#include -#include -#include -#include -#include -#include -#include - -#include #include -#include -#include #include +#include #include -#include #include #include -#ifdef INET -#include -#include -#endif +/* + * Association id's are managed with a bit vector. + */ +#define IEEE80211_AID_SET(b, w) \ + ((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_CLR(b, w) \ + ((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_ISSET(b, w) \ + ((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32))) + +static struct ieee80211_node *node_alloc(struct ieee80211_node_table *); +static void node_cleanup(struct ieee80211_node *); +static void node_free(struct ieee80211_node *); +static uint8_t node_getrssi(const struct ieee80211_node *); -static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); -static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *); -static void ieee80211_node_copy(struct ieee80211com *, - struct ieee80211_node *, const struct ieee80211_node *); -static uint8_t ieee80211_node_getrssi(struct ieee80211com *, - struct ieee80211_node *); +static void ieee80211_setup_node(struct ieee80211_node_table *, + struct ieee80211_node *, const uint8_t *); +static void _ieee80211_free_node(struct ieee80211_node *); +static void ieee80211_free_allnodes(struct ieee80211_node_table *); -static void ieee80211_setup_node(struct ieee80211com *ic, - struct ieee80211_node *ni, uint8_t *macaddr); -static void _ieee80211_free_node(struct ieee80211com *, - struct ieee80211_node *); +static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *); +static void ieee80211_timeout_stations(struct ieee80211_node_table *); + +static void ieee80211_set_tim(struct ieee80211_node *, int set); + +static void ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, const char *name, + int inact, int keyixmax, + void (*timeout)(struct ieee80211_node_table *)); +static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt); MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); void -ieee80211_node_attach(struct ifnet *ifp) +ieee80211_node_attach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + ic->ic_node_alloc = node_alloc; + ic->ic_node_free = node_free; + ic->ic_node_cleanup = node_cleanup; + ic->ic_node_getrssi = node_getrssi; - lwkt_token_init(&ic->ic_nodetoken); - TAILQ_INIT(&ic->ic_node); - ic->ic_node_alloc = ieee80211_node_alloc; - ic->ic_node_free = ieee80211_node_free; - ic->ic_node_copy = ieee80211_node_copy; - ic->ic_node_getrssi = ieee80211_node_getrssi; - ic->ic_scangen = 1; + /* default station inactivity timer setings */ + ic->ic_inact_init = IEEE80211_INACT_INIT; + ic->ic_inact_auth = IEEE80211_INACT_AUTH; + ic->ic_inact_run = IEEE80211_INACT_RUN; + ic->ic_inact_probe = IEEE80211_INACT_PROBE; + + /* NB: driver should override */ + ic->ic_max_aid = IEEE80211_AID_DEF; + ic->ic_set_tim = ieee80211_set_tim; } void -ieee80211_node_lateattach(struct ifnet *ifp) +ieee80211_node_lateattach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni; + struct ieee80211_rsnparms *rsn; - ni = (*ic->ic_node_alloc)(ic); - KASSERT(ni != NULL, ("unable to setup inital BSS node")); - ni->ni_chan = IEEE80211_CHAN_ANYC; - ic->ic_bss = ni; - ic->ic_txpower = IEEE80211_TXPOWER_MAX; + if (ic->ic_max_aid > IEEE80211_AID_MAX) + ic->ic_max_aid = IEEE80211_AID_MAX; + + ic->ic_aid_bitmap = + malloc(howmany(ic->ic_max_aid, 32) * sizeof(uint32_t), + M_DEVBUF, M_WAITOK | M_ZERO); + + /* XXX defer until using hostap/ibss mode */ + ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(uint8_t); + ic->ic_tim_bitmap = malloc(ic->ic_tim_len, M_DEVBUF, + M_WAITOK | M_ZERO); + + ieee80211_node_table_init(ic, &ic->ic_sta, "station", + IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix, + ieee80211_timeout_stations); + ieee80211_node_table_init(ic, &ic->ic_scan, "scan", + IEEE80211_INACT_SCAN, 0, + ieee80211_timeout_scan_candidates); + + ieee80211_reset_bss(ic); + /* + * Setup "global settings" in the bss node so that + * each new station automatically inherits them. + */ + rsn = &ic->ic_bss->ni_rsn; + /* WEP, TKIP, and AES-CCM are always supported */ + rsn->rsn_ucastcipherset |= 1<rsn_ucastcipherset |= 1<rsn_ucastcipherset |= 1<ic_caps & IEEE80211_C_AES) + rsn->rsn_ucastcipherset |= 1<ic_caps & IEEE80211_C_CKIP) + rsn->rsn_ucastcipherset |= 1<rsn_ucastcipher = IEEE80211_CIPHER_WEP; + rsn->rsn_ucastkeylen = 104 / NBBY; + /* + * WPA says the multicast cipher is the lowest unicast + * cipher supported. But we skip WEP which would + * otherwise be used based on this criteria. + */ + rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP; + rsn->rsn_mcastkeylen = 128 / NBBY; + + /* + * We support both WPA-PSK and 802.1x; the one used + * is determined by the authentication mode and the + * setting of the PSK state. + */ + rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK; + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode); +} + +void +ieee80211_node_detach(struct ieee80211com *ic) +{ + if (ic->ic_bss != NULL) { + ieee80211_free_node(ic->ic_bss); + ic->ic_bss = NULL; + } + ieee80211_node_table_cleanup(&ic->ic_scan); + ieee80211_node_table_cleanup(&ic->ic_sta); + if (ic->ic_aid_bitmap != NULL) { + free(ic->ic_aid_bitmap, M_DEVBUF); + ic->ic_aid_bitmap = NULL; + } + if (ic->ic_tim_bitmap != NULL) { + free(ic->ic_tim_bitmap, M_DEVBUF); + ic->ic_tim_bitmap = NULL; + } +} + +/* + * Port authorize/unauthorize interfaces for use by an authenticator. + */ + +void +ieee80211_node_authorize(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + ni->ni_flags |= IEEE80211_NODE_AUTH; + ni->ni_inact_reload = ic->ic_inact_run; } void -ieee80211_node_detach(struct ifnet *ifp) +ieee80211_node_unauthorize(struct ieee80211_node *ni) { - struct ieee80211com *ic = (void *)ifp; + ni->ni_flags &= ~IEEE80211_NODE_AUTH; +} - if (ic->ic_bss != NULL) - (*ic->ic_node_free)(ic, ic->ic_bss); - ieee80211_free_allnodes(ic); - lwkt_token_uninit(&ic->ic_nodetoken); +/* + * Set/change the channel. The rate set is also updated as + * to insure a consistent view by drivers. + */ +static void +ieee80211_set_chan(struct ieee80211com *ic, + struct ieee80211_node *ni, struct ieee80211_channel *chan) +{ + if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */ + chan = ic->ic_curchan; + ni->ni_chan = chan; + ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; } /* * AP scanning support. */ +#ifdef IEEE80211_DEBUG +static void +dump_chanlist(const u_char chans[]) +{ + const char *sep; + int i; + + sep = " "; + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(chans, i)) { + printf("%s%u", sep, i); + sep = ", "; + } +} +#endif /* IEEE80211_DEBUG */ + /* - * Initialize the active channel set based on the set + * Initialize the channel set to scan based on the * of available channels and the current PHY mode. */ static void -ieee80211_reset_scan(struct ifnet *ifp) +ieee80211_reset_scan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - memcpy(ic->ic_chan_scan, ic->ic_chan_active, - sizeof(ic->ic_chan_active)); - /* NB: hack, setup so next_scan starts with the first channel */ - if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) - ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; + /* XXX ic_des_chan should be handled with ic_chan_active */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan)); + setbit(ic->ic_chan_scan, + ieee80211_chan2ieee(ic, ic->ic_des_chan)); + } else + memcpy(ic->ic_chan_scan, ic->ic_chan_active, + sizeof(ic->ic_chan_active)); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + printf("%s: scan set:", __func__); + dump_chanlist(ic->ic_chan_scan); + printf(" start chan %u\n", + ieee80211_chan2ieee(ic, ic->ic_curchan)); + } +#endif /* IEEE80211_DEBUG */ } /* * Begin an active scan. */ void -ieee80211_begin_scan(struct ifnet *ifp) +ieee80211_begin_scan(struct ieee80211com *ic, int reset) { - struct ieee80211com *ic = (void *)ifp; + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); /* * In all but hostap mode scanning starts off in @@ -157,95 +281,204 @@ ieee80211_begin_scan(struct ifnet *ifp) ic->ic_stats.is_scan_active++; } else ic->ic_stats.is_scan_passive++; - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "begin %s scan\n", - (ic->ic_flags & IEEE80211_F_ASCAN) ? - "active" : "passive"); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "begin %s scan in %s mode\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive", + ieee80211_phymode_name[ic->ic_curmode]); /* - * Clear scan state and flush any previously seen - * AP's. Note that the latter assumes we don't act - * as both an AP and a station, otherwise we'll - * potentially flush state of stations associated - * with us. + * Clear scan state and flush any previously seen AP's. */ - ieee80211_reset_scan(ifp); - ieee80211_free_allnodes(ic); + ieee80211_reset_scan(ic); + if (reset) + ieee80211_free_allnodes(&ic->ic_scan); + + ic->ic_flags |= IEEE80211_F_SCAN; /* Scan the next channel. */ - ieee80211_next_scan(ifp); + ieee80211_next_scan(ic); } /* * Switch to the next channel marked for scanning. */ -void -ieee80211_next_scan(struct ifnet *ifp) +int +ieee80211_next_scan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; struct ieee80211_channel *chan; - chan = ic->ic_bss->ni_chan; - for (;;) { + /* + * Insure any previous mgt frame timeouts don't fire. + * This assumes the driver does the right thing in + * flushing anything queued in the driver and below. + */ + ic->ic_mgt_timer = 0; + ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; + + chan = ic->ic_curchan; + do { if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) chan = &ic->ic_channels[0]; if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { + clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: chan %d->%d\n", __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + ieee80211_chan2ieee(ic, chan)); + ic->ic_curchan = chan; /* - * Honor channels marked passive-only - * during an active scan. + * XXX drivers should do this as needed, + * XXX for now maintain compatibility */ - if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || - (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) - break; + ic->ic_bss->ni_rates = + ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + return 1; } - if (chan == ic->ic_bss->ni_chan) { - ieee80211_end_scan(ifp); - return; - } - } - clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); - IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", - ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), - ieee80211_chan2ieee(ic, chan))); - ic->ic_bss->ni_chan = chan; - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } while (chan != ic->ic_curchan); + ieee80211_end_scan(ic); + return 0; +} + +/* + * Probe the curent channel, if allowed, while scanning. + * If the channel is not marked passive-only then send + * a probe request immediately. Otherwise mark state and + * listen for beacons on the channel; if we receive something + * then we'll transmit a probe request. + */ +void +ieee80211_probe_curchan(struct ieee80211com *ic, int force) +{ + struct ifnet *ifp = ic->ic_ifp; + + if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 || force) { + /* + * XXX send both broadcast+directed probe request + */ + ieee80211_send_probereq(ic->ic_bss, + ic->ic_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + ic->ic_des_essid, ic->ic_des_esslen, + ic->ic_opt_ie, ic->ic_opt_ie_len); + } else + ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; +} + +static __inline void +copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss) +{ + /* propagate useful state */ + nbss->ni_authmode = obss->ni_authmode; + nbss->ni_txpower = obss->ni_txpower; + nbss->ni_vlan = obss->ni_vlan; + nbss->ni_rsn = obss->ni_rsn; + /* XXX statistics? */ } void ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) { + struct ieee80211_node_table *nt; struct ieee80211_node *ni; - struct ifnet *ifp = &ic->ic_if; - ni = ic->ic_bss; - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "creating ibss\n"); - ic->ic_flags |= IEEE80211_F_SIBSS; - ni->ni_chan = chan; - ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; - IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: creating ibss\n", __func__); + + /* + * Create the station/neighbor table. Note that for adhoc + * mode we make the initial inactivity timer longer since + * we create nodes only through discovery and they typically + * are long-lived associations. + */ + nt = &ic->ic_sta; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + nt->nt_name = "station"; + nt->nt_inact_init = ic->ic_inact_init; + } else { + nt->nt_name = "neighbor"; + nt->nt_inact_init = ic->ic_inact_run; + } + + ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr); + if (ni == NULL) { + /* XXX recovery? */ + return; + } IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); - if (ic->ic_opmode == IEEE80211_M_IBSS) - ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ ni->ni_esslen = ic->ic_des_esslen; memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); - ni->ni_rssi = 0; - ni->ni_rstamp = 0; - memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); - ni->ni_intval = ic->ic_lintval; - ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; - if (ic->ic_flags & IEEE80211_F_WEPON) + copy_bss(ni, ic->ic_bss); + ni->ni_intval = ic->ic_bintval; + if (ic->ic_flags & IEEE80211_F_PRIVACY) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ ni->ni_fhindex = 1; } - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + if (ic->ic_opmode == IEEE80211_M_IBSS) { + ic->ic_flags |= IEEE80211_F_SIBSS; + ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */ + if (ic->ic_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid); + else + ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ + } else if (ic->ic_opmode == IEEE80211_M_AHDEMO) { + if (ic->ic_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid); + else + memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN); + } + /* + * Fix the channel and related attributes. + */ + ieee80211_set_chan(ic, ni, chan); + ic->ic_curchan = chan; + ic->ic_curmode = ieee80211_chan2mode(ic, chan); + /* + * Do mode-specific rate setup. + */ + if (ic->ic_curmode == IEEE80211_MODE_11G) { + /* + * Use a mixed 11b/11g rate set. + */ + ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11G); + } else if (ic->ic_curmode == IEEE80211_MODE_11B) { + /* + * Force pure 11b rate set. + */ + ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11B); + } + + ieee80211_sta_join(ic, ieee80211_ref_node(ni)); +} + +void +ieee80211_reset_bss(struct ieee80211com *ic) +{ + struct ieee80211_node *ni, *obss; + + ieee80211_node_table_reset(&ic->ic_scan); + ieee80211_node_table_reset(&ic->ic_sta); + + ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr); + KASSERT(ni != NULL, ("unable to setup inital BSS node")); + obss = ic->ic_bss; + ic->ic_bss = ieee80211_ref_node(ni); + if (obss != NULL) { + copy_bss(ni, obss); + ni->ni_intval = ic->ic_bintval; + ieee80211_free_node(obss); + } } +/* XXX tunable */ +#define STA_FAILS_MAX 2 /* assoc failures before ignored */ + static int -ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) +ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) { - struct ieee80211com *ic = (void *)ifp; uint8_t rate; int fail; @@ -262,7 +495,7 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } - if (ic->ic_flags & IEEE80211_F_WEPON) { + if (ic->ic_flags & IEEE80211_F_PRIVACY) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { @@ -270,7 +503,7 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= 0x04; } - rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); + rate = ieee80211_fix_rate(ni, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; if (ic->ic_des_esslen != 0 && @@ -280,10 +513,15 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; + if (ni->ni_fails >= STA_FAILS_MAX) + fail |= 0x40; #ifdef IEEE80211_DEBUG - if (ifp->if_flags & IFF_DEBUG) { - printf(" %c %6D", fail ? '-' : '+', ni->ni_macaddr, ":"); - printf(" %6D%c", ni->ni_bssid, ":", fail & 0x20 ? '!' : ' '); + if (ieee80211_msg_scan(ic)) { + printf(" %c %6D", + fail & 0x40 ? '=' : fail & 0x80 ? '^' : fail ? '-' : '+', + ni->ni_macaddr, ":"); + printf(" %6D%c", ni->ni_bssid, ":", + fail & 0x20 ? '!' : ' '); printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); printf(" %+4d", ni->ni_rssi); @@ -305,49 +543,155 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) return fail; } +static __inline uint8_t +maxrate(const struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs = &ni->ni_rates; + /* NB: assumes rate set is sorted (happens on frame receive) */ + return rs->rs_rates[rs->rs_nrates-1] & IEEE80211_RATE_VAL; +} + +/* + * Compare the capabilities of two nodes and decide which is + * more desirable (return >0 if a is considered better). Note + * that we assume compatibility/usability has already been checked + * so we don't need to (e.g. validate whether privacy is supported). + * Used to select the best scan candidate for association in a BSS. + */ +static int +ieee80211_node_compare(struct ieee80211com *ic, + const struct ieee80211_node *a, + const struct ieee80211_node *b) +{ +#define ABS(a) ((a) < 0 ? -(a) : (a)) + uint8_t maxa, maxb; + uint8_t rssia, rssib; + int weight; + + /* privacy support preferred */ + if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) && + (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + return 1; + if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 && + (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)) + return -1; + + /* compare count of previous failures */ + weight = b->ni_fails - a->ni_fails; + if (ABS(weight) > 1) + return weight; + + rssia = ic->ic_node_getrssi(a); + rssib = ic->ic_node_getrssi(b); + if (ABS(rssib - rssia) < 5) { + /* best/max rate preferred if signal level close enough XXX */ + maxa = maxrate(a); + maxb = maxrate(b); + if (maxa != maxb) + return maxa - maxb; + /* XXX use freq for channel preference */ + /* for now just prefer 5Ghz band to all other bands */ + if (IEEE80211_IS_CHAN_5GHZ(a->ni_chan) && + !IEEE80211_IS_CHAN_5GHZ(b->ni_chan)) + return 1; + if (!IEEE80211_IS_CHAN_5GHZ(a->ni_chan) && + IEEE80211_IS_CHAN_5GHZ(b->ni_chan)) + return -1; + } + /* all things being equal, use signal level */ + return rssia - rssib; +#undef ABS +} + +/* + * Mark an ongoing scan stopped. + */ +void +ieee80211_cancel_scan(struct ieee80211com *ic) +{ + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s: end %s scan\n", + __func__, + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); + + ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN); + ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; +} + /* * Complete a scan of potential channels. */ void -ieee80211_end_scan(struct ifnet *ifp) +ieee80211_end_scan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni, *nextbs, *selbs; - int i, fail; + struct ieee80211_node_table *nt = &ic->ic_scan; + struct ieee80211_node *ni, *selbs; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); - ic->ic_flags &= ~IEEE80211_F_ASCAN; - ni = TAILQ_FIRST(&ic->ic_node); + ieee80211_cancel_scan(ic); + ieee80211_notify_scan_done(ic); if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - /* XXX off stack? */ - u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; + uint8_t maxrssi[IEEE80211_CHAN_MAX]; /* XXX off stack? */ + int i, bestchan; + uint8_t rssi; + /* * The passive scan to look for existing AP's completed, * select a channel to camp on. Identify the channels * that already have one or more AP's and try to locate - * an unnoccupied one. If that fails, pick a random - * channel from the active set. + * an unoccupied one. If that fails, pick a channel that + * looks to be quietest. */ - for (; ni != NULL; ni = nextbs) { - ieee80211_ref_node(ni); - nextbs = TAILQ_NEXT(ni, ni_list); - setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); - ieee80211_free_node(ic, ni); + memset(maxrssi, 0, sizeof(maxrssi)); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + rssi = ic->ic_node_getrssi(ni); + i = ieee80211_chan2ieee(ic, ni->ni_chan); + if (rssi > maxrssi[i]) + maxrssi[i] = rssi; } + /* XXX select channel more intelligently */ + bestchan = -1; for (i = 0; i < IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) - break; - if (i == IEEE80211_CHAN_MAX) { - fail = arc4random() & 3; /* random 0-3 */ - for (i = 0; i < IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_active, i) && fail-- == 0) + if (isset(ic->ic_chan_active, i)) { + /* + * If the channel is unoccupied the max rssi + * should be zero; just take it. Otherwise + * track the channel with the lowest rssi and + * use that when all channels appear occupied. + */ + if (maxrssi[i] == 0) { + bestchan = i; break; + } + if (bestchan == -1 || + maxrssi[i] < maxrssi[bestchan]) + bestchan = i; + } + if (bestchan != -1) { + ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]); + return; } - ieee80211_create_ibss(ic, &ic->ic_channels[i]); - return; + /* no suitable channel, should not happen */ } - if (ni == NULL) { - IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); + + /* + * When manually sequencing the state machine; scan just once + * regardless of whether we have a candidate or not. The + * controlling application is expected to setup state and + * initiate an association. + */ + if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL) + return; + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + /* NB: unlocked read should be ok */ + if (TAILQ_FIRST(&nt->nt_node) == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); notfound: if (ic->ic_opmode == IEEE80211_M_IBSS && (ic->ic_flags & IEEE80211_F_IBSSON) && @@ -355,219 +699,768 @@ ieee80211_end_scan(struct ifnet *ifp) ieee80211_create_ibss(ic, ic->ic_ibss_chan); return; } + /* + * Decrement the failure counts so entries will be + * reconsidered the next time around. We really want + * to do this only for sta's where we've previously + * had some success. + */ + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) + if (ni->ni_fails) + ni->ni_fails--; /* * Reset the list of channels to scan and start again. */ - ieee80211_reset_scan(ifp); - ieee80211_next_scan(ifp); + ieee80211_reset_scan(ic); + ic->ic_flags |= IEEE80211_F_SCAN; + ieee80211_next_scan(ic); return; } selbs = NULL; - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n"); - for (; ni != NULL; ni = nextbs) { - ieee80211_ref_node(ni); - nextbs = TAILQ_NEXT(ni, ni_list); - if (ni->ni_fails) { - /* - * The configuration of the access points may change - * during my scan. So delete the entry for the AP - * and retry to associate if there is another beacon. - */ - if (ni->ni_fails++ > 2) - ieee80211_free_node(ic, ni); - continue; - } - if (ieee80211_match_bss(ifp, ni) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n", + "macaddr bssid chan rssi rate flag wep essid"); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + if (ieee80211_match_bss(ic, ni) == 0) { if (selbs == NULL) selbs = ni; - else if (ni->ni_rssi > selbs->ni_rssi) { - ieee80211_unref_node(&selbs); + else if (ieee80211_node_compare(ic, ni, selbs) > 0) selbs = ni; - } else - ieee80211_unref_node(&ni); - } else { - ieee80211_unref_node(&ni); } } + if (selbs != NULL) /* NB: grab ref while dropping lock */ + ieee80211_ref_node(selbs); if (selbs == NULL) goto notfound; - (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); + if (!ieee80211_sta_join(ic, selbs)) { + ieee80211_free_node(selbs); + goto notfound; + } +} + +/* + * Handle 802.11 ad hoc network merge. The + * convention, set by the Wireless Ethernet Compatibility Alliance + * (WECA), is that an 802.11 station will change its BSSID to match + * the "oldest" 802.11 ad hoc network, on the same channel, that + * has the station's desired SSID. The "oldest" 802.11 network + * sends beacons with the greatest TSF timestamp. + * + * The caller is assumed to validate TSF's before attempting a merge. + * + * Return !0 if the BSSID changed, 0 otherwise. + */ +int +ieee80211_ibss_merge(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + if (ni == ic->ic_bss || + IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) { + /* unchanged, nothing to do */ + return 0; + } + if (ieee80211_match_bss(ic, ni) != 0) { /* capabilities mismatch */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: merge failed, capabilities mismatch\n", __func__); + ic->ic_stats.is_ibss_capmismatch++; + return 0; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%6D: new bssid %s: %s preamble, %s slot time%s\n", __func__, + ni->ni_bssid, ":", + ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "" + ); + return ieee80211_sta_join(ic, ieee80211_ref_node(ni)); +} + +/* + * Join the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +int +ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs) +{ + struct ieee80211_node *obss; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + if (ic->ic_opmode == IEEE80211_M_IBSS) { - ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ic->ic_bss->ni_rates.rs_nrates == 0) { - selbs->ni_fails++; - ieee80211_unref_node(&selbs); - goto notfound; - } - ieee80211_unref_node(&selbs); + struct ieee80211_node_table *nt; /* - * Discard scan set; the nodes have a refcnt of zero - * and have not asked the driver to setup private - * node state. Let them be repopulated on demand either - * through transmission (ieee80211_find_txnode) or receipt - * of a probe response (to be added). + * Delete unusable rates; we've already checked + * that the negotiated rate set is acceptable. */ - ieee80211_free_allnodes(ic); - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - } else { - ieee80211_unref_node(&selbs); - ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); + ieee80211_fix_rate(selbs, IEEE80211_F_DODEL); + /* + * Fillin the neighbor table; it will already + * exist if we are simply switching mastership. + * XXX ic_sta always setup so this is unnecessary? + */ + nt = &ic->ic_sta; + nt->nt_name = "neighbor"; + nt->nt_inact_init = ic->ic_inact_run; + } + + /* + * Committed to selbs, setup state. + */ + obss = ic->ic_bss; + ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */ + if (obss != NULL) { + copy_bss(selbs, obss); + ieee80211_free_node(obss); } + /* + * Set the erp state (mostly the slot time) to deal with + * the auto-select case; this should be redundant if the + * mode is locked. + */ + ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan); + ic->ic_curchan = selbs->ni_chan; + ieee80211_reset_erp(ic); + ieee80211_wme_initparams(ic); + + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); + else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + return 1; +} + +/* + * Leave the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +void +ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ic->ic_node_cleanup(ni); + ieee80211_notify_node_leave(ic, ni); } static struct ieee80211_node * -ieee80211_node_alloc(struct ieee80211com *ic) +node_alloc(struct ieee80211_node_table *nt) { struct ieee80211_node *ni; - MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node), - M_80211_NODE, M_NOWAIT | M_ZERO); + + ni = malloc(sizeof(struct ieee80211_node), M_80211_NODE, + M_NOWAIT | M_ZERO); return ni; } +/* + * Reclaim any resources in a node and reset any critical + * state. Typically nodes are free'd immediately after, + * but in some cases the storage may be reused so we need + * to insure consistent state (should probably fix that). + */ static void -ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) +node_cleanup(struct ieee80211_node *ni) { - FREE(ni, M_80211_NODE); +#define N(a) (sizeof(a)/sizeof(a[0])) + struct ieee80211com *ic = ni->ni_ic; + int i, qlen; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + /* NB: preserve ni_table */ + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { + ic->ic_ps_sta--; + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] power save mode off, %u sta's in ps mode\n", + ni->ni_macaddr, ":", ic->ic_ps_sta); + } + /* + * Clear AREF flag that marks the authorization refcnt bump + * has happened. This is probably not needed as the node + * should always be removed from the table so not found but + * do it just in case. + */ + ni->ni_flags &= ~IEEE80211_NODE_AREF; + + /* + * Drain power save queue and, if needed, clear TIM. + */ + IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen); + if (qlen != 0 && ic->ic_set_tim != NULL) + ic->ic_set_tim(ni, 0); + + ni->ni_associd = 0; + if (ni->ni_challenge != NULL) { + free(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + /* + * Preserve SSID, WPA, and WME ie's so the bss node is + * reusable during a re-auth/re-assoc state transition. + * If we remove these data they will not be recreated + * because they come from a probe-response or beacon frame + * which cannot be expected prior to the association-response. + * This should not be an issue when operating in other modes + * as stations leaving always go through a full state transition + * which will rebuild this state. + * + * XXX does this leave us open to inheriting old state? + */ + for (i = 0; i < N(ni->ni_rxfrag); i++) + if (ni->ni_rxfrag[i] != NULL) { + m_freem(ni->ni_rxfrag[i]); + ni->ni_rxfrag[i] = NULL; + } + /* + * Must be careful here to remove any key map entry w/o a LOR. + */ + ieee80211_node_delucastkey(ni); +#undef N } static void -ieee80211_node_copy(struct ieee80211com *ic, - struct ieee80211_node *dst, const struct ieee80211_node *src) +node_free(struct ieee80211_node *ni) { - *dst = *src; + struct ieee80211com *ic = ni->ni_ic; + + ic->ic_node_cleanup(ni); + if (ni->ni_wpa_ie != NULL) + free(ni->ni_wpa_ie, M_DEVBUF); + if (ni->ni_wme_ie != NULL) + free(ni->ni_wme_ie, M_DEVBUF); + IEEE80211_NODE_SAVEQ_DESTROY(ni); + free(ni, M_80211_NODE); } static uint8_t -ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) +node_getrssi(const struct ieee80211_node *ni) { return ni->ni_rssi; } static void -ieee80211_setup_node(struct ieee80211com *ic, - struct ieee80211_node *ni, uint8_t *macaddr) +ieee80211_setup_node(struct ieee80211_node_table *nt, + struct ieee80211_node *ni, const uint8_t *macaddr) { - lwkt_tokref ilock; + struct ieee80211com *ic = nt->nt_ic; int hash; + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%6D %p<%s> in %s table\n", __func__, ni, + macaddr, ":", nt->nt_name); + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); hash = IEEE80211_NODE_HASH(macaddr); - ni->ni_refcnt = 1; /* mark referenced */ - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); - LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); - /* - * Note we don't enable the inactive timer when acting - * as a station. Nodes created in this mode represent - * AP's identified while scanning. If we time them out - * then several things happen: we can't return the data - * to users to show the list of AP's we encountered, and - * more importantly, we'll incorrectly deauthenticate - * ourself because the inactivity timer will kick us off. - */ - if (ic->ic_opmode != IEEE80211_M_STA) - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - lwkt_reltoken(&ilock); + ieee80211_node_initref(ni); /* mark referenced */ + ni->ni_chan = IEEE80211_CHAN_ANYC; + ni->ni_authmode = IEEE80211_AUTH_OPEN; + ni->ni_txpower = ic->ic_txpowlimit; /* max power */ + ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); + ni->ni_inact_reload = nt->nt_inact_init; + ni->ni_inact = ni->ni_inact_reload; + IEEE80211_NODE_SAVEQ_INIT(ni, "unknown"); + + TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list); + LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash); + ni->ni_table = nt; + ni->ni_ic = ic; } struct ieee80211_node * -ieee80211_alloc_node(struct ieee80211com *ic, uint8_t *macaddr) +ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) { - struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + + ni = ic->ic_node_alloc(nt); if (ni != NULL) - ieee80211_setup_node(ic, ni, macaddr); + ieee80211_setup_node(nt, ni, macaddr); else ic->ic_stats.is_rx_nodealloc++; return ni; } +/* + * Craft a temporary node suitable for sending a management frame + * to the specified station. We craft only as much state as we + * need to do the work since the node will be immediately reclaimed + * once the send completes. + */ +struct ieee80211_node * +ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr) +{ + struct ieee80211_node *ni; + + ni = ic->ic_node_alloc(&ic->ic_sta); + if (ni != NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s %p<%6D>\n", __func__, ni, macaddr, ":"); + + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); + ieee80211_node_initref(ni); /* mark referenced */ + ni->ni_txpower = ic->ic_bss->ni_txpower; + /* NB: required by ieee80211_fix_rate */ + ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan); + ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, + IEEE80211_KEYIX_NONE); + /* XXX optimize away */ + IEEE80211_NODE_SAVEQ_INIT(ni, "unknown"); + + ni->ni_table = NULL; /* NB: pedantic */ + ni->ni_ic = ic; + } else { + /* XXX msg */ + ic->ic_stats.is_rx_nodealloc++; + } + return ni; +} + struct ieee80211_node * -ieee80211_dup_bss(struct ieee80211com *ic, uint8_t *macaddr) +ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr) { - struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + + ni = ic->ic_node_alloc(nt); if (ni != NULL) { - ieee80211_setup_node(ic, ni, macaddr); + ieee80211_setup_node(nt, ni, macaddr); /* * Inherit from ic_bss. */ + ni->ni_authmode = ic->ic_bss->ni_authmode; + ni->ni_txpower = ic->ic_bss->ni_txpower; + ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); - ni->ni_chan = ic->ic_bss->ni_chan; + ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan); + ni->ni_rsn = ic->ic_bss->ni_rsn; } else ic->ic_stats.is_rx_nodealloc++; return ni; } static struct ieee80211_node * -_ieee80211_find_node(struct ieee80211com *ic, uint8_t *macaddr) +#ifdef IEEE80211_DEBUG_REFCNT +_ieee80211_find_node_debug(struct ieee80211_node_table *nt, + const uint8_t *macaddr, const char *func, int line) +#else +_ieee80211_find_node(struct ieee80211_node_table *nt, + const uint8_t *macaddr) +#endif { struct ieee80211_node *ni; int hash; hash = IEEE80211_NODE_HASH(macaddr); - LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { - atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ + ieee80211_ref_node(ni); /* mark referenced */ +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, + func, line, + ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni)); +#endif return ni; } } return NULL; } +#ifdef IEEE80211_DEBUG_REFCNT +#define _ieee80211_find_node(nt, mac) \ + _ieee80211_find_node_debug(nt, mac, func, line) +#endif struct ieee80211_node * -ieee80211_find_node(struct ieee80211com *ic, uint8_t *macaddr) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_debug(struct ieee80211_node_table *nt, + const uint8_t *macaddr, const char *func, int line) +#else +ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) +#endif { struct ieee80211_node *ni; - struct lwkt_tokref ilock; - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - ni = _ieee80211_find_node(ic, macaddr); - lwkt_reltoken(&ilock); + ASSERT_SERIALIZED(nt->nt_ic->ic_ifp->if_serializer); + + ni = _ieee80211_find_node(nt, macaddr); return ni; } /* - * Return a reference to the appropriate node for sending - * a data frame. This handles node discovery in adhoc networks. + * Fake up a node; this handles node discovery in adhoc mode. + * Note that for the driver's benefit we we treat this like + * an association so the driver has an opportunity to setup + * it's private state. */ struct ieee80211_node * -ieee80211_find_txnode(struct ieee80211com *ic, uint8_t *macaddr) +ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { + struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; - struct lwkt_tokref ilock; - /* - * The destination address should be in the node table - * unless we are operating in station mode or this is a - * multicast/broadcast frame. - */ - if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) - return ic->ic_bss; - - /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - ni = _ieee80211_find_node(ic, macaddr); - lwkt_reltoken(&ilock); - ni = _ieee80211_find_node(ic, macaddr); - if (ni == NULL && - (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO)) { - /* - * Fake up a node; this handles node discovery in - * adhoc mode. Note that for the driver's benefit - * we we treat this like an association so the driver - * has an opportunity to setup it's private state. - * - * XXX need better way to handle this; issue probe - * request so we can deduce rate set, etc. - */ - ni = ieee80211_dup_bss(ic, macaddr); - if (ni != NULL) { - /* XXX no rate negotiation; just dup */ - ni->ni_rates = ic->ic_bss->ni_rates; - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, 1); + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s: mac<%6D>\n", __func__, macaddr, ":"); + ni = ieee80211_dup_bss(nt, macaddr); + if (ni != NULL) { + /* XXX no rate negotiation; just dup */ + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, 1); + /* XXX not right for 802.1x/WPA */ + ieee80211_node_authorize(ni); + if (ic->ic_opmode == IEEE80211_M_AHDEMO) { + /* + * Blindly propagate capabilities based on the + * local configuration. In particular this permits + * us to use QoS to disable ACK's. + */ + if (ic->ic_flags & IEEE80211_F_WME) + ni->ni_flags |= IEEE80211_NODE_QOS; + } + } + return ni; +} + +#ifdef IEEE80211_DEBUG +static void +dump_probe_beacon(uint8_t subtype, int isnew, + const uint8_t mac[IEEE80211_ADDR_LEN], + const struct ieee80211_scanparams *sp) +{ + + printf("[%6D] %s%s on chan %u (bss chan %u) ", + mac, ":", isnew ? "new " : "", + ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], + sp->chan, sp->bchan); + ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); + printf("\n"); + + if (isnew) { + printf("[%6D] caps 0x%x bintval %u erp 0x%x", + mac, ":", sp->capinfo, sp->bintval, sp->erp); + if (sp->country != NULL) { +#if defined(__FreeBSD__) || defined(__DragonFly__) + printf(" country info %*D", + sp->country[1], sp->country+2, " "); +#else + int i; + printf(" country info"); + for (i = 0; i < sp->country[1]; i++) + printf(" %02x", sp->country[i+2]); +#endif + } + printf("\n"); + } +} +#endif /* IEEE80211_DEBUG */ + +static void +saveie(uint8_t **iep, const uint8_t *ie) +{ + + if (ie == NULL) + *iep = NULL; + else + ieee80211_saveie(iep, ie); +} + +/* + * Process a beacon or probe response frame. + */ +void +ieee80211_add_scan(struct ieee80211com *ic, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int rstamp) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) + struct ieee80211_node_table *nt = &ic->ic_scan; + struct ieee80211_node *ni; + int newnode = 0; + + ni = ieee80211_find_node(nt, wh->i_addr2); + if (ni == NULL) { + /* + * Create a new entry. + */ + ni = ic->ic_node_alloc(nt); + if (ni == NULL) { + ic->ic_stats.is_rx_nodealloc++; + return; + } + ieee80211_setup_node(nt, ni, wh->i_addr2); + /* + * XXX inherit from ic_bss. + */ + ni->ni_authmode = ic->ic_bss->ni_authmode; + ni->ni_txpower = ic->ic_bss->ni_txpower; + ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */ + ieee80211_set_chan(ic, ni, ic->ic_curchan); + ni->ni_rsn = ic->ic_bss->ni_rsn; + newnode = 1; + } +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN)) + dump_probe_beacon(subtype, newnode, wh->i_addr2, sp); +#endif + /* XXX ap beaconing multiple ssid w/ same bssid */ + if (sp->ssid[1] != 0 && + (ISPROBE(subtype) || ni->ni_esslen == 0)) { + ni->ni_esslen = sp->ssid[1]; + memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); + memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); + } + IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp)); + ni->ni_intval = sp->bintval; + ni->ni_capinfo = sp->capinfo; + ni->ni_chan = &ic->ic_channels[sp->chan]; + ni->ni_fhdwell = sp->fhdwell; + ni->ni_fhindex = sp->fhindex; + ni->ni_erp = sp->erp; + if (sp->tim != NULL) { + struct ieee80211_tim_ie *ie = + (struct ieee80211_tim_ie *) sp->tim; + + ni->ni_dtim_count = ie->tim_count; + ni->ni_dtim_period = ie->tim_period; + } + /* + * Record the byte offset from the mac header to + * the start of the TIM information element for + * use by hardware and/or to speedup software + * processing of beacon frames. + */ + ni->ni_timoff = sp->timoff; + /* + * Record optional information elements that might be + * used by applications or drivers. + */ + saveie(&ni->ni_wme_ie, sp->wme); + saveie(&ni->ni_wpa_ie, sp->wpa); + + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT); + + if (!newnode) + ieee80211_free_node(ni); +#undef ISPROBE +} + +void +ieee80211_init_neighbor(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const struct ieee80211_scanparams *sp) +{ + + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s: %p<%s>\n", __func__, ni, ni->ni_macaddr, ":"); + ni->ni_esslen = sp->ssid[1]; + memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); + IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); + memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp)); + ni->ni_intval = sp->bintval; + ni->ni_capinfo = sp->capinfo; + ni->ni_chan = ni->ni_ic->ic_curchan; + ni->ni_fhdwell = sp->fhdwell; + ni->ni_fhindex = sp->fhindex; + ni->ni_erp = sp->erp; + ni->ni_timoff = sp->timoff; + if (sp->wme != NULL) + ieee80211_saveie(&ni->ni_wme_ie, sp->wme); + if (sp->wpa != NULL) + ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa); + + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT); +} + +/* + * Do node discovery in adhoc mode on receipt of a beacon + * or probe response frame. Note that for the driver's + * benefit we we treat this like an association so the + * driver has an opportunity to setup it's private state. + */ +struct ieee80211_node * +ieee80211_add_neighbor(struct ieee80211com *ic, + const struct ieee80211_frame *wh, + const struct ieee80211_scanparams *sp) +{ + struct ieee80211_node *ni; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s: mac<%s>\n", __func__, wh->i_addr2, ":"); + ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);/* XXX alloc_node? */ + if (ni != NULL) { + ieee80211_init_neighbor(ni, wh, sp); + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, 1); + /* XXX not right for 802.1x/WPA */ + ieee80211_node_authorize(ni); + } + return ni; +} + +#define IS_CTL(wh) \ + ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) +#define IS_PSPOLL(wh) \ + ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) +/* + * Locate the node for sender, track state, and then pass the + * (referenced) node up to the 802.11 layer for its use. We + * are required to pass some node so we fall back to ic_bss + * when this frame is from an unknown sender. The 802.11 layer + * knows this means the sender wasn't in the node table and + * acts accordingly. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_rxnode_debug(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, const char *func, int line) +#else +ieee80211_find_rxnode(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh) +#endif +{ + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + /* XXX may want scanned nodes in the neighbor table for adhoc */ + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_MONITOR || + (ic->ic_flags & IEEE80211_F_SCAN)) + nt = &ic->ic_scan; + else + nt = &ic->ic_sta; + /* XXX check ic_bss first in station mode */ + /* XXX 4-address frames? */ + if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) + ni = _ieee80211_find_node(nt, wh->i_addr1); + else + ni = _ieee80211_find_node(nt, wh->i_addr2); + if (ni == NULL) + ni = ieee80211_ref_node(ic->ic_bss); + + return ni; +} + +/* + * Like ieee80211_find_rxnode but use the supplied h/w + * key index as a hint to locate the node in the key + * mapping table. If an entry is present at the key + * index we return it; otherwise do a normal lookup and + * update the mapping table if the station has a unicast + * key assigned to it. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_rxnode_withkey_debug(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, ieee80211_keyix keyix, + const char *func, int line) +#else +ieee80211_find_rxnode_withkey(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, ieee80211_keyix keyix) +#endif +{ + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_MONITOR || + (ic->ic_flags & IEEE80211_F_SCAN)) + nt = &ic->ic_scan; + else + nt = &ic->ic_sta; + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) + ni = nt->nt_keyixmap[keyix]; + else + ni = NULL; + if (ni == NULL) { + if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) + ni = _ieee80211_find_node(nt, wh->i_addr1); + else + ni = _ieee80211_find_node(nt, wh->i_addr2); + if (ni == NULL) + ni = ieee80211_ref_node(ic->ic_bss); + if (nt->nt_keyixmap != NULL) { + /* + * If the station has a unicast key cache slot + * assigned update the key->node mapping table. + */ + keyix = ni->ni_ucastkey.wk_rxkeyix; + /* XXX can keyixmap[keyix] != NULL? */ + if (keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == NULL) { + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s: add key map entry %p<%6D> refcnt %d\n", + __func__, ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni)+1); + nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni); + } + } + } else { + ieee80211_ref_node(ni); + } + + return ni; +} +#undef IS_PSPOLL +#undef IS_CTL + +/* + * Return a reference to the appropriate node for sending + * a data frame. This handles node discovery in adhoc networks. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr, + const char *func, int line) +#else +ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr) +#endif +{ + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *ni; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + /* + * The destination address should be in the node table + * unless this is a multicast/broadcast frame. We can + * also optimize station mode operation, all frames go + * to the bss node. + */ + if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) + ni = ieee80211_ref_node(ic->ic_bss); + else + ni = _ieee80211_find_node(nt, macaddr); + + if (ni == NULL) { + if (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO) { + /* + * In adhoc mode cons up a node for the destination. + * Note that we need an additional reference for the + * caller to be consistent with _ieee80211_find_node. + */ + ni = ieee80211_fakeup_adhoc_node(nt, macaddr); + if (ni != NULL) + ieee80211_ref_node(ni); + } else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + "[%6D] no node, discard frame (%s)\n", + macaddr, ":", __func__); + ic->ic_stats.is_tx_nonode++; } } return ni; @@ -577,126 +1470,843 @@ ieee80211_find_txnode(struct ieee80211com *ic, uint8_t *macaddr) * Like find but search based on the channel too. */ struct ieee80211_node * -ieee80211_lookup_node(struct ieee80211com *ic, - uint8_t *macaddr, struct ieee80211_channel *chan) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_with_channel_debug(struct ieee80211_node_table *nt, + const uint8_t *macaddr, struct ieee80211_channel *chan, + const char *func, int line) +#else +ieee80211_find_node_with_channel(struct ieee80211_node_table *nt, + const uint8_t *macaddr, struct ieee80211_channel *chan) +#endif { struct ieee80211_node *ni; int hash; - lwkt_tokref ilock; + + ASSERT_SERIALIZED(nt->nt_ic->ic_ifp->if_serializer); hash = IEEE80211_NODE_HASH(macaddr); - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) { - atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ + ieee80211_ref_node(ni); /* mark referenced */ + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, +#ifdef IEEE80211_DEBUG_REFCNT + "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, + func, line, +#else + "%s %p<%6D> refcnt %d\n", __func__, +#endif + ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni)); break; } } - lwkt_reltoken(&ilock); return ni; } +/* + * Like find but search based on the ssid too. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt, + const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid, + const char *func, int line) +#else +ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt, + const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid) +#endif +{ +#define MATCH_SSID(ni, ssid, ssidlen) \ + (ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0) + static const uint8_t zeromac[IEEE80211_ADDR_LEN]; + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + int hash; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + /* + * A mac address that is all zero means match only the ssid; + * otherwise we must match both. + */ + if (IEEE80211_ADDR_EQ(macaddr, zeromac)) { + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + if (MATCH_SSID(ni, ssid, ssidlen)) + break; + } + } else { + hash = IEEE80211_NODE_HASH(macaddr); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && + MATCH_SSID(ni, ssid, ssidlen)) + break; + } + } + if (ni != NULL) { + ieee80211_ref_node(ni); /* mark referenced */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, +#ifdef IEEE80211_DEBUG_REFCNT + "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, + func, line, +#else + "%s %p<%6D> refcnt %d\n", __func__, +#endif + ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni)); + } + return ni; +#undef MATCH_SSID +} + static void -_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +_ieee80211_free_node(struct ieee80211_node *ni) { - KASSERT(ni != ic->ic_bss, ("freeing bss node")); + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node_table *nt = ni->ni_table; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s %p<%6D> in %s table\n", __func__, ni, + ni->ni_macaddr, ":", + nt != NULL ? nt->nt_name : ""); - TAILQ_REMOVE(&ic->ic_node, ni, ni_list); - LIST_REMOVE(ni, ni_hash); - if (TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = 0; - (*ic->ic_node_free)(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + if (nt != NULL) { + TAILQ_REMOVE(&nt->nt_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + } + ic->ic_node_free(ni); } void -ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line) +#else +ieee80211_free_node(struct ieee80211_node *ni) +#endif { - lwkt_tokref ilock; + struct ieee80211_node_table *nt = ni->ni_table; - KASSERT(ni != ic->ic_bss, ("freeing ic_bss")); + ASSERT_SERIALIZED(ni->ni_ic->ic_ifp->if_serializer); - /* XXX DF atomic op */ - crit_enter(); - --ni->ni_refcnt; - if (ni->ni_refcnt == 0) { - crit_exit(); - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - _ieee80211_free_node(ic, ni); - lwkt_reltoken(&ilock); +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, func, line, ni, + ni->ni_macaddr, ":", ieee80211_node_refcnt(ni) - 1); +#endif + if (nt != NULL) { + if (ieee80211_node_dectestref(ni)) { + /* + * Last reference, reclaim state. + */ + _ieee80211_free_node(ni); + } else if (ieee80211_node_refcnt(ni) == 1 && + nt->nt_keyixmap != NULL) { + ieee80211_keyix keyix; + /* + * Check for a last reference in the key mapping table. + */ + keyix = ni->ni_ucastkey.wk_rxkeyix; + if (keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == ni) { + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s: %p<%6D> clear key map entry", __func__, + ni, ni->ni_macaddr, ":"); + nt->nt_keyixmap[keyix] = NULL; + ieee80211_node_decref(ni); /* XXX needed? */ + _ieee80211_free_node(ni); + } + } } else { - crit_exit(); + if (ieee80211_node_dectestref(ni)) + _ieee80211_free_node(ni); } } -void -ieee80211_free_allnodes(struct ieee80211com *ic) +/* + * Reclaim a unicast key and clear any key cache state. + */ +int +ieee80211_node_delucastkey(struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *nikey; + ieee80211_keyix keyix; + int status; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + keyix = ni->ni_ucastkey.wk_rxkeyix; + status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) { + nikey = nt->nt_keyixmap[keyix]; + nt->nt_keyixmap[keyix] = NULL;; + } else + nikey = NULL; + + if (nikey != NULL) { + KASSERT(nikey == ni, + ("key map out of sync, ni %p nikey %p", ni, nikey)); + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s: delete key map entry %p<%6D> refcnt %d\n", + __func__, ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni)-1); + ieee80211_free_node(ni); + } + return status; +} + +/* + * Reclaim a node. If this is the last reference count then + * do the normal free work. Otherwise remove it from the node + * table and mark it gone by clearing the back-reference. + */ +static void +node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) +{ + ieee80211_keyix keyix; + + ASSERT_SERIALIZED(nt->nt_ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s: remove %p<%6D> from %s table, refcnt %d\n", + __func__, ni, ni->ni_macaddr, ":", + nt->nt_name, ieee80211_node_refcnt(ni)-1); + /* + * Clear any entry in the unicast key mapping table. + * We need to do it here so rx lookups don't find it + * in the mapping table even if it's not in the hash + * table. We cannot depend on the mapping table entry + * being cleared because the node may not be free'd. + */ + keyix = ni->ni_ucastkey.wk_rxkeyix; + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == ni) { + IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + "%s: %p<%6D> clear key map entry\n", + __func__, ni, ni->ni_macaddr, ":"); + nt->nt_keyixmap[keyix] = NULL; + ieee80211_node_decref(ni); /* NB: don't need free */ + } + if (!ieee80211_node_dectestref(ni)) { + /* + * Other references are present, just remove the + * node from the table so it cannot be found. When + * the references are dropped storage will be + * reclaimed. + */ + TAILQ_REMOVE(&nt->nt_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + ni->ni_table = NULL; /* clear reference */ + } else + _ieee80211_free_node(ni); +} + +static void +ieee80211_free_allnodes(struct ieee80211_node_table *nt) +{ + struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; - lwkt_tokref ilock; - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) - _ieee80211_free_node(ic, ni); - lwkt_reltoken(&ilock); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s: free all nodes in %s table\n", __func__, nt->nt_name); + + while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) { + if (ni->ni_associd != 0) { + if (ic->ic_auth->ia_node_leave != NULL) + ic->ic_auth->ia_node_leave(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + } + node_reclaim(nt, ni); + } + ieee80211_reset_erp(ic); } /* - * Timeout inactive nodes. Note that we cannot hold the node - * lock while sending a frame as this would lead to a LOR. - * Instead we use a generation number to mark nodes that we've - * scanned and drop the lock and restart a scan if we have to - * time out a node. Since we are single-threaded by virtue of + * Timeout entries in the scan cache. + */ +static void +ieee80211_timeout_scan_candidates(struct ieee80211_node_table *nt) +{ + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni, *tni; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + ni = ic->ic_bss; + /* XXX belongs elsewhere */ + if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + TAILQ_FOREACH_MUTABLE(ni, &nt->nt_node, ni_list, tni) { + if (ni->ni_inact && --ni->ni_inact == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "[%6D] scan candidate purged from cache " + "(refcnt %u)\n", ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni)); + node_reclaim(nt, ni); + } + } + + nt->nt_inact_timer = IEEE80211_INACT_WAIT; +} + +/* + * Timeout inactive stations and do related housekeeping. + * Note that we cannot hold the node lock while sending a + * frame as this would lead to a LOR. Instead we use a + * generation number to mark nodes that we've scanned and + * drop the lock and restart a scan if we have to time out + * a node. Since we are single-threaded by virtue of * controlling the inactivity timer we can be sure this will * process each node only once. */ -void -ieee80211_timeout_nodes(struct ieee80211com *ic) +static void +ieee80211_timeout_stations(struct ieee80211_node_table *nt) { - struct ieee80211_node *ni; - lwkt_tokref ilock; - u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/ + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni, *next; + int isadhoc; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO); -restart: - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if (ni->ni_scangen == gen) /* previously handled */ + TAILQ_FOREACH_MUTABLE(ni, &nt->nt_node, ni_list, next) { + /* + * Ignore entries for which have yet to receive an + * authentication frame. These are transient and + * will be reclaimed when the last reference to them + * goes away (when frame xmits complete). + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (ni->ni_flags & IEEE80211_NODE_AREF) == 0) continue; - ni->ni_scangen = gen; - if (++ni->ni_inact > IEEE80211_INACT_MAX) { - IEEE80211_DPRINTF(("station %6D timed out " - "due to inactivity (%u secs)\n", - ni->ni_macaddr, ":", ni->ni_inact)); + /* + * Free fragment if not needed anymore + * (last fragment older than 1s). + * XXX doesn't belong here + */ + if (ni->ni_rxfrag[0] != NULL && + ticks > ni->ni_rxfragstamp + hz) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + /* + * Special case ourself; we may be idle for extended periods + * of time and regardless reclaiming our state is wrong. + */ + if (ni == ic->ic_bss) + continue; + ni->ni_inact--; + if (ni->ni_associd != 0 || isadhoc) { + /* + * Age frames on the power save queue. The + * aging interval is 4 times the listen + * interval specified by the station. This + * number is factored into the age calculations + * when the frame is placed on the queue. We + * store ages as time differences we can check + * and/or adjust only the head of the list. + */ + if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { + struct mbuf *m; + int discard = 0; + + while (IF_POLL(&ni->ni_savedq, m) != NULL && + M_AGE_GET(m) < IEEE80211_INACT_WAIT) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_POWER, + "[%6D] discard frame, age %u\n", + ni->ni_macaddr, ":", + M_AGE_GET(m));/*XXX*/ + _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); + m_freem(m); + discard++; + } + if (m != NULL) + M_AGE_SUB(m, IEEE80211_INACT_WAIT); + + if (discard != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_POWER, + "[%6D] discard %u frames for age\n", + ni->ni_macaddr, ":", + discard); + IEEE80211_NODE_STAT_ADD(ni, + ps_discard, discard); + if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) + ic->ic_set_tim(ni, 0); + } + } /* - * Send a deauthenticate frame. - * - * Drop the node lock before sending the - * deauthentication frame in case the driver takes - * a lock, as this will result in a LOR between the - * node lock and the driver lock. + * Probe the station before time it out. We + * send a null data frame which may not be + * universally supported by drivers (need it + * for ps-poll support so it should be...). */ - lwkt_reltoken(&ilock); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_EXPIRE); - ieee80211_free_node(ic, ni); + if (0 < ni->ni_inact && + ni->ni_inact <= ic->ic_inact_probe) { + IEEE80211_NOTE(ic, + IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, + ni, "%s", + "probe station due to inactivity"); + /* + * Grab a reference before unlocking the table + * so the node cannot be reclaimed before we + * send the frame. ieee80211_send_nulldata + * understands we've done this and reclaims the + * ref for us as needed. + */ + ieee80211_ref_node(ni); + ieee80211_send_nulldata(ni); + /* XXX stat? */ + continue; + } + } + if (ni->ni_inact <= 0) { + IEEE80211_NOTE(ic, + IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, + "station timed out due to inactivity " + "(refcnt %u)", ieee80211_node_refcnt(ni)); + /* + * Send a deauthenticate frame and drop the station. + * This is somewhat complicated due to reference counts + * and locking. At this point a station will typically + * have a reference count of 1. ieee80211_node_leave + * will do a "free" of the node which will drop the + * reference count. But in the meantime a reference + * wil be held by the deauth frame. The actual reclaim + * of the node will happen either after the tx is + * completed or by ieee80211_node_leave. + */ + if (ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + } + ieee80211_node_leave(ic, ni); ic->ic_stats.is_node_timeout++; - goto restart; + continue; + } + } + + nt->nt_inact_timer = IEEE80211_INACT_WAIT; +} + +void +ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg) +{ + struct ieee80211_node *ni, *next; + + ASSERT_SERIALIZED(nt->nt_ic->ic_ifp->if_serializer); + + TAILQ_FOREACH_MUTABLE(ni, &nt->nt_node, ni_list, next) + f(arg, ni); +} + +void +ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) +{ + printf("0x%p: mac %6D refcnt %d\n", ni, + ni->ni_macaddr, ":", ieee80211_node_refcnt(ni)); + printf("\tauthmode %u flags 0x%x\n", + ni->ni_authmode, ni->ni_flags); + printf("\tassocid 0x%x txpower %u vlan %u\n", + ni->ni_associd, ni->ni_txpower, ni->ni_vlan); + printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n", + ni->ni_txseqs[0], + ni->ni_rxseqs[0] >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseqs[0] & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxfragstamp); + printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n", + ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo); + printf("\tbssid %6D essid \"%.*s\" channel %u:0x%x\n", + ni->ni_bssid, ":", + ni->ni_esslen, ni->ni_essid, + ni->ni_chan->ic_freq, ni->ni_chan->ic_flags); + printf("\tfails %u inact %u txrate %u\n", + ni->ni_fails, ni->ni_inact, ni->ni_txrate); +} + +void +ieee80211_dump_nodes(struct ieee80211_node_table *nt) +{ + ieee80211_iterate_nodes(nt, + (ieee80211_iter_func *) ieee80211_dump_node, nt); +} + +/* + * Handle a station joining an 11g network. + */ +static void +ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + /* + * Station isn't capable of short slot time. Bump + * the count of long slot time stations and disable + * use of short slot time. Note that the actual switch + * over to long slot time use may not occur until the + * next beacon transmission (per sec. 7.3.1.4 of 11g). + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + ic->ic_longslotsta++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] station needs long slot time, count %d\n", + ni->ni_macaddr, ":", ic->ic_longslotsta); + /* XXX vap's w/ conflicting needs won't work */ + ieee80211_set_shortslottime(ic, 0); + } + /* + * If the new station is not an ERP station + * then bump the counter and enable protection + * if configured. + */ + if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) { + ic->ic_nonerpsta++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] station is !ERP, %d non-ERP stations associated\n", + ni->ni_macaddr, ":", ic->ic_nonerpsta); + /* + * If protection is configured, enable it. + */ + if (ic->ic_protmode != IEEE80211_PROT_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: enable use of protection\n", __func__); + ic->ic_flags |= IEEE80211_F_USEPROT; + } + /* + * If station does not support short preamble + * then we must enable use of Barker preamble. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] station needs long preamble\n", + ni->ni_macaddr, ":"); + ic->ic_flags |= IEEE80211_F_USEBARKER; + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + } + if (ic->ic_nonerpsta == 1) + ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE; + } else + ni->ni_flags |= IEEE80211_NODE_ERP; +} + +void +ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp) +{ + int newassoc; + + if (ni->ni_associd == 0) { + uint16_t aid; + + /* + * It would be good to search the bitmap + * more efficiently, but this will do for now. + */ + for (aid = 1; aid < ic->ic_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(aid, + ic->ic_aid_bitmap)) + break; + } + if (aid >= ic->ic_max_aid) { + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_REASON_ASSOC_TOOMANY); + ieee80211_node_leave(ic, ni); + return; + } + ni->ni_associd = aid | 0xc000; + IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); + ic->ic_sta_assoc++; + newassoc = 1; + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_node_join_11g(ic, ni); + } else + newassoc = 0; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, + "[%6D] station %sassociated at aid %d: %s preamble, %s slot time%s%s\n", + ni->ni_macaddr, ":", newassoc ? "" : "re", + IEEE80211_NODE_AID(ni), + ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "", + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "" + ); + + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, newassoc); + ni->ni_inact_reload = ic->ic_inact_auth; + ni->ni_inact = ni->ni_inact_reload; + IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); + /* tell the authenticator about new station */ + if (ic->ic_auth->ia_node_join != NULL) + ic->ic_auth->ia_node_join(ic, ni); + ieee80211_notify_node_join(ic, ni, newassoc); +} + +/* + * Handle a station leaving an 11g network. + */ +static void +ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + KASSERT(ic->ic_curmode == IEEE80211_MODE_11G, + ("not in 11g, bss %u:0x%x, curmode %u", ni->ni_chan->ic_freq, + ni->ni_chan->ic_flags, ic->ic_curmode)); + + /* + * If a long slot station do the slot time bookkeeping. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + KASSERT(ic->ic_longslotsta > 0, + ("bogus long slot station count %d", ic->ic_longslotsta)); + ic->ic_longslotsta--; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] long slot time station leaves, count now %d\n", + ni->ni_macaddr, ":", ic->ic_longslotsta); + if (ic->ic_longslotsta == 0) { + /* + * Re-enable use of short slot time if supported + * and not operating in IBSS mode (per spec). + */ + if ((ic->ic_caps & IEEE80211_C_SHSLOT) && + ic->ic_opmode != IEEE80211_M_IBSS) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: re-enable use of short slot time\n", + __func__); + ieee80211_set_shortslottime(ic, 1); + } + } + } + /* + * If a non-ERP station do the protection-related bookkeeping. + */ + if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) { + KASSERT(ic->ic_nonerpsta > 0, + ("bogus non-ERP station count %d", ic->ic_nonerpsta)); + ic->ic_nonerpsta--; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] non-ERP station leaves, count now %d\n", + ni->ni_macaddr, ":", ic->ic_nonerpsta); + if (ic->ic_nonerpsta == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: disable use of protection\n", __func__); + ic->ic_flags &= ~IEEE80211_F_USEPROT; + /* XXX verify mode? */ + if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: re-enable use of short preamble\n", + __func__); + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } + ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE; } } - if (!TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - lwkt_reltoken(&ilock); } +/* + * Handle bookkeeping for station deauthentication/disassociation + * when operating as an ap. + */ void -ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) +ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) { + struct ieee80211_node_table *nt = ni->ni_table; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, + "[%6D] station with aid %d leaves\n", + ni->ni_macaddr, ":", IEEE80211_NODE_AID(ni)); + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO, + ("unexpected operating mode %u", ic->ic_opmode)); + /* + * If node wasn't previously associated all + * we need to do is reclaim the reference. + */ + /* XXX ibss mode bypasses 11g and notification */ + if (ni->ni_associd == 0) + goto done; + /* + * Tell the authenticator the station is leaving. + * Note that we must do this before yanking the + * association id as the authenticator uses the + * associd to locate it's state block. + */ + if (ic->ic_auth->ia_node_leave != NULL) + ic->ic_auth->ia_node_leave(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + ni->ni_associd = 0; + ic->ic_sta_assoc--; + + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_node_leave_11g(ic, ni); + /* + * Cleanup station state. In particular clear various + * state that might otherwise be reused if the node + * is reused before the reference count goes to zero + * (and memory is reclaimed). + */ + ieee80211_sta_leave(ic, ni); +done: + /* + * Remove the node from any table it's recorded in and + * drop the caller's reference. Removal from the table + * is important to insure the node is not reprocessed + * for inactivity. + */ + if (nt != NULL) + node_reclaim(nt, ni); + else + ieee80211_free_node(ni); +} + +uint8_t +ieee80211_getrssi(struct ieee80211com *ic) +{ +#define NZ(x) ((x) == 0 ? 1 : (x)) + struct ieee80211_node_table *nt = &ic->ic_sta; + uint32_t rssi_samples, rssi_total; struct ieee80211_node *ni; - struct lwkt_tokref ilock; - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) - (*f)(arg, ni); - lwkt_reltoken(&ilock); + rssi_total = 0; + rssi_samples = 0; + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: /* average of all ibss neighbors */ + /* XXX locking */ + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) + if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ni); + } + break; + case IEEE80211_M_AHDEMO: /* average of all neighbors */ + /* XXX locking */ + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ni); + } + break; + case IEEE80211_M_HOSTAP: /* average of all associated stations */ + /* XXX locking */ + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) + if (IEEE80211_AID(ni->ni_associd) != 0) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ni); + } + break; + case IEEE80211_M_MONITOR: /* XXX */ + case IEEE80211_M_STA: /* use stats from associated ap */ + default: + if (ic->ic_bss != NULL) + rssi_total = ic->ic_node_getrssi(ic->ic_bss); + rssi_samples = 1; + break; + } + return rssi_total / NZ(rssi_samples); +#undef NZ +} + +/* + * Indicate whether there are frames queued for a station in power-save mode. + */ +static void +ieee80211_set_tim(struct ieee80211_node *ni, int set) +{ + struct ieee80211com *ic = ni->ni_ic; + uint16_t aid; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS, + ("operating mode %u", ic->ic_opmode)); + + aid = IEEE80211_AID(ni->ni_associd); + KASSERT(aid < ic->ic_max_aid, + ("bogus aid %u, max %u", aid, ic->ic_max_aid)); + + if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { + if (set) { + setbit(ic->ic_tim_bitmap, aid); + ic->ic_ps_pending++; + } else { + clrbit(ic->ic_tim_bitmap, aid); + ic->ic_ps_pending--; + } + ic->ic_flags |= IEEE80211_F_TIMUPDATE; + } +} + +/* + * Node table support. + */ + +static void +ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, + const char *name, int inact, int keyixmax, + void (*timeout)(struct ieee80211_node_table *)) +{ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s %s table, inact %u\n", __func__, name, inact); + + nt->nt_ic = ic; + TAILQ_INIT(&nt->nt_node); + nt->nt_name = name; + nt->nt_inact_init = inact; + nt->nt_timeout = timeout; + nt->nt_keyixmax = keyixmax; + if (nt->nt_keyixmax > 0) { + nt->nt_keyixmap = + malloc(keyixmax * sizeof(struct ieee80211_node *), + M_80211_NODE, M_WAITOK | M_ZERO); + } else { + nt->nt_keyixmap = NULL; + } +} + +void +ieee80211_node_table_reset(struct ieee80211_node_table *nt) +{ + ASSERT_SERIALIZED(nt->nt_ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s %s table\n", __func__, nt->nt_name); + + nt->nt_inact_timer = 0; + ieee80211_free_allnodes(nt); +} + +static void +ieee80211_node_table_cleanup(struct ieee80211_node_table *nt) +{ + ASSERT_SERIALIZED(nt->nt_ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s %s table\n", __func__, nt->nt_name); + + ieee80211_free_allnodes(nt); + if (nt->nt_keyixmap != NULL) { + /* XXX verify all entries are NULL */ + int i; + for (i = 0; i < nt->nt_keyixmax; i++) + if (nt->nt_keyixmap[i] != NULL) { + printf("%s: %s[%u] still active\n", __func__, + nt->nt_name, i); + } + free(nt->nt_keyixmap, M_80211_NODE); + nt->nt_keyixmap = NULL; + } } diff --git a/sys/netproto/802_11/wlan/ieee80211_output.c b/sys/netproto/802_11/wlan/ieee80211_output.c index b6e8d266f1..4637edfe61 100644 --- a/sys/netproto/802_11/wlan/ieee80211_output.c +++ b/sys/netproto/802_11/wlan/ieee80211_output.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,8 +29,8 @@ * (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: src/sys/net80211/ieee80211_output.c,v 1.10 2004/04/02 23:25:39 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_output.c,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.26.2.7 2006/03/23 23:28:43 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_output.c,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ #include "opt_inet.h" @@ -38,34 +38,102 @@ #include #include #include -#include #include -#include -#include #include -#include -#include -#include -#include -#include +#include +#include +#include #include -#include -#include #include -#include #include +#include +#include #include -#include - #ifdef INET #include #include +#include +#include +#endif + +#ifdef IEEE80211_DEBUG +/* + * Decide if an outbound management frame should be + * printed when debugging is enabled. This filters some + * of the less interesting frames that come frequently + * (e.g. beacons). + */ +static __inline int +doprint(struct ieee80211com *ic, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return (ic->ic_opmode == IEEE80211_M_IBSS); + } + return 1; +} #endif +/* + * Set the direction field and address fields of an outgoing + * non-QoS frame. Note this should be called early on in + * constructing a frame as it sets i_fc[1]; other bits can + * then be or'd in. + */ +static void +ieee80211_send_setup(struct ieee80211com *ic, + struct ieee80211_node *ni, + struct ieee80211_frame *wh, + int type, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN]) +{ +#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) + + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, da); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, bssid); + break; + case IEEE80211_M_HOSTAP: + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, sa); + break; + case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ + break; + } + } else { + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, bssid); + } + *(uint16_t *)&wh->i_dur[0] = 0; + /* NB: use non-QoS tid */ + *(uint16_t *)&wh->i_seq[0] = + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; +#undef WH4 +} + /* * Send a management frame to the specified node. The node pointer * must have a reference as the pointer will be passed to the driver @@ -74,14 +142,13 @@ * reference (and potentially free'ing up any associated storage). */ static int -ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, - struct mbuf *m, int type) +ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m, int type, int timer) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_frame *wh; KASSERT(ni != NULL, ("null node")); - ni->ni_inact = 0; /* * Yech, hack alert! We want to pass the node down to the @@ -102,74 +169,391 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, m->m_pkthdr.rcvif = (void *)ni; wh = mtod(m, struct ieee80211_frame *); - wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; - wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - *(uint16_t *)wh->i_dur = 0; - *(uint16_t *)wh->i_seq = - htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); - ni->ni_txseq++; - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); - - if (ifp->if_flags & IFF_DEBUG) { - /* avoid to print too many frames */ - if (ic->ic_opmode == IEEE80211_M_IBSS || + ieee80211_send_setup(ic, ni, wh, + IEEE80211_FC0_TYPE_MGT | type, + ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { + m->m_flags &= ~M_LINK0; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "[%6D] encrypting frame (%s)\n", + wh->i_addr1, ":", __func__); + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } #ifdef IEEE80211_DEBUG - ieee80211_debug > 1 || + /* avoid printing too many frames */ + if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || + ieee80211_msg_dumppkts(ic)) { + printf("[%6D] send %s on channel %u\n", + wh->i_addr1, ":", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ieee80211_chan2ieee(ic, ic->ic_curchan)); + } #endif - (type & IEEE80211_FC0_SUBTYPE_MASK) != - IEEE80211_FC0_SUBTYPE_PROBE_RESP) - if_printf(ifp, "sending %s to %6D on channel %u\n", - ieee80211_mgt_subtype_name[ - (type & IEEE80211_FC0_SUBTYPE_MASK) - >> IEEE80211_FC0_SUBTYPE_SHIFT], - ni->ni_macaddr, ":", - ieee80211_chan2ieee(ic, ni->ni_chan)); + IEEE80211_NODE_STAT(ni, tx_mgmt); + IF_ENQUEUE(&ic->ic_mgtq, m); + if (timer) { + /* + * Set the mgt frame timeout. + */ + ic->ic_mgt_timer = timer; + ifp->if_timer = 1; } + ifp->if_start(ifp); + return 0; +} - IF_ENQUEUE(&ic->ic_mgtq, m); - ifp->if_timer = 1; - (*ifp->if_start)(ifp); +/* + * Send a null data frame to the specified node. + * + * NB: the caller is assumed to have setup a node reference + * for use; this is necessary to deal with a race condition + * when probing for inactive stations. + */ +int +ieee80211_send_nulldata(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct mbuf *m; + struct ieee80211_frame *wh; + + MGETHDR(m, M_NOWAIT, MT_HEADER); + if (m == NULL) { + /* XXX debug msg */ + ic->ic_stats.is_tx_nobuf++; + ieee80211_unref_node(&ni); + return ENOMEM; + } + m->m_pkthdr.rcvif = (void *) ni; + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(ic, ni, wh, + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, + ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + /* NB: power management bit is never sent by an AP */ + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + ic->ic_opmode != IEEE80211_M_HOSTAP) + wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; + m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); + + IEEE80211_NODE_STAT(ni, tx_data); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "[%s] send null data frame on channel %u, pwr mgt %s\n", + ni->ni_macaddr, ":", + ieee80211_chan2ieee(ic, ic->ic_curchan), + wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); + + IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ + ifp->if_start(ifp); + return 0; +} + +/* + * Assign priority to a frame based on any vlan tag assigned + * to the station and/or any Diffserv setting in an IP header. + * Finally, if an ACM policy is setup (in station mode) it's + * applied. + */ +int +ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) +{ + int v_wme_ac = 0, d_wme_ac, ac; +#ifdef INET + struct ether_header *eh; +#endif + + if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { + ac = WME_AC_BE; + goto done; + } + +#ifdef FREEBSD_VLAN + /* + * If node has a vlan tag then all traffic + * to it must have a matching tag. + */ + v_wme_ac = 0; + if (ni->ni_vlan != 0) { + struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m); + if (mtag == NULL) { + IEEE80211_NODE_STAT(ni, tx_novlantag); + return 1; + } + if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) != + EVL_VLANOFTAG(ni->ni_vlan)) { + IEEE80211_NODE_STAT(ni, tx_vlanmismatch); + return 1; + } + /* map vlan priority to AC */ + switch (EVL_PRIOFTAG(ni->ni_vlan)) { + case 1: + case 2: + v_wme_ac = WME_AC_BK; + break; + case 0: + case 3: + v_wme_ac = WME_AC_BE; + break; + case 4: + case 5: + v_wme_ac = WME_AC_VI; + break; + case 6: + case 7: + v_wme_ac = WME_AC_VO; + break; + } + } +#endif /* FREEBSD_VLAN */ + +#ifdef INET + eh = mtod(m, struct ether_header *); + if (eh->ether_type == htons(ETHERTYPE_IP)) { + const struct ip *ip = (struct ip *) + (mtod(m, uint8_t *) + sizeof (*eh)); + /* + * IP frame, map the TOS field. + */ + switch (ip->ip_tos) { + case 0x08: + case 0x20: + d_wme_ac = WME_AC_BK; /* background */ + break; + case 0x28: + case 0xa0: + d_wme_ac = WME_AC_VI; /* video */ + break; + case 0x30: /* voice */ + case 0xe0: + case 0x88: /* XXX UPSD */ + case 0xb8: + d_wme_ac = WME_AC_VO; + break; + default: + d_wme_ac = WME_AC_BE; + break; + } + } else { +#endif /* INET */ + d_wme_ac = WME_AC_BE; +#ifdef INET + } +#endif + /* + * Use highest priority AC. + */ + if (v_wme_ac > d_wme_ac) + ac = v_wme_ac; + else + ac = d_wme_ac; + + /* + * Apply ACM policy. + */ + if (ic->ic_opmode == IEEE80211_M_STA) { + static const int acmap[4] = { + WME_AC_BK, /* WME_AC_BE */ + WME_AC_BK, /* WME_AC_BK */ + WME_AC_BE, /* WME_AC_VI */ + WME_AC_VI, /* WME_AC_VO */ + }; + while (ac != WME_AC_BK && + ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) + ac = acmap[ac]; + } +done: + M_WME_SETAC(m, ac); return 0; } /* - * Encapsulate an outbound data frame. The mbuf chain is updated and - * a reference to the destination node is returned. If an error is - * encountered NULL is returned and the node reference will also be NULL. - * - * NB: The caller is responsible for free'ing a returned node reference. - * The convention is ic_bss is not reference counted; the caller must - * maintain that. + * Insure there is sufficient contiguous space to encapsulate the + * 802.11 data frame. If room isn't already there, arrange for it. + * Drivers and cipher modules assume we have done the necessary work + * and fail rudely if they don't find the space they need. + */ +static struct mbuf * +ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, + struct ieee80211_key *key, struct mbuf *m) +{ +#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) + int needed_space = hdrsize; + + if (key != NULL) { + /* XXX belongs in crypto code? */ + needed_space += key->wk_cipher->ic_header; + /* XXX frags */ + /* + * When crypto is being done in the host we must insure + * the data are writable for the cipher routines; clone + * a writable mbuf chain. + * XXX handle SWMIC specially + */ + if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) { + m = ieee80211_mbuf_clone(m, MB_DONTWAIT); + if (m == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + "%s: cannot get writable mbuf\n", __func__); + ic->ic_stats.is_tx_nobuf++; /* XXX new stat */ + return NULL; + } + } + } + /* + * We know we are called just before stripping an Ethernet + * header and prepending an LLC header. This means we know + * there will be + * sizeof(struct ether_header) - sizeof(struct llc) + * bytes recovered to which we need additional space for the + * 802.11 header and any crypto header. + */ + /* XXX check trailing space and copy instead? */ + if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { + struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); + if (n == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + "%s: cannot expand storage\n", __func__); + ic->ic_stats.is_tx_nobuf++; + m_freem(m); + return NULL; + } + KASSERT(needed_space <= MHLEN, + ("not enough room, need %u got %zu\n", needed_space, MHLEN)); + /* + * Setup new mbuf to have leading space to prepend the + * 802.11 header and any crypto header bits that are + * required (the latter are added when the driver calls + * back to ieee80211_crypto_encap to do crypto encapsulation). + */ + /* NB: must be first 'cuz it clobbers m_data */ + m_move_pkthdr(n, m); + n->m_len = 0; /* NB: m_gethdr does not set */ + n->m_data += needed_space; + /* + * Pull up Ethernet header to create the expected layout. + * We could use m_pullup but that's overkill (i.e. we don't + * need the actual data) and it cannot fail so do it inline + * for speed. + */ + /* NB: struct ether_header is known to be contiguous */ + n->m_len += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_data += sizeof(struct ether_header); + /* + * Replace the head of the chain. + */ + n->m_next = m; + m = n; + } + return m; +#undef TO_BE_RECLAIMED +} + +#define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none) +/* + * Return the transmit key to use in sending a unicast frame. + * If a unicast key is set we use that. When no unicast key is set + * we fall back to the default transmit key. + */ +static __inline struct ieee80211_key * +ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (KEY_UNDEFINED(ni->ni_ucastkey)) { + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || + KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) + return NULL; + return &ic->ic_nw_keys[ic->ic_def_txkey]; + } else { + return &ni->ni_ucastkey; + } +} + +/* + * Return the transmit key to use in sending a multicast frame. + * Multicast traffic always uses the group key which is installed as + * the default tx key. + */ +static __inline struct ieee80211_key * +ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || + KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) + return NULL; + return &ic->ic_nw_keys[ic->ic_def_txkey]; +} + +/* + * Encapsulate an outbound data frame. The mbuf chain is updated. + * If an error is encountered NULL is returned. The caller is required + * to provide a node reference and pullup the ethernet header in the + * first mbuf. */ struct mbuf * -ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) +ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) { - struct ieee80211com *ic = (void *)ifp; struct ether_header eh; struct ieee80211_frame *wh; - struct ieee80211_node *ni = NULL; + struct ieee80211_key *key; struct llc *llc; + int hdrsize, datalen, addqos; - if (m->m_len < sizeof(struct ether_header)) { - m = m_pullup(m, sizeof(struct ether_header)); - if (m == NULL) { - ic->ic_stats.is_tx_nombuf++; - goto bad; - } - } + KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); - ni = ieee80211_find_txnode(ic, eh.ether_dhost); - if (ni == NULL) { - IEEE80211_DPRINTF(("%s: no node for dst %6D, discard frame\n", - __func__, eh.ether_dhost, ":")); - ic->ic_stats.is_tx_nonode++; + /* + * Insure space for additional headers. First identify + * transmit key to use in calculating any buffer adjustments + * required. This is also used below to do privacy + * encapsulation work. Then calculate the 802.11 header + * size and any padding required by the driver. + * + * Note key may be NULL if we fall back to the default + * transmit key and that is not set. In that case the + * buffer may not be expanded as needed by the cipher + * routines, but they will/should discard it. + */ + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if (ic->ic_opmode == IEEE80211_M_STA || + !IEEE80211_IS_MULTICAST(eh.ether_dhost)) + key = ieee80211_crypto_getucastkey(ic, ni); + else + key = ieee80211_crypto_getmcastkey(ic, ni); + if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] no default transmit key (%s) deftxkey %u\n", + eh.ether_dhost, ":", __func__, + ic->ic_def_txkey); + ic->ic_stats.is_tx_nodefkey++; + } + } else + key = NULL; + /* XXX 4-address format */ + /* + * XXX Some ap's don't handle QoS-encapsulated EAPOL + * frames so suppress use. This may be an issue if other + * ap's require all data frames to be QoS-encapsulated + * once negotiated in which case we'll need to make this + * configurable. + */ + addqos = (ni->ni_flags & IEEE80211_NODE_QOS) && + eh.ether_type != htons(ETHERTYPE_PAE); + if (addqos) + hdrsize = sizeof(struct ieee80211_qosframe); + else + hdrsize = sizeof(struct ieee80211_frame); + if (ic->ic_flags & IEEE80211_F_DATAPAD) + hdrsize = roundup(hdrsize, sizeof(uint32_t)); + m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); + if (m == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; } - ni->ni_inact = 0; + /* NB: this could be optimized because of ieee80211_mbuf_adjust */ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; @@ -178,17 +562,16 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) llc->llc_snap.org_code[1] = 0; llc->llc_snap.org_code[2] = 0; llc->llc_snap.ether_type = eh.ether_type; - M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT); + datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ + + M_PREPEND(m, hdrsize, MB_DONTWAIT); if (m == NULL) { - ic->ic_stats.is_tx_nombuf++; + ic->ic_stats.is_tx_nobuf++; goto bad; } wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; *(uint16_t *)wh->i_dur = 0; - *(uint16_t *)wh->i_seq = - htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); - ni->ni_txseq++; switch (ic->ic_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; @@ -201,7 +584,11 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); - IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + /* + * NB: always use the bssid from ic_bss as the + * neighbor's may be stale after an ibss merge + */ + IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); break; case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; @@ -212,14 +599,58 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) case IEEE80211_M_MONITOR: goto bad; } - *pni = ni; + if (m->m_flags & M_MORE_DATA) + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + if (addqos) { + struct ieee80211_qosframe *qwh = + (struct ieee80211_qosframe *) wh; + int ac, tid; + + ac = M_WME_GETAC(m); + /* map from access class/queue to 11e header priorty value */ + tid = WME_AC_TO_TID(ac); + qwh->i_qos[0] = tid & IEEE80211_QOS_TID; + if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) + qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S; + qwh->i_qos[1] = 0; + qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + + *(uint16_t *)wh->i_seq = + htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[tid]++; + } else { + *(uint16_t *)wh->i_seq = + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; + } + if (key != NULL) { + /* + * IEEE 802.1X: send EAPOL frames always in the clear. + * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. + */ + if (eh.ether_type != htons(ETHERTYPE_PAE) || + ((ic->ic_flags & IEEE80211_F_WPA) && + (ic->ic_opmode == IEEE80211_M_STA ? + !KEY_UNDEFINED(*key) : !KEY_UNDEFINED(ni->ni_ucastkey)))) { + wh->i_fc[1] |= IEEE80211_FC1_WEP; + /* XXX do fragmentation */ + if (!ieee80211_crypto_enmic(ic, key, m, 0)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + "[%6D] enmic failed, discard frame\n", + eh.ether_dhost, ":"); + ic->ic_stats.is_crypto_enmicfail++; + goto bad; + } + } + } + + IEEE80211_NODE_STAT(ni, tx_data); + IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); + return m; bad: if (m != NULL) m_freem(m); - if (ni && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); - *pni = NULL; return NULL; } @@ -262,7 +693,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) /* * Add an ssid elemet to a frame. */ -static uint8_t * +uint8_t * ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) { *frm++ = IEEE80211_ELEMID_SSID; @@ -271,17 +702,386 @@ ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) return frm + len; } -static struct mbuf * -ieee80211_getmbuf(int flags, int type, u_int pktlen) +/* + * Add an erp element to a frame. + */ +static uint8_t * +ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) +{ + uint8_t erp; + + *frm++ = IEEE80211_ELEMID_ERP; + *frm++ = 1; + erp = 0; + if (ic->ic_nonerpsta != 0) + erp |= IEEE80211_ERP_NON_ERP_PRESENT; + if (ic->ic_flags & IEEE80211_F_USEPROT) + erp |= IEEE80211_ERP_USE_PROTECTION; + if (ic->ic_flags & IEEE80211_F_USEBARKER) + erp |= IEEE80211_ERP_LONG_PREAMBLE; + *frm++ = erp; + return frm; +} + +static uint8_t * +ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie) +{ +#define WPA_OUI_BYTES 0x00, 0x50, 0xf2 +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDSELECTOR(frm, sel) do { \ + memcpy(frm, sel, 4); \ + frm += 4; \ +} while (0) + static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; + static const uint8_t cipher_suite[][4] = { + { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */ + { WPA_OUI_BYTES, WPA_CSE_TKIP }, + { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */ + { WPA_OUI_BYTES, WPA_CSE_CCMP }, + { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ + { WPA_OUI_BYTES, WPA_CSE_NULL }, + }; + static const uint8_t wep104_suite[4] = + { WPA_OUI_BYTES, WPA_CSE_WEP104 }; + static const uint8_t key_mgt_unspec[4] = + { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; + static const uint8_t key_mgt_psk[4] = + { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + uint8_t *frm = ie; + uint8_t *selcnt; + + *frm++ = IEEE80211_ELEMID_VENDOR; + *frm++ = 0; /* length filled in below */ + memcpy(frm, oui, sizeof(oui)); /* WPA OUI */ + frm += sizeof(oui); + ADDSHORT(frm, WPA_VERSION); + + /* XXX filter out CKIP */ + + /* multicast cipher */ + if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && + rsn->rsn_mcastkeylen >= 13) + ADDSELECTOR(frm, wep104_suite); + else + ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); + + /* unicast cipher list */ + selcnt = frm; + ADDSHORT(frm, 0); /* selector count */ + if (rsn->rsn_ucastcipherset & (1<rsn_ucastcipherset & (1<rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_unspec); + } + if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_psk); + } + + /* optional capabilities */ + if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH) + ADDSHORT(frm, rsn->rsn_caps); + + /* calculate element length */ + ie[1] = frm - ie - 2; + KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), + ("WPA IE too big, %u > %zu", + ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + return frm; +#undef ADDSHORT +#undef ADDSELECTOR +#undef WPA_OUI_BYTES +} + +static uint8_t * +ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie) { +#define RSN_OUI_BYTES 0x00, 0x0f, 0xac +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDSELECTOR(frm, sel) do { \ + memcpy(frm, sel, 4); \ + frm += 4; \ +} while (0) + static const uint8_t cipher_suite[][4] = { + { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */ + { RSN_OUI_BYTES, RSN_CSE_TKIP }, + { RSN_OUI_BYTES, RSN_CSE_WRAP }, + { RSN_OUI_BYTES, RSN_CSE_CCMP }, + { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ + { RSN_OUI_BYTES, RSN_CSE_NULL }, + }; + static const uint8_t wep104_suite[4] = + { RSN_OUI_BYTES, RSN_CSE_WEP104 }; + static const uint8_t key_mgt_unspec[4] = + { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; + static const uint8_t key_mgt_psk[4] = + { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + uint8_t *frm = ie; + uint8_t *selcnt; + + *frm++ = IEEE80211_ELEMID_RSN; + *frm++ = 0; /* length filled in below */ + ADDSHORT(frm, RSN_VERSION); + + /* XXX filter out CKIP */ + + /* multicast cipher */ + if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && + rsn->rsn_mcastkeylen >= 13) + ADDSELECTOR(frm, wep104_suite); + else + ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); + + /* unicast cipher list */ + selcnt = frm; + ADDSHORT(frm, 0); /* selector count */ + if (rsn->rsn_ucastcipherset & (1<rsn_ucastcipherset & (1<rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_unspec); + } + if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_psk); + } + + /* optional capabilities */ + ADDSHORT(frm, rsn->rsn_caps); + /* XXX PMKID */ + + /* calculate element length */ + ie[1] = frm - ie - 2; + KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), + ("RSN IE too big, %u > %zu", + ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + return frm; +#undef ADDSELECTOR +#undef ADDSHORT +#undef RSN_OUI_BYTES +} + +/* + * Add a WPA/RSN element to a frame. + */ +static uint8_t * +ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic) +{ + + KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); + if (ic->ic_flags & IEEE80211_F_WPA2) + frm = ieee80211_setup_rsn_ie(ic, frm); + if (ic->ic_flags & IEEE80211_F_WPA1) + frm = ieee80211_setup_wpa_ie(ic, frm); + return frm; +} + +#define WME_OUI_BYTES 0x00, 0x50, 0xf2 +/* + * Add a WME information element to a frame. + */ +static uint8_t * +ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) +{ + static const struct ieee80211_wme_info info = { + .wme_id = IEEE80211_ELEMID_VENDOR, + .wme_len = sizeof(struct ieee80211_wme_info) - 2, + .wme_oui = { WME_OUI_BYTES }, + .wme_type = WME_OUI_TYPE, + .wme_subtype = WME_INFO_OUI_SUBTYPE, + .wme_version = WME_VERSION, + .wme_info = 0, + }; + memcpy(frm, &info, sizeof(info)); + return frm + sizeof(info); +} + +/* + * Add a WME parameters element to a frame. + */ +static uint8_t * +ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) +{ +#define SM(_v, _f) (((_v) << _f##_S) & _f) +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + /* NB: this works 'cuz a param has an info at the front */ + static const struct ieee80211_wme_info param = { + .wme_id = IEEE80211_ELEMID_VENDOR, + .wme_len = sizeof(struct ieee80211_wme_param) - 2, + .wme_oui = { WME_OUI_BYTES }, + .wme_type = WME_OUI_TYPE, + .wme_subtype = WME_PARAM_OUI_SUBTYPE, + .wme_version = WME_VERSION, + }; + int i; + + memcpy(frm, ¶m, sizeof(param)); + frm += __offsetof(struct ieee80211_wme_info, wme_info); + *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ + *frm++ = 0; /* reserved field */ + for (i = 0; i < WME_NUM_AC; i++) { + const struct wmeParams *ac = + &wme->wme_bssChanParams.cap_wmeParams[i]; + *frm++ = SM(i, WME_PARAM_ACI) + | SM(ac->wmep_acm, WME_PARAM_ACM) + | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) + ; + *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) + | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) + ; + ADDSHORT(frm, ac->wmep_txopLimit); + } + return frm; +#undef SM +#undef ADDSHORT +} +#undef WME_OUI_BYTES + +/* + * Send a probe request frame with the specified ssid + * and any optional information element data. + */ +int +ieee80211_send_probereq(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t *ssid, size_t ssidlen, + const void *optie, size_t optielen) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + enum ieee80211_phymode mode; + struct ieee80211_frame *wh; struct mbuf *m; + uint8_t *frm; - KASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen)); - if (pktlen <= MHLEN) - MGETHDR(m, flags, type); + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n", + __func__, __LINE__, + ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni) + 1); + ieee80211_ref_node(ni); + + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] user-specified ie's + */ + m = ieee80211_getmgtframe(&frm, + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (optie != NULL ? optielen : 0) + ); + if (m == NULL) { + ic->ic_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } + + frm = ieee80211_add_ssid(frm, ssid, ssidlen); + mode = ieee80211_chan2mode(ic, ic->ic_curchan); + frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); + frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); + + if (optie != NULL) { + memcpy(frm, optie, optielen); + frm += optielen; + } + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT); + if (m == NULL) + return ENOMEM; + KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); + m->m_pkthdr.rcvif = (void *)ni; + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(ic, ni, wh, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, + sa, da, bssid); + /* XXX power management? */ + + IEEE80211_NODE_STAT(ni, tx_probereq); + IEEE80211_NODE_STAT(ni, tx_mgmt); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "[%6D] send probe req on channel %u\n", + wh->i_addr1, ":", + ieee80211_chan2ieee(ic, ic->ic_curchan)); + + IF_ENQUEUE(&ic->ic_mgtq, m); + ifp->if_start(ifp); + return 0; +} + +/* + * Calculate capability information for mgt frames. + */ +static uint16_t +getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) +{ + uint16_t capinfo; + + KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode")); + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + capinfo = IEEE80211_CAPINFO_ESS; + else if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; else - m = m_getcl(flags, type, M_PKTHDR); - return m; + capinfo = 0; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + return capinfo; } /* @@ -294,12 +1094,10 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, int type, int arg) { #define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) - struct ifnet *ifp = &ic->ic_if; struct mbuf *m; uint8_t *frm; - enum ieee80211_phymode mode; uint16_t capinfo; - int ret, timer; + int has_challenge, is_shared_key, ret, timer, status; KASSERT(ni != NULL, ("null node")); @@ -308,34 +1106,15 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * the xmit is complete all the way in the driver. On error we * will remove our reference. */ - if (ni != ic->ic_bss) - ieee80211_ref_node(ni); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n", + __func__, __LINE__, + ni, ni->ni_macaddr, ":", + ieee80211_node_refcnt(ni) + 1); + ieee80211_ref_node(ni); + timer = 0; switch (type) { - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - /* - * prreq frame format - * [tlv] ssid - * [tlv] supported rates - * [tlv] extended supported rates - */ - m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA, - 2 + ic->ic_des_esslen - + 2 + IEEE80211_RATE_SIZE - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); - if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, uint8_t *); - frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); - mode = ieee80211_chan2mode(ic, ni->ni_chan); - frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); - frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); - m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); - - timer = IEEE80211_TRANS_WAIT; - break; - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: /* * probe response frame format @@ -346,39 +1125,40 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] supported rates * [tlv] parameter set (FH/DS) * [tlv] parameter set (IBSS) + * [tlv] extended rate phy (ERP) * [tlv] extended supported rates + * [tlv] WPA + * [tlv] WME (optional) */ - m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA, - 8 + 2 + 2 + 2 - + 2 + ni->ni_esslen + m = ieee80211_getmgtframe(&frm, + 8 + + sizeof(uint16_t) + + sizeof(uint16_t) + + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE - + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3) + + 7 /* max(7,3) */ + 6 - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 3 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + /* XXX !WPA1+WPA2 fits w/o a cluster */ + + (ic->ic_flags & IEEE80211_F_WPA ? + 2*sizeof(struct ieee80211_ie_wpa) : 0) + + sizeof(struct ieee80211_wme_param) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, uint8_t *); + senderr(ENOMEM, is_tx_nobuf); memset(frm, 0, 8); /* timestamp should be filled later */ frm += 8; *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval); frm += 2; - if (ic->ic_opmode == IEEE80211_M_IBSS) - capinfo = IEEE80211_CAPINFO_IBSS; - else - capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) - capinfo |= IEEE80211_CAPINFO_PRIVACY; - if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && - IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) - capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + capinfo = getcapinfo(ic, ic->ic_curchan); *(uint16_t *)frm = htole16(capinfo); frm += 2; frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen); - frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); + frm = ieee80211_add_rates(frm, &ni->ni_rates); if (ic->ic_phytype == IEEE80211_T_FH) { *frm++ = IEEE80211_ELEMID_FHPARMS; @@ -386,58 +1166,105 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, *frm++ = ni->ni_fhdwell & 0x00ff; *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; *frm++ = IEEE80211_FH_CHANSET( - ieee80211_chan2ieee(ic, ni->ni_chan)); + ieee80211_chan2ieee(ic, ic->ic_curchan)); *frm++ = IEEE80211_FH_CHANPAT( - ieee80211_chan2ieee(ic, ni->ni_chan)); + ieee80211_chan2ieee(ic, ic->ic_curchan)); *frm++ = ni->ni_fhindex; } else { *frm++ = IEEE80211_ELEMID_DSPARMS; *frm++ = 1; - *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); } if (ic->ic_opmode == IEEE80211_M_IBSS) { *frm++ = IEEE80211_ELEMID_IBSSPARMS; *frm++ = 2; *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ - } else { /* IEEE80211_M_HOSTAP */ - /* TODO: TIM */ - *frm++ = IEEE80211_ELEMID_TIM; - *frm++ = 4; /* length */ - *frm++ = 0; /* DTIM count */ - *frm++ = 1; /* DTIM period */ - *frm++ = 0; /* bitmap control */ - *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ } - frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); + if (ic->ic_flags & IEEE80211_F_WPA) + frm = ieee80211_add_wpa(frm, ic); + if (ic->ic_curmode == IEEE80211_MODE_11G) + frm = ieee80211_add_erp(frm, ic); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if (ic->ic_flags & IEEE80211_F_WME) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_AUTH: - MGETHDR(m, MB_DONTWAIT, MT_DATA); + status = arg >> 16; + arg &= 0xffff; + has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + ni->ni_challenge != NULL); + + /* + * Deduce whether we're doing open authentication or + * shared key authentication. We do the latter if + * we're in the middle of a shared key authentication + * handshake or if we're initiating an authentication + * request and configured to use shared key. + */ + is_shared_key = has_challenge || + arg >= IEEE80211_AUTH_SHARED_RESPONSE || + (arg == IEEE80211_AUTH_SHARED_REQUEST && + ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + + m = ieee80211_getmgtframe(&frm, + 3 * sizeof(uint16_t) + + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? + sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - MH_ALIGN(m, 2 * 3); - m->m_pkthdr.len = m->m_len = 6; - frm = mtod(m, uint8_t *); - /* TODO: shared key auth */ - ((uint16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN); + senderr(ENOMEM, is_tx_nobuf); + + ((uint16_t *)frm)[0] = + (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) + : htole16(IEEE80211_AUTH_ALG_OPEN); ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ - ((uint16_t *)frm)[2] = 0; /* status */ + ((uint16_t *)frm)[2] = htole16(status);/* status */ + + if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { + ((uint16_t *)frm)[3] = + htole16((IEEE80211_CHALLENGE_LEN << 8) | + IEEE80211_ELEMID_CHALLENGE); + memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + m->m_pkthdr.len = m->m_len = + 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; + if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "[%6D] request encrypt frame (%s)\n", + ni->ni_macaddr, ":", __func__); + m->m_flags |= M_LINK0; /* WEP-encrypt, please */ + } + } else + m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); + + /* XXX not right for shared key */ + if (status == IEEE80211_STATUS_SUCCESS) + IEEE80211_NODE_STAT(ni, tx_auth); + else + IEEE80211_NODE_STAT(ni, tx_auth_fail); + if (ic->ic_opmode == IEEE80211_M_STA) timer = IEEE80211_TRANS_WAIT; break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %6D deauthenticate (reason %d)\n", - ni->ni_macaddr, ":", arg); - MGETHDR(m, MB_DONTWAIT, MT_DATA); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "[%6D] send station deauthenticate (reason %d)\n", + ni->ni_macaddr, ":", arg); + m = ieee80211_getmgtframe(&frm, sizeof(uint16_t)); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - MH_ALIGN(m, 2); - m->m_pkthdr.len = m->m_len = 2; - *mtod(m, uint16_t *) = htole16(arg); /* reason */ + senderr(ENOMEM, is_tx_nobuf); + *(uint16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(uint16_t); + + IEEE80211_NODE_STAT(ni, tx_deauth); + IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); + + ieee80211_node_unauthorize(ni); /* port closed */ break; case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: @@ -450,34 +1277,36 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME + * [tlv] user-specified ie's */ - m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA, - sizeof(capinfo) + m = ieee80211_getmgtframe(&frm, + sizeof(uint16_t) + sizeof(uint16_t) + IEEE80211_ADDR_LEN - + 2 + ni->ni_esslen + + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_wme_info) + + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, uint8_t *); + senderr(ENOMEM, is_tx_nobuf); - capinfo = 0; - if (ic->ic_opmode == IEEE80211_M_IBSS) - capinfo |= IEEE80211_CAPINFO_IBSS; - else /* IEEE80211_M_STA */ - capinfo |= IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) + KASSERT(ic->ic_opmode == IEEE80211_M_STA, + ("wrong mode %u", ic->ic_opmode)); + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; /* * NB: Some 11a AP's reject the request when * short premable is set. */ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && - IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; - if (ic->ic_flags & IEEE80211_F_SHSLOT) + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) && + (ic->ic_caps & IEEE80211_C_SHSLOT)) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; *(uint16_t *)frm = htole16(capinfo); frm += 2; @@ -493,6 +1322,12 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + if (ic->ic_opt_ie != NULL) { + memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); + frm += ic->ic_opt_ie_len; + } m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); timer = IEEE80211_TRANS_WAIT; @@ -507,67 +1342,376 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [2] association ID * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME (if enabled and STA enabled) */ - m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA, - sizeof(capinfo) + m = ieee80211_getmgtframe(&frm, + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + 2 + IEEE80211_RATE_SIZE - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_wme_param) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, uint8_t *); + senderr(ENOMEM, is_tx_nobuf); - capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) - capinfo |= IEEE80211_CAPINFO_PRIVACY; - if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && - IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) - capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + capinfo = getcapinfo(ic, ic->ic_curchan); *(uint16_t *)frm = htole16(capinfo); frm += 2; *(uint16_t *)frm = htole16(arg); /* status */ frm += 2; - if (arg == IEEE80211_STATUS_SUCCESS) + if (arg == IEEE80211_STATUS_SUCCESS) { *(uint16_t *)frm = htole16(ni->ni_associd); + IEEE80211_NODE_STAT(ni, tx_assoc); + } else + IEEE80211_NODE_STAT(ni, tx_assoc_fail); frm += 2; frm = ieee80211_add_rates(frm, &ni->ni_rates); frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %6D disassociate (reason %d)\n", - ni->ni_macaddr, ":", arg); - MGETHDR(m, MB_DONTWAIT, MT_DATA); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%6D] send station disassociate (reason %d)\n", + ni->ni_macaddr, ":", arg); + m = ieee80211_getmgtframe(&frm, sizeof(uint16_t)); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - MH_ALIGN(m, 2); - m->m_pkthdr.len = m->m_len = 2; - *mtod(m, uint16_t *) = htole16(arg); /* reason */ + senderr(ENOMEM, is_tx_nobuf); + *(uint16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(uint16_t); + + IEEE80211_NODE_STAT(ni, tx_disassoc); + IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); break; default: - IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n", - __func__, type)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%6D] invalid mgmt frame type %u\n", + ni->ni_macaddr, ":", type); senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } - - ret = ieee80211_mgmt_output(ifp, ni, m, type); - if (ret == 0) { - if (timer) - ic->ic_mgt_timer = timer; - } else { + ret = ieee80211_mgmt_output(ic, ni, m, type, timer); + if (ret != 0) { bad: - if (ni != ic->ic_bss) /* remove ref we added */ - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); } return ret; #undef senderr } + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +struct mbuf * +ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_frame *wh; + struct mbuf *m; + int pktlen; + uint8_t *frm, *efrm; + uint16_t capinfo; + struct ieee80211_rateset *rs; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [tlv] parameter set (IBSS/TIM) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] WME parameters + * [tlv] WPA/RSN parameters + * XXX Vendor-specific OIDs (e.g. Atheros) + * NB: we allocate the max space required for the TIM bitmap. + */ + rs = &ni->ni_rates; + pktlen = 8 /* time stamp */ + + sizeof(uint16_t) /* beacon interval */ + + sizeof(uint16_t) /* capabilities */ + + 2 + ni->ni_esslen /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* supported rates */ + + 2 + 1 /* DS parameters */ + + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ + + 2 + 1 /* ERP */ + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (ic->ic_caps & IEEE80211_C_WME ? /* WME */ + sizeof(struct ieee80211_wme_param) : 0) + + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ + 2*sizeof(struct ieee80211_ie_wpa) : 0) + ; + m = ieee80211_getmgtframe(&frm, pktlen); + if (m == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: cannot get buf; size %u\n", __func__, pktlen); + ic->ic_stats.is_tx_nobuf++; + return NULL; + } + + memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ + frm += 8; + *(uint16_t *)frm = htole16(ni->ni_intval); + frm += 2; + capinfo = getcapinfo(ic, ni->ni_chan); + bo->bo_caps = (uint16_t *)frm; + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + *frm++ = IEEE80211_ELEMID_SSID; + if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { + *frm++ = ni->ni_esslen; + memcpy(frm, ni->ni_essid, ni->ni_esslen); + frm += ni->ni_esslen; + } else + *frm++ = 0; + frm = ieee80211_add_rates(frm, rs); + if (ic->ic_curmode != IEEE80211_MODE_FH) { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + bo->bo_tim = frm; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + bo->bo_tim_len = 0; + } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; + + tie->tim_ie = IEEE80211_ELEMID_TIM; + tie->tim_len = 4; /* length */ + tie->tim_count = 0; /* DTIM count */ + tie->tim_period = ic->ic_dtim_period; /* DTIM period */ + tie->tim_bitctl = 0; /* bitmap control */ + tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ + frm += sizeof(struct ieee80211_tim_ie); + bo->bo_tim_len = 1; + } + bo->bo_trailer = frm; + if (ic->ic_flags & IEEE80211_F_WME) { + bo->bo_wme = frm; + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + ic->ic_flags &= ~IEEE80211_F_WMEUPDATE; + } + if (ic->ic_flags & IEEE80211_F_WPA) + frm = ieee80211_add_wpa(frm, ic); + if (ic->ic_curmode == IEEE80211_MODE_11G) { + bo->bo_erp = frm; + frm = ieee80211_add_erp(frm, ic); + } + efrm = ieee80211_add_xrates(frm, rs); + bo->bo_trailer_len = efrm - bo->bo_trailer; + m->m_pkthdr.len = m->m_len = efrm - mtod(m, uint8_t *); + + M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT); + KASSERT(m != NULL, ("no space for 802.11 header?")); + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(uint16_t *)wh->i_dur = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + *(uint16_t *)wh->i_seq = 0; + + return m; +} + +/* + * Update the dynamic parts of a beacon frame based on the current state. + */ +int +ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) +{ + int len_changed = 0; + uint16_t capinfo; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + /* XXX faster to recalculate entirely or just changes? */ + capinfo = getcapinfo(ic, ni->ni_chan); + *bo->bo_caps = htole16(capinfo); + + if (ic->ic_flags & IEEE80211_F_WME) { + struct ieee80211_wme_state *wme = &ic->ic_wme; + + /* + * Check for agressive mode change. When there is + * significant high priority traffic in the BSS + * throttle back BE traffic by using conservative + * parameters. Otherwise BE uses agressive params + * to optimize performance of legacy/non-QoS traffic. + */ + if (wme->wme_flags & WME_F_AGGRMODE) { + if (wme->wme_hipri_traffic > + wme->wme_hipri_switch_thresh) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: traffic %u, disable aggressive mode\n", + __func__, wme->wme_hipri_traffic); + wme->wme_flags &= ~WME_F_AGGRMODE; + ieee80211_wme_updateparams(ic); + wme->wme_hipri_traffic = + wme->wme_hipri_switch_hysteresis; + } else + wme->wme_hipri_traffic = 0; + } else { + if (wme->wme_hipri_traffic <= + wme->wme_hipri_switch_thresh) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: traffic %u, enable aggressive mode\n", + __func__, wme->wme_hipri_traffic); + wme->wme_flags |= WME_F_AGGRMODE; + ieee80211_wme_updateparams(ic); + wme->wme_hipri_traffic = 0; + } else + wme->wme_hipri_traffic = + wme->wme_hipri_switch_hysteresis; + } + if (ic->ic_flags & IEEE80211_F_WMEUPDATE) { + (void) ieee80211_add_wme_param(bo->bo_wme, wme); + ic->ic_flags &= ~IEEE80211_F_WMEUPDATE; + } + } + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ + struct ieee80211_tim_ie *tie = + (struct ieee80211_tim_ie *) bo->bo_tim; + if (ic->ic_flags & IEEE80211_F_TIMUPDATE) { + u_int timlen, timoff, i; + /* + * ATIM/DTIM needs updating. If it fits in the + * current space allocated then just copy in the + * new bits. Otherwise we need to move any trailing + * data to make room. Note that we know there is + * contiguous space because ieee80211_beacon_allocate + * insures there is space in the mbuf to write a + * maximal-size virtual bitmap (based on ic_max_aid). + */ + /* + * Calculate the bitmap size and offset, copy any + * trailer out of the way, and then copy in the + * new bitmap and update the information element. + * Note that the tim bitmap must contain at least + * one byte and any offset must be even. + */ + if (ic->ic_ps_pending != 0) { + timoff = 128; /* impossibly large */ + for (i = 0; i < ic->ic_tim_len; i++) + if (ic->ic_tim_bitmap[i]) { + timoff = i &~ 1; + break; + } + KASSERT(timoff != 128, ("tim bitmap empty!")); + for (i = ic->ic_tim_len-1; i >= timoff; i--) + if (ic->ic_tim_bitmap[i]) + break; + timlen = 1 + (i - timoff); + } else { + timoff = 0; + timlen = 1; + } + if (timlen != bo->bo_tim_len) { + /* copy up/down trailer */ + int adjust = tie->tim_bitmap+timlen + - bo->bo_trailer; + ovbcopy(bo->bo_trailer, bo->bo_trailer+adjust, + bo->bo_trailer_len); + bo->bo_trailer += adjust; + bo->bo_wme += adjust; + bo->bo_erp += adjust; + bo->bo_tim_len = timlen; + + /* update information element */ + tie->tim_len = 3 + timlen; + tie->tim_bitctl = timoff; + len_changed = 1; + } + memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff, + bo->bo_tim_len); + + ic->ic_flags &= ~IEEE80211_F_TIMUPDATE; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "%s: TIM updated, pending %u, off %u, len %u\n", + __func__, ic->ic_ps_pending, timoff, timlen); + } + /* count down DTIM period */ + if (tie->tim_count == 0) + tie->tim_count = tie->tim_period - 1; + else + tie->tim_count--; + /* update state for buffered multicast frames on DTIM */ + if (mcast && tie->tim_count == 0) + tie->tim_bitctl |= 1; + else + tie->tim_bitctl &= ~1; + if (ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) { + /* + * ERP element needs updating. + */ + (void) ieee80211_add_erp(bo->bo_erp, ic); + ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE; + } + } + + return len_changed; +} + +/* + * Save an outbound packet for a node in power-save sleep state. + * The new packet is placed on the node's saved queue, and the TIM + * is changed, if necessary. + */ +void +ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m) +{ + int qlen, age; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + if (IF_QFULL(&ni->ni_savedq)) { + IF_DROP(&ni->ni_savedq); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%6D] pwr save q overflow, drops %d (size %d)\n", + ni->ni_macaddr, ":", + ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumppkts(ic)) + ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1); +#endif + m_freem(m); + return; + } + /* + * Tag the frame with it's expiry time and insert + * it in the queue. The aging interval is 4 times + * the listen interval specified by the station. + * Frames that sit around too long are reclaimed + * using this information. + */ + /* XXX handle overflow? */ + age = ((ni->ni_intval * ic->ic_bintval) << 2) / 1024; /* TU -> secs */ + _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%6D] save frame with age %d, %u now queued\n", + ni->ni_macaddr, ":", age, qlen); + + if (qlen == 1) + ic->ic_set_tim(ni, 1); +} diff --git a/sys/netproto/802_11/wlan/ieee80211_proto.c b/sys/netproto/802_11/wlan/ieee80211_proto.c index e447efee97..8acbb21bbb 100644 --- a/sys/netproto/802_11/wlan/ieee80211_proto.c +++ b/sys/netproto/802_11/wlan/ieee80211_proto.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,8 +29,8 @@ * (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: src/sys/net80211/ieee80211_proto.c,v 1.8 2004/04/02 20:22:25 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_proto.c,v 1.1 2004/07/26 16:30:17 joerg Exp $ + * $FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.17.2.9 2006/03/13 03:10:31 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_proto.c,v 1.2 2006/05/18 13:51:46 sephe Exp $ */ /* @@ -40,35 +40,22 @@ #include "opt_inet.h" #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include +#include +#include +#include + #include -#include -#include #include -#include -#include +#include +#include /* XXX for ether_sprintf */ #include -#include - -#ifdef INET -#include -#include -#endif +/* XXX tunables */ +#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ +#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) @@ -78,6 +65,12 @@ const char *ieee80211_mgt_subtype_name[] = { "beacon", "atim", "disassoc", "auth", "deauth", "reserved#13", "reserved#14", "reserved#15" }; +const char *ieee80211_ctl_subtype_name[] = { + "reserved#0", "reserved#1", "reserved#2", "reserved#3", + "reserved#3", "reserved#5", "reserved#6", "reserved#7", + "reserved#8", "reserved#9", "ps_poll", "rts", + "cts", "ack", "cf_end", "cf_end_ack" +}; const char *ieee80211_state_name[IEEE80211_S_MAX] = { "INIT", /* IEEE80211_S_INIT */ "SCAN", /* IEEE80211_S_SCAN */ @@ -85,24 +78,35 @@ const char *ieee80211_state_name[IEEE80211_S_MAX] = { "ASSOC", /* IEEE80211_S_ASSOC */ "RUN" /* IEEE80211_S_RUN */ }; +const char *ieee80211_wme_acnames[] = { + "WME_AC_BE", + "WME_AC_BK", + "WME_AC_VI", + "WME_AC_VO", + "WME_UPSD", +}; static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); void -ieee80211_proto_attach(struct ifnet *ifp) +ieee80211_proto_attach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; - ifp->if_hdrlen = sizeof(struct ieee80211_frame); + /* XXX room for crypto */ + ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); -#ifdef notdef ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; -#else - ic->ic_rtsthreshold = IEEE80211_RTS_MAX; -#endif - ic->ic_fragthreshold = 2346; /* XXX not used yet */ - ic->ic_fixed_rate = -1; /* no fixed rate */ + ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; + ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; + ic->ic_bmiss_max = IEEE80211_BMISS_MAX; + callout_init(&ic->ic_swbmiss); + ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; ic->ic_protmode = IEEE80211_PROT_CTSONLY; + ic->ic_roaming = IEEE80211_ROAMING_AUTO; + + ic->ic_wme.wme_hipri_switch_hysteresis = + AGGRESSIVE_MODE_SWITCH_HYSTERESIS; /* protocol state change handler */ ic->ic_newstate = ieee80211_newstate; @@ -113,18 +117,124 @@ ieee80211_proto_attach(struct ifnet *ifp) } void -ieee80211_proto_detach(struct ifnet *ifp) +ieee80211_proto_detach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + + /* + * This should not be needed as we detach when reseting + * the state but be conservative here since the + * authenticator may do things like spawn kernel threads. + */ + if (ic->ic_auth->ia_detach) + ic->ic_auth->ia_detach(ic); IF_DRAIN(&ic->ic_mgtq); + + /* + * Detach any ACL'ator. + */ + if (ic->ic_acl != NULL) + ic->ic_acl->iac_detach(ic); +} + +/* + * Simple-minded authenticator module support. + */ + +#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) +/* XXX well-known names */ +static const char *auth_modnames[IEEE80211_AUTH_MAX] = { + "wlan_internal", /* IEEE80211_AUTH_NONE */ + "wlan_internal", /* IEEE80211_AUTH_OPEN */ + "wlan_internal", /* IEEE80211_AUTH_SHARED */ + "wlan_xauth", /* IEEE80211_AUTH_8021X */ + "wlan_internal", /* IEEE80211_AUTH_AUTO */ + "wlan_xauth", /* IEEE80211_AUTH_WPA */ +}; +static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; + +static const struct ieee80211_authenticator auth_internal = { + .ia_name = "wlan_internal", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +/* + * Setup internal authenticators once; they are never unregistered. + */ +static void +ieee80211_auth_setup(void) +{ + ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); +} +SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); + +const struct ieee80211_authenticator * +ieee80211_authenticator_get(int auth) +{ + if (auth >= IEEE80211_AUTH_MAX) + return NULL; + if (authenticators[auth] == NULL) + ieee80211_load_module(auth_modnames[auth]); + return authenticators[auth]; +} + +void +ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *auth) +{ + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = auth; +} + +void +ieee80211_authenticator_unregister(int type) +{ + + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = NULL; +} + +/* + * Very simple-minded ACL module support. + */ +/* XXX just one for now */ +static const struct ieee80211_aclator *acl = NULL; + +void +ieee80211_aclator_register(const struct ieee80211_aclator *iac) +{ + printf("wlan: %s acl policy registered\n", iac->iac_name); + acl = iac; +} + +void +ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) +{ + if (acl == iac) + acl = NULL; + printf("wlan: %s acl policy unregistered\n", iac->iac_name); +} + +const struct ieee80211_aclator * +ieee80211_aclator_get(const char *name) +{ + if (acl == NULL) + ieee80211_load_module("wlan_acl"); + return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; } void -ieee80211_print_essid(uint8_t *essid, int len) +ieee80211_print_essid(const uint8_t *essid, int len) { + const uint8_t *p; int i; - uint8_t *p; if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; @@ -146,28 +256,33 @@ ieee80211_print_essid(uint8_t *essid, int len) } void -ieee80211_dump_pkt(uint8_t *buf, int len, int rate, int rssi) +ieee80211_dump_pkt(const uint8_t *buf, int len, int rate, int rssi) { - struct ieee80211_frame *wh; + const struct ieee80211_frame *wh; int i; - wh = (struct ieee80211_frame *)buf; + wh = (const struct ieee80211_frame *)buf; switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: - printf("NODS %6D->%6D(%6D)", wh->i_addr2, ":", - wh->i_addr1, ":", wh->i_addr3, ":"); + printf("NODS %6D", wh->i_addr2, ":"); + printf("->%6D", wh->i_addr1, ":"); + printf("(%6D)", wh->i_addr3, ":"); break; case IEEE80211_FC1_DIR_TODS: - printf("TODS %6D->%6D(%6D)", wh->i_addr2, ":", - wh->i_addr3, ":", wh->i_addr1, ":"); + printf("TODS %6D", wh->i_addr2, ":"); + printf("->%6D", wh->i_addr3, ":"); + printf("(%6D)", wh->i_addr1, ":"); break; case IEEE80211_FC1_DIR_FROMDS: - printf("FRDS %6D->%6D(%6D)", wh->i_addr3, ":", - wh->i_addr1, ":", wh->i_addr2, ":"); + printf("FRDS %6D", wh->i_addr3, ":"); + printf("->%6D", wh->i_addr1, ":"); + printf("(%6D)", wh->i_addr2, ":"); break; case IEEE80211_FC1_DIR_DSTODS: - printf("DSDS %6D->%6D(%6D->%6D)", (uint8_t *)&wh[1], ":", - wh->i_addr3, ":", wh->i_addr2, ":", wh->i_addr1, ":"); + printf("DSDS %6D", (const uint8_t *)&wh[1], ":"); + printf("->%6D", wh->i_addr3, ":"); + printf("(%6D", wh->i_addr2, ":"); + printf("->%6D)", wh->i_addr1, ":"); break; } switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { @@ -183,8 +298,13 @@ ieee80211_dump_pkt(uint8_t *buf, int len, int rate, int rssi) printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } - if (wh->i_fc[1] & IEEE80211_FC1_WEP) - printf(" WEP"); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + int i; + printf(" WEP [IV"); + for (i = 0; i < IEEE80211_WEP_IVLEN; i++) + printf(" %.02x", buf[sizeof(*wh)+i]); + printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); + } if (rate >= 0) printf(" %dM", rate / 2); if (rssi >= 0) @@ -201,16 +321,24 @@ ieee80211_dump_pkt(uint8_t *buf, int len, int rate, int rssi) } int -ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags) +ieee80211_fix_rate(struct ieee80211_node *ni, int flags) { #define RV(v) ((v) & IEEE80211_RATE_VAL) + struct ieee80211com *ic = ni->ni_ic; int i, j, ignore, error; - int okrate, badrate; + int okrate, badrate, fixedrate; struct ieee80211_rateset *srs, *nrs; uint8_t r; + /* + * If the fixed rate check was requested but no + * fixed has been defined then just remove it. + */ + if ((flags & IEEE80211_F_DOFRATE) && + ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) + flags &= ~IEEE80211_F_DOFRATE; error = 0; - okrate = badrate = 0; + okrate = badrate = fixedrate = 0; srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; nrs = &ni->ni_rates; for (i = 0; i < nrs->rs_nrates; ) { @@ -231,17 +359,10 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags badrate = r; if (flags & IEEE80211_F_DOFRATE) { /* - * Apply fixed rate constraint. Note that we do - * not apply the constraint to basic rates as - * otherwise we may not be able to associate if - * the rate set we submit to the AP is invalid - * (e.g. fix rate at 36Mb/s which is not a basic - * rate for 11a operation). + * Check any fixed rate is included. */ - if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 && - ic->ic_fixed_rate >= 0 && - r != RV(srs->rs_rates[ic->ic_fixed_rate])) - ignore++; + if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) + fixedrate = r; } if (flags & IEEE80211_F_DONEGO) { /* @@ -291,26 +412,504 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags okrate = nrs->rs_rates[i]; i++; } - if (okrate == 0 || error != 0) + if (okrate == 0 || error != 0 || + ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); #undef RV } +/* + * Reset 11g-related state. + */ +void +ieee80211_reset_erp(struct ieee80211com *ic) +{ + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ic->ic_nonerpsta = 0; + ic->ic_longslotsta = 0; + /* + * Short slot time is enabled only when operating in 11g + * and not in an IBSS. We must also honor whether or not + * the driver is capable of doing it. + */ + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ic->ic_curmode == IEEE80211_MODE_11G && + ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_caps & IEEE80211_C_SHSLOT))); + /* + * Set short preamble and ERP barker-preamble flags. + */ + if (ic->ic_curmode == IEEE80211_MODE_11A || + (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } +} + +/* + * Set the short slot time state and notify the driver. + */ +void +ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) +{ + if (onoff) + ic->ic_flags |= IEEE80211_F_SHSLOT; + else + ic->ic_flags &= ~IEEE80211_F_SHSLOT; + /* notify driver */ + if (ic->ic_updateslot != NULL) + ic->ic_updateslot(ic->ic_ifp); +} + +/* + * Check if the specified rate set supports ERP. + * NB: the rate set is assumed to be sorted. + */ +int +ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; + int i, j; + + if (rs->rs_nrates < N(rates)) + return 0; + for (i = 0; i < N(rates); i++) { + for (j = 0; j < rs->rs_nrates; j++) { + int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; + if (rates[i] == r) + goto next; + if (r > rates[i]) + return 0; + } + return 0; + next: + ; + } + return 1; +#undef N +} + +/* + * Mark the basic rates for the 11g rate table based on the + * operating mode. For real 11g we mark all the 11b rates + * and 6, 12, and 24 OFDM. For 11b compatibility we mark only + * 11b rates. There's also a pseudo 11a-mode used to mark only + * the basic OFDM rates. + */ +void +ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) +{ + static const struct ieee80211_rateset basic[] = { + { 0 }, /* IEEE80211_MODE_AUTO */ + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ + { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ + { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ + { 0 }, /* IEEE80211_MODE_FH */ + /* IEEE80211_MODE_PUREG (not yet) */ + { 7, { 2, 4, 11, 22, 12, 24, 48 } }, + }; + int i, j; + + for (i = 0; i < rs->rs_nrates; i++) { + rs->rs_rates[i] &= IEEE80211_RATE_VAL; + for (j = 0; j < basic[mode].rs_nrates; j++) + if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { + rs->rs_rates[i] |= IEEE80211_RATE_BASIC; + break; + } + } +} + +/* + * WME protocol support. The following parameters come from the spec. + */ +typedef struct phyParamType { + uint8_t aifsn; + uint8_t logcwmin; + uint8_t logcwmax; + uint16_t txopLimit; + uint8_t acm; +} paramType; + +static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { + { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ + { 3, 4, 6 }, /* IEEE80211_MODE_11A */ + { 3, 5, 7 }, /* IEEE80211_MODE_11B */ + { 3, 4, 6 }, /* IEEE80211_MODE_11G */ + { 3, 5, 7 }, /* IEEE80211_MODE_FH */ + { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { + { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ + { 7, 4, 10 }, /* IEEE80211_MODE_11A */ + { 7, 5, 10 }, /* IEEE80211_MODE_11B */ + { 7, 4, 10 }, /* IEEE80211_MODE_11G */ + { 7, 5, 10 }, /* IEEE80211_MODE_FH */ + { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ + { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { + { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ + { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ + { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ + { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ + { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ + { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { + { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ + { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ + { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ + { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ + { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ +}; + +static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { + { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ + { 3, 4, 10 }, /* IEEE80211_MODE_11A */ + { 3, 5, 10 }, /* IEEE80211_MODE_11B */ + { 3, 4, 10 }, /* IEEE80211_MODE_11G */ + { 3, 5, 10 }, /* IEEE80211_MODE_FH */ + { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { + { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ + { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ + { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ + { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ + { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ + { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { + { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ + { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ + { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ + { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ + { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ +}; + +void +ieee80211_wme_initparams(struct ieee80211com *ic) +{ + struct ieee80211_wme_state *wme = &ic->ic_wme; + const paramType *pPhyParam, *pBssPhyParam; + struct wmeParams *wmep; + int i; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return; + + for (i = 0; i < WME_NUM_AC; i++) { + switch (i) { + case WME_AC_BK: + pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; + pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; + break; + case WME_AC_VI: + pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; + pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; + break; + case WME_AC_VO: + pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; + pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; + break; + case WME_AC_BE: + default: + pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; + pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; + break; + } + + wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + wmep->wmep_acm = pPhyParam->acm; + wmep->wmep_aifsn = pPhyParam->aifsn; + wmep->wmep_logcwmin = pPhyParam->logcwmin; + wmep->wmep_logcwmax = pPhyParam->logcwmax; + wmep->wmep_txopLimit = pPhyParam->txopLimit; + } else { + wmep->wmep_acm = pBssPhyParam->acm; + wmep->wmep_aifsn = pBssPhyParam->aifsn; + wmep->wmep_logcwmin = pBssPhyParam->logcwmin; + wmep->wmep_logcwmax = pBssPhyParam->logcwmax; + wmep->wmep_txopLimit = pBssPhyParam->txopLimit; + + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " + "log2(cwmax) %u txpoLimit %u]\n", __func__ + , ieee80211_wme_acnames[i] + , wmep->wmep_acm + , wmep->wmep_aifsn + , wmep->wmep_logcwmin + , wmep->wmep_logcwmax + , wmep->wmep_txopLimit + ); + + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; + wmep->wmep_acm = pBssPhyParam->acm; + wmep->wmep_aifsn = pBssPhyParam->aifsn; + wmep->wmep_logcwmin = pBssPhyParam->logcwmin; + wmep->wmep_logcwmax = pBssPhyParam->logcwmax; + wmep->wmep_txopLimit = pBssPhyParam->txopLimit; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " + "log2(cwmax) %u txpoLimit %u]\n", __func__ + , ieee80211_wme_acnames[i] + , wmep->wmep_acm + , wmep->wmep_aifsn + , wmep->wmep_logcwmin + , wmep->wmep_logcwmax + , wmep->wmep_txopLimit + ); + } + /* NB: check ic_bss to avoid NULL deref on initial attach */ + if (ic->ic_bss != NULL) { + /* + * Calculate agressive mode switching threshold based + * on beacon interval. This doesn't need locking since + * we're only called before entering the RUN state at + * which point we start sending beacon frames. + */ + wme->wme_hipri_switch_thresh = + (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; + ieee80211_wme_updateparams(ic); + } +} + +/* + * Update WME parameters for ourself and the BSS. + */ +void +ieee80211_wme_updateparams(struct ieee80211com *ic) +{ + static const paramType phyParam[IEEE80211_MODE_MAX] = { + { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ + { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ + { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ + { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ + { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ + { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ + }; + struct ieee80211_wme_state *wme = &ic->ic_wme; + const struct wmeParams *wmep; + struct wmeParams *chanp, *bssp; + int i; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return; + + /* set up the channel access parameters for the physical device */ + for (i = 0; i < WME_NUM_AC; i++) { + chanp = &wme->wme_chanParams.cap_wmeParams[i]; + wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; + chanp->wmep_aifsn = wmep->wmep_aifsn; + chanp->wmep_logcwmin = wmep->wmep_logcwmin; + chanp->wmep_logcwmax = wmep->wmep_logcwmax; + chanp->wmep_txopLimit = wmep->wmep_txopLimit; + + chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; + chanp->wmep_aifsn = wmep->wmep_aifsn; + chanp->wmep_logcwmin = wmep->wmep_logcwmin; + chanp->wmep_logcwmax = wmep->wmep_logcwmax; + chanp->wmep_txopLimit = wmep->wmep_txopLimit; + } + + /* + * This implements agressive mode as found in certain + * vendors' AP's. When there is significant high + * priority (VI/VO) traffic in the BSS throttle back BE + * traffic by using conservative parameters. Otherwise + * BE uses agressive params to optimize performance of + * legacy/non-QoS traffic. + */ + if ((ic->ic_opmode == IEEE80211_M_HOSTAP && + (wme->wme_flags & WME_F_AGGRMODE) != 0) || + (ic->ic_opmode == IEEE80211_M_STA && + (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || + (ic->ic_flags & IEEE80211_F_WME) == 0) { + chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; + bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; + + chanp->wmep_aifsn = bssp->wmep_aifsn = + phyParam[ic->ic_curmode].aifsn; + chanp->wmep_logcwmin = bssp->wmep_logcwmin = + phyParam[ic->ic_curmode].logcwmin; + chanp->wmep_logcwmax = bssp->wmep_logcwmax = + phyParam[ic->ic_curmode].logcwmax; + chanp->wmep_txopLimit = bssp->wmep_txopLimit = + (ic->ic_flags & IEEE80211_F_BURST) ? + phyParam[ic->ic_curmode].txopLimit : 0; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s [acm %u aifsn %u log2(cwmin) %u " + "log2(cwmax) %u txpoLimit %u]\n", __func__ + , ieee80211_wme_acnames[WME_AC_BE] + , chanp->wmep_acm + , chanp->wmep_aifsn + , chanp->wmep_logcwmin + , chanp->wmep_logcwmax + , chanp->wmep_txopLimit + ); + } + + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { + static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { + 3, /* IEEE80211_MODE_AUTO */ + 3, /* IEEE80211_MODE_11A */ + 4, /* IEEE80211_MODE_11B */ + 3, /* IEEE80211_MODE_11G */ + 4, /* IEEE80211_MODE_FH */ + 3, /* IEEE80211_MODE_TURBO_A */ + 3, /* IEEE80211_MODE_TURBO_G */ + }; + chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; + bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; + + chanp->wmep_logcwmin = bssp->wmep_logcwmin = + logCwMin[ic->ic_curmode]; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s log2(cwmin) %u\n", __func__ + , ieee80211_wme_acnames[WME_AC_BE] + , chanp->wmep_logcwmin + ); + } + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ + /* + * Arrange for a beacon update and bump the parameter + * set number so associated stations load the new values. + */ + wme->wme_bssChanParams.cap_info = + (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; + ic->ic_flags |= IEEE80211_F_WMEUPDATE; + } + + wme->wme_update(ic); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: WME params updated, cap_info 0x%x\n", __func__, + ic->ic_opmode == IEEE80211_M_STA ? + wme->wme_wmeChanParams.cap_info : + wme->wme_bssChanParams.cap_info); +} + +void +ieee80211_beacon_miss(struct ieee80211com *ic) +{ + + if (ic->ic_flags & IEEE80211_F_SCAN) { + /* XXX check ic_curchan != ic_bsschan? */ + return; + } + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s\n", "beacon miss"); + + /* + * Our handling is only meaningful for stations that are + * associated; any other conditions else will be handled + * through different means (e.g. the tx timeout on mgt frames). + */ + if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) + return; + + if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { + /* + * Send a directed probe req before falling back to a scan; + * if we receive a response ic_bmiss_count will be reset. + * Some cards mistakenly report beacon miss so this avoids + * the expensive scan if the ap is still there. + */ + ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, + ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, + ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, + ic->ic_opt_ie, ic->ic_opt_ie_len); + return; + } + ic->ic_bmiss_count = 0; + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); +} + +/* + * Software beacon miss handling. Check if any beacons + * were received in the last period. If not post a + * beacon miss; otherwise reset the counter. + */ +static void +ieee80211_swbmiss(void *arg) +{ + struct ieee80211com *ic = arg; + struct ifnet *ifp = ic->ic_ifp; + + lwkt_serialize_enter(ifp->if_serializer); + + if (ic->ic_swbmiss_count == 0) { + ieee80211_beacon_miss(ic); + if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ + goto back; + } else + ic->ic_swbmiss_count = 0; + callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, + ieee80211_swbmiss, ic); + +back: + lwkt_serialize_exit(ifp->if_serializer); +} + +static void +sta_disassoc(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = arg; + + if (ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_node_leave(ic, ni); + } +} + +static void +sta_deauth(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = arg; + + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_LEAVE); +} + static int -ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) +ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_node *ni; enum ieee80211_state ostate; - struct lwkt_tokref ilock; ostate = ic->ic_state; - IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__, - ieee80211_state_name[ostate], ieee80211_state_name[nstate])); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); ic->ic_state = nstate; /* state transition */ ni = ic->ic_bss; /* NB: no reference held */ + if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) + callout_stop(&ic->ic_swbmiss); switch (nstate) { case IEEE80211_S_INIT: switch (ostate) { @@ -322,22 +921,16 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_sta_leave(ic, ni); break; case IEEE80211_M_HOSTAP: - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if (ni->ni_associd == 0) - continue; - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_ASSOC_LEAVE); - } - lwkt_reltoken(&ilock); + ieee80211_iterate_nodes(&ic->ic_sta, + sta_disassoc, ic); break; default: break; } - /* FALLTHRU */ + goto reset; case IEEE80211_S_ASSOC: switch (ic->ic_opmode) { case IEEE80211_M_STA: @@ -346,42 +939,32 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt IEEE80211_REASON_AUTH_LEAVE); break; case IEEE80211_M_HOSTAP: - lwkt_gettoken(&ilock, &ic->ic_nodetoken); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_LEAVE); - } - lwkt_reltoken(&ilock); + ieee80211_iterate_nodes(&ic->ic_sta, + sta_deauth, ic); break; default: break; } - /* FALLTHRU */ - case IEEE80211_S_AUTH: + goto reset; case IEEE80211_S_SCAN: + ieee80211_cancel_scan(ic); + goto reset; + case IEEE80211_S_AUTH: + reset: ic->ic_mgt_timer = 0; IF_DRAIN(&ic->ic_mgtq); - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; - } - ieee80211_free_allnodes(ic); + ieee80211_reset_bss(ic); break; } + if (ic->ic_auth->ia_detach != NULL) + ic->ic_auth->ia_detach(ic); break; case IEEE80211_S_SCAN: - ic->ic_flags &= ~IEEE80211_F_SIBSS; - /* initialize bss for probe request */ - IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr); - IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr); - ni->ni_rates = ic->ic_sup_rates[ - ieee80211_chan2mode(ic, ni->ni_chan)]; - ni->ni_associd = 0; - ni->ni_rstamp = 0; switch (ostate) { case IEEE80211_S_INIT: - if (ic->ic_opmode == IEEE80211_M_HOSTAP && + if ((ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO) && ic->ic_des_chan != IEEE80211_CHAN_ANYC) { /* * AP operation and we already have a channel; @@ -389,51 +972,49 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt */ ieee80211_create_ibss(ic, ic->ic_des_chan); } else { - ieee80211_begin_scan(ifp); + ieee80211_begin_scan(ic, arg); } break; case IEEE80211_S_SCAN: - /* scan next */ - if (ic->ic_flags & IEEE80211_F_ASCAN) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); - } + /* + * Scan next. If doing an active scan probe + * for the requested ap (if any). + */ + if (ic->ic_flags & IEEE80211_F_ASCAN) + ieee80211_probe_curchan(ic, 0); break; case IEEE80211_S_RUN: /* beacon miss */ - if (ifp->if_flags & IFF_DEBUG) { - /* XXX bssid clobbered above */ - if_printf(ifp, "no recent beacons from %6D;" - " rescanning\n", - ic->ic_bss->ni_bssid, ":"); - } - ieee80211_free_allnodes(ic); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, + "no recent beacons from %6D; rescanning\n", + ic->ic_bss->ni_bssid, ":"); + ieee80211_sta_leave(ic, ni); + ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: /* timeout restart scan */ - ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + ni = ieee80211_find_node(&ic->ic_scan, + ic->ic_bss->ni_macaddr); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } - ieee80211_begin_scan(ifp); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_begin_scan(ic, arg); break; } break; case IEEE80211_S_AUTH: switch (ostate) { case IEEE80211_S_INIT: - IEEE80211_DPRINTF(("%s: invalid transition\n", - __func__)); - break; case IEEE80211_S_SCAN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: - switch (mgt) { + switch (arg) { case IEEE80211_FC0_SUBTYPE_AUTH: /* ??? */ IEEE80211_SEND_MGMT(ic, ni, @@ -445,16 +1026,19 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt } break; case IEEE80211_S_RUN: - switch (mgt) { + switch (arg) { case IEEE80211_FC0_SUBTYPE_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); ic->ic_state = ostate; /* stay RUN */ break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - /* try to reauth */ - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 1); + ieee80211_sta_leave(ic, ni); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { + /* try to reauth */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + } break; } break; @@ -465,49 +1049,101 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt case IEEE80211_S_INIT: case IEEE80211_S_SCAN: case IEEE80211_S_ASSOC: - IEEE80211_DPRINTF(("%s: invalid transition\n", - __func__)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: invalid transition\n", __func__); break; case IEEE80211_S_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); break; case IEEE80211_S_RUN: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + ieee80211_sta_leave(ic, ni); + if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + } break; } break; case IEEE80211_S_RUN: + if (ic->ic_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } switch (ostate) { case IEEE80211_S_INIT: + if (ic->ic_opmode == IEEE80211_M_MONITOR) + break; + /* fall thru... */ case IEEE80211_S_AUTH: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: invalid transition\n", __func__); + /* fall thru... */ case IEEE80211_S_RUN: - IEEE80211_DPRINTF(("%s: invalid transition\n", - __func__)); break; case IEEE80211_S_SCAN: /* adhoc/hostap mode */ case IEEE80211_S_ASSOC: /* infra mode */ KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, ("%s: bogus xmit rate %u setup\n", __func__, ni->ni_txrate)); - if (ifp->if_flags & IFF_DEBUG) { - if_printf(ifp, " "); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(ic)) { if (ic->ic_opmode == IEEE80211_M_STA) - printf("associated "); + if_printf(ifp, "associated "); else - printf("synchronized "); + if_printf(ifp, "synchronized "); printf("with %6D ssid ", ni->ni_bssid, ":"); ieee80211_print_essid(ic->ic_bss->ni_essid, ni->ni_esslen); printf(" channel %d start %uMb\n", - ieee80211_chan2ieee(ic, ni->ni_chan), + ieee80211_chan2ieee(ic, ic->ic_curchan), IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); } +#endif ic->ic_mgt_timer = 0; - (*ifp->if_start)(ifp); + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_notify_node_join(ic, ni, + arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + ifp->if_start(ifp); /* XXX not authorized yet */ break; } + if (ostate != IEEE80211_S_RUN && + ic->ic_opmode == IEEE80211_M_STA && + (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { + /* + * Start s/w beacon miss timer for devices w/o + * hardware support. We fudge a bit here since + * we're doing this in software. + */ + ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( + 2 * ic->ic_bmissthreshold * ni->ni_intval); + ic->ic_swbmiss_count = 0; + callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, + ieee80211_swbmiss, ic); + } + /* + * Start/stop the authenticator when operating as an + * AP. We delay until here to allow configuration to + * happen out of order. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ + ic->ic_auth->ia_attach != NULL) { + /* XXX check failure */ + ic->ic_auth->ia_attach(ic); + } else if (ic->ic_auth->ia_detach != NULL) { + ic->ic_auth->ia_detach(ic); + } + /* + * When 802.1x is not in use mark the port authorized + * at this point so traffic can flow. + */ + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ni); + /* + * Enable inactivity processing. + * XXX + */ + ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; + ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; break; } return 0; diff --git a/sys/netproto/802_11/wlan_acl/Makefile b/sys/netproto/802_11/wlan_acl/Makefile new file mode 100644 index 0000000000..dabeda762c --- /dev/null +++ b/sys/netproto/802_11/wlan_acl/Makefile @@ -0,0 +1,7 @@ +# $DragonFly: src/sys/netproto/802_11/wlan_acl/Makefile,v 1.1 2006/05/18 13:51:46 sephe Exp $ + +KMOD = wlan_acl +SRCS = ieee80211_acl.c +KMODDEPS= wlan + +.include diff --git a/sys/netproto/802_11/wlan_acl/ieee80211_acl.c b/sys/netproto/802_11/wlan_acl/ieee80211_acl.c new file mode 100644 index 0000000000..6f2d0fe283 --- /dev/null +++ b/sys/netproto/802_11/wlan_acl/ieee80211_acl.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2004-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/ieee80211_acl.c,v 1.3.2.1 2005/09/03 22:40:02 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_acl/ieee80211_acl.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * IEEE 802.11 MAC ACL support. + * + * When this module is loaded the sender address of each received + * frame is passed to the iac_check method and the module indicates + * if the frame should be accepted or rejected. If the policy is + * set to ACL_POLICY_OPEN then all frames are accepted w/o checking + * the address. Otherwise, the address is looked up in the database + * and if found the frame is either accepted (ACL_POLICY_ALLOW) + * or rejected (ACL_POLICY_DENT). + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +enum { + ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ + ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ + ACL_POLICY_DENY = 2, /* deny traffic from MAC */ +}; + +#define ACL_HASHSIZE 32 + +struct acl { + TAILQ_ENTRY(acl) acl_list; + LIST_ENTRY(acl) acl_hash; + uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; +}; +struct aclstate { +#if 0 + acl_lock_t as_lock; +#endif + int as_policy; + int as_nacls; + TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ + LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; + struct ieee80211com *as_ic; +}; + +/* simple hash is enough for variation of macaddr */ +#define ACL_HASH(addr) \ + (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) + +MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); + +static int acl_free_all(struct ieee80211com *); + +static int +acl_attach(struct ieee80211com *ic) +{ + struct aclstate *as; + + as = malloc(sizeof(struct aclstate), M_80211_ACL, M_NOWAIT | M_ZERO); + if (as == NULL) + return 0; + TAILQ_INIT(&as->as_list); + as->as_policy = ACL_POLICY_OPEN; + as->as_ic = ic; + ic->ic_as = as; + return 1; +} + +static void +acl_detach(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + + acl_free_all(ic); + ic->ic_as = NULL; + free(as, M_DEVBUF); +} + +static __inline struct acl * +_find_acl(struct aclstate *as, const uint8_t *macaddr) +{ + struct acl *acl; + int hash; + + hash = ACL_HASH(macaddr); + LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { + if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) + return acl; + } + return NULL; +} + +static void +_acl_free(struct aclstate *as, struct acl *acl) +{ + ASSERT_SERIALIZED(as->as_ic->ic_ifp->if_serializer); + + TAILQ_REMOVE(&as->as_list, acl, acl_list); + LIST_REMOVE(acl, acl_hash); + free(acl, M_80211_ACL); + as->as_nacls--; +} + +static int +acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + + switch (as->as_policy) { + case ACL_POLICY_OPEN: + return 1; + case ACL_POLICY_ALLOW: + return _find_acl(as, mac) != NULL; + case ACL_POLICY_DENY: + return _find_acl(as, mac) == NULL; + } + return 0; /* should not happen */ +} + +static int +acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl, *new; + int hash; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + new = malloc(sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); + if (new == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: add %6D failed, no memory\n", mac, ":"); + /* XXX statistic */ + return ENOMEM; + } + + hash = ACL_HASH(mac); + LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { + if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { + FREE(new, M_80211_ACL); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: add %6D failed, already present\n", + mac, ":"); + return EEXIST; + } + } + IEEE80211_ADDR_COPY(new->acl_macaddr, mac); + TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); + LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); + as->as_nacls++; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: add %6D\n", mac, ":"); + return 0; +} + +static int +acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + acl = _find_acl(as, mac); + if (acl != NULL) + _acl_free(as, acl); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: remove %6D%s\n", mac, ":", + acl == NULL ? ", not present" : ""); + + return (acl == NULL ? ENOENT : 0); +} + +static int +acl_free_all(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); + + while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) + _acl_free(as, acl); + + return 0; +} + +static int +acl_setpolicy(struct ieee80211com *ic, int policy) +{ + struct aclstate *as = ic->ic_as; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: set policy to %u\n", policy); + + switch (policy) { + case IEEE80211_MACCMD_POLICY_OPEN: + as->as_policy = ACL_POLICY_OPEN; + break; + case IEEE80211_MACCMD_POLICY_ALLOW: + as->as_policy = ACL_POLICY_ALLOW; + break; + case IEEE80211_MACCMD_POLICY_DENY: + as->as_policy = ACL_POLICY_DENY; + break; + default: + return EINVAL; + } + return 0; +} + +static int +acl_getpolicy(struct ieee80211com *ic) +{ + return ((struct aclstate *)(ic->ic_as))->as_policy; +} + +static int +acl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + return EINVAL; +} + +static int +acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + struct ieee80211req_maclist *ap; + int error, space, i; + + ASSERT_SERIALIZED(ic->ic_ifp->if_serializer); + + switch (ireq->i_val) { + case IEEE80211_MACCMD_POLICY: + ireq->i_val = as->as_policy; + return 0; + case IEEE80211_MACCMD_LIST: + space = as->as_nacls * IEEE80211_ADDR_LEN; + if (ireq->i_len == 0) { + ireq->i_len = space; /* return required space */ + return 0; /* NB: must not error */ + } + ap = malloc(space, M_TEMP, M_NOWAIT); + if (ap == NULL) + return ENOMEM; + i = 0; + TAILQ_FOREACH(acl, &as->as_list, acl_list) { + IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr); + i++; + } + if (ireq->i_len >= space) { + error = copyout(ap, ireq->i_data, space); + ireq->i_len = space; + } else + error = copyout(ap, ireq->i_data, ireq->i_len); + free(ap, M_TEMP); + return error; + } + return EINVAL; +} + +static const struct ieee80211_aclator mac = { + .iac_name = "mac", + .iac_attach = acl_attach, + .iac_detach = acl_detach, + .iac_check = acl_check, + .iac_add = acl_add, + .iac_remove = acl_remove, + .iac_flush = acl_free_all, + .iac_setpolicy = acl_setpolicy, + .iac_getpolicy = acl_getpolicy, + .iac_setioctl = acl_setioctl, + .iac_getioctl = acl_getioctl, +}; + +/* + * Module glue. + */ +static int +wlan_acl_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("wlan: <802.11 MAC ACL support>\n"); + ieee80211_aclator_register(&mac); + return 0; + case MOD_UNLOAD: + ieee80211_aclator_unregister(&mac); + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_acl_mod = { + "wlan_acl", + wlan_acl_modevent, + 0 +}; +DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_acl, 1); +MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1); diff --git a/sys/netproto/802_11/wlan_ccmp/Makefile b/sys/netproto/802_11/wlan_ccmp/Makefile new file mode 100644 index 0000000000..72095d0a91 --- /dev/null +++ b/sys/netproto/802_11/wlan_ccmp/Makefile @@ -0,0 +1,9 @@ +# $DragonFly: src/sys/netproto/802_11/wlan_ccmp/Makefile,v 1.1 2006/05/18 13:51:46 sephe Exp $ + +.PATH: ${.CURDIR}/../../../../crypto/rijndael + +KMOD = wlan_ccmp +SRCS = ieee80211_crypto_ccmp.c +KMODDEPS= wlan + +.include diff --git a/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c b/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c new file mode 100644 index 0000000000..255940e251 --- /dev/null +++ b/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2002-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * IEEE 802.11i AES-CCMP crypto support. + * + * Part of this module is derived from similar code in the Host + * AP driver. The code is used with the consent of the author and + * it's license is included below. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#define AES_BLOCK_LEN 16 + +struct ccmp_ctx { + struct ieee80211com *cc_ic; /* for diagnostics */ + rijndael_ctx cc_aes; +}; + +static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *); +static void ccmp_detach(struct ieee80211_key *); +static int ccmp_setkey(struct ieee80211_key *); +static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid); +static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int); +static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int); +static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int); + +static const struct ieee80211_cipher ccmp = { + .ic_name = "AES-CCM", + .ic_cipher = IEEE80211_CIPHER_AES_CCM, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = IEEE80211_WEP_MICLEN, + .ic_miclen = 0, + .ic_attach = ccmp_attach, + .ic_detach = ccmp_detach, + .ic_setkey = ccmp_setkey, + .ic_encap = ccmp_encap, + .ic_decap = ccmp_decap, + .ic_enmic = ccmp_enmic, + .ic_demic = ccmp_demic, +}; + +static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); +static int ccmp_decrypt(struct ieee80211_key *, uint64_t pn, + struct mbuf *, int hdrlen); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static void * +ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx; + + ctx = malloc(sizeof(struct ccmp_ctx), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + return NULL; + } + ctx->cc_ic = ic; + nrefs++; /* NB: we assume caller locking */ + return ctx; +} + +static void +ccmp_detach(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + free(ctx, M_DEVBUF); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ +} + +static int +ccmp_setkey(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (128/NBBY)) { + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 128/NBBY); + return 0; + } + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen * NBBY, + 1); + } + return 1; +} + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) +{ + struct ccmp_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->cc_ic; + uint8_t *ivp; + int hdrlen; + + hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + M_PREPEND(m, ccmp.ic_header, M_NOWAIT); + if (m == NULL) + return 0; + ivp = mtod(m, uint8_t *); + ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen); + ivp += hdrlen; + + k->wk_keytsc++; /* XXX wrap at 48 bits */ + ivp[0] = k->wk_keytsc >> 0; /* PN0 */ + ivp[1] = k->wk_keytsc >> 8; /* PN1 */ + ivp[2] = 0; /* Reserved */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* PN2 */ + ivp[5] = k->wk_keytsc >> 24; /* PN3 */ + ivp[6] = k->wk_keytsc >> 32; /* PN4 */ + ivp[7] = k->wk_keytsc >> 40; /* PN5 */ + + /* + * Finally, do software encrypt if neeed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !ccmp_encrypt(k, m, hdrlen)) + return 0; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +ccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + return 1; +} + +static __inline uint64_t +READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) +{ + uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + uint16_t iv16 = (b4 << 0) | (b5 << 8); + return (((uint64_t)iv16) << 32) | iv32; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. The specified key should be correct but + * is also verified. + */ +static int +ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) +{ + struct ccmp_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + uint8_t *ivp; + uint64_t pn; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = mtod(m, struct ieee80211_frame *); + ivp = mtod(m, uint8_t *) + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] Missing ExtIV for AES-CCM cipher\n", + wh->i_addr2, ":"); + ctx->cc_ic->ic_stats.is_rx_ccmpformat++; + return 0; + } + pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); + if (pn <= k->wk_keyrsc) { + /* + * Replay violation. + */ + ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn); + ctx->cc_ic->ic_stats.is_rx_ccmpreplay++; + return 0; + } + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. Note that for the + * latter we leave the header in place for use in the + * decryption work. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !ccmp_decrypt(k, pn, m, hdrlen)) + return 0; + + /* + * Copy up 802.11 header and strip crypto bits. + */ + ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen); + m_adj(m, ccmp.ic_header); + m_adj(m, -ccmp.ic_trailer); + + /* + * Ok to update rsc now. + */ + k->wk_keyrsc = pn; + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + return 1; +} + +static __inline void +xor_block(uint8_t *b, const uint8_t *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +static void +ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, + uint64_t pn, size_t dlen, + uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN], + uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN]) +{ +#define IS_4ADDRESS(wh) \ + ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) +#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh) + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + /* NB: b0[1] set below */ + IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2); + b0[8] = pn >> 40; + b0[9] = pn >> 32; + b0[10] = pn >> 24; + b0[11] = pn >> 16; + b0[12] = pn >> 8; + b0[13] = pn >> 0; + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + aad[0] = 0; /* AAD length >> 8 */ + /* NB: aad[1] set below */ + aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */ + aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */ + /* NB: we know 3 addresses are contiguous */ + memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN); + aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK; + aad[23] = 0; /* all bits masked */ + /* + * Construct variable-length portion of AAD based + * on whether this is a 4-address frame/QOS frame. + * We always zero-pad to 32 bytes before running it + * through the cipher. + * + * We also fill in the priority bits of the CCM + * initial block as we know whether or not we have + * a QOS frame. + */ + if (IS_4ADDRESS(wh)) { + IEEE80211_ADDR_COPY(aad + 24, + ((struct ieee80211_frame_addr4 *)wh)->i_addr4); + if (IS_QOS_DATA(wh)) { + struct ieee80211_qosframe_addr4 *qwh4 = + (struct ieee80211_qosframe_addr4 *) wh; + aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */ + aad[31] = 0; + b0[1] = aad[30]; + aad[1] = 22 + IEEE80211_ADDR_LEN + 2; + } else { + *(uint16_t *)&aad[30] = 0; + b0[1] = 0; + aad[1] = 22 + IEEE80211_ADDR_LEN; + } + } else { + if (IS_QOS_DATA(wh)) { + struct ieee80211_qosframe *qwh = + (struct ieee80211_qosframe*) wh; + aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */ + aad[25] = 0; + b0[1] = aad[24]; + aad[1] = 22 + 2; + } else { + *(uint16_t *)&aad[24] = 0; + b0[1] = 0; + aad[1] = 22; + } + *(uint16_t *)&aad[26] = 0; + *(uint32_t *)&aad[28] = 0; + } + + /* Start with the first block and AAD */ + rijndael_encrypt(ctx, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + rijndael_encrypt(ctx, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + rijndael_encrypt(ctx, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + rijndael_encrypt(ctx, b0, s0); +#undef IS_QOS_DATA +#undef IS_4ADDRESS +} + +#define CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do { \ + /* Authentication */ \ + xor_block(_b, _pos, _len); \ + rijndael_encrypt(&ctx->cc_aes, _b, _b); \ + /* Encryption, with counter */ \ + _b0[14] = (_i >> 8) & 0xff; \ + _b0[15] = _i & 0xff; \ + rijndael_encrypt(&ctx->cc_aes, _b0, _e); \ + xor_block(_pos, _e, _len); \ +} while (0) + +static int +ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ + struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + struct mbuf *m = m0; + int data_len, i, space; + uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], + e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN]; + uint8_t *pos; + + ctx->cc_ic->ic_stats.is_crypto_ccmp++; + + wh = mtod(m, struct ieee80211_frame *); + data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header); + ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc, + data_len, b0, aad, b, s0); + + i = 1; + pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; + /* NB: assumes header is entirely in first mbuf */ + space = m->m_len - (hdrlen + ccmp.ic_header); + for (;;) { + if (space > data_len) + space = data_len; + /* + * Do full blocks. + */ + while (space >= AES_BLOCK_LEN) { + CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN); + pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; + data_len -= AES_BLOCK_LEN; + i++; + } + if (data_len <= 0) /* no more data */ + break; + m = m->m_next; + if (m == NULL) { /* last buffer */ + if (space != 0) { + /* + * Short last block. + */ + CCMP_ENCRYPT(i, b, b0, pos, e, space); + } + break; + } + if (space != 0) { + uint8_t *pos_next; + int space_next; + int len, dl, sp; + struct mbuf *n; + + /* + * Block straddles one or more mbufs, gather data + * into the block buffer b, apply the cipher, then + * scatter the results back into the mbuf chain. + * The buffer will automatically get space bytes + * of data at offset 0 copied in+out by the + * CCMP_ENCRYPT request so we must take care of + * the remaining data. + */ + n = m; + dl = data_len; + sp = space; + for (;;) { + pos_next = mtod(n, uint8_t *); + len = min(dl, AES_BLOCK_LEN); + space_next = len > sp ? len - sp : 0; + if (n->m_len >= space_next) { + /* + * This mbuf has enough data; just grab + * what we need and stop. + */ + xor_block(b+sp, pos_next, space_next); + break; + } + /* + * This mbuf's contents are insufficient, + * take 'em all and prepare to advance to + * the next mbuf. + */ + xor_block(b+sp, pos_next, n->m_len); + sp += n->m_len, dl -= n->m_len; + n = n->m_next; + if (n == NULL) + break; + } + + CCMP_ENCRYPT(i, b, b0, pos, e, space); + + /* NB: just like above, but scatter data to mbufs */ + dl = data_len; + sp = space; + for (;;) { + pos_next = mtod(m, uint8_t *); + len = min(dl, AES_BLOCK_LEN); + space_next = len > sp ? len - sp : 0; + if (m->m_len >= space_next) { + xor_block(pos_next, e+sp, space_next); + break; + } + xor_block(pos_next, e+sp, m->m_len); + sp += m->m_len, dl -= m->m_len; + m = m->m_next; + if (m == NULL) + goto done; + } + /* + * Do bookkeeping. m now points to the last mbuf + * we grabbed data from. We know we consumed a + * full block of data as otherwise we'd have hit + * the end of the mbuf chain, so deduct from data_len. + * Otherwise advance the block number (i) and setup + * pos+space to reflect contents of the new mbuf. + */ + data_len -= AES_BLOCK_LEN; + i++; + pos = pos_next + space_next; + space = m->m_len - space_next; + } else { + /* + * Setup for next buffer. + */ + pos = mtod(m, uint8_t *); + space = m->m_len; + } + } +done: + /* tack on MIC */ + xor_block(b, s0, ccmp.ic_trailer); + return ieee80211_mbuf_append(m0, ccmp.ic_trailer, b); +} +#undef CCMP_ENCRYPT + +#define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \ + /* Decrypt, with counter */ \ + _b0[14] = (_i >> 8) & 0xff; \ + _b0[15] = _i & 0xff; \ + rijndael_encrypt(&ctx->cc_aes, _b0, _b); \ + xor_block(_pos, _b, _len); \ + /* Authentication */ \ + xor_block(_a, _pos, _len); \ + rijndael_encrypt(&ctx->cc_aes, _a, _a); \ +} while (0) + +static int +ccmp_decrypt(struct ieee80211_key *key, uint64_t pn, struct mbuf *m, int hdrlen) +{ + struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + uint8_t aad[2 * AES_BLOCK_LEN]; + uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; + uint8_t mic[AES_BLOCK_LEN]; + size_t data_len; + int i; + uint8_t *pos; + u_int space; + + ctx->cc_ic->ic_stats.is_crypto_ccmp++; + + wh = mtod(m, struct ieee80211_frame *); + data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer); + ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b); + m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic); + xor_block(mic, b, ccmp.ic_trailer); + + i = 1; + pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; + space = m->m_len - (hdrlen + ccmp.ic_header); + for (;;) { + if (space > data_len) + space = data_len; + while (space >= AES_BLOCK_LEN) { + CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN); + pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; + data_len -= AES_BLOCK_LEN; + i++; + } + if (data_len <= 0) /* no more data */ + break; + m = m->m_next; + if (m == NULL) { /* last buffer */ + if (space != 0) /* short last block */ + CCMP_DECRYPT(i, b, b0, pos, a, space); + break; + } + if (space != 0) { + uint8_t *pos_next; + u_int space_next; + u_int len; + + /* + * Block straddles buffers, split references. We + * do not handle splits that require >2 buffers + * since rx'd frames are never badly fragmented + * because drivers typically recv in clusters. + */ + pos_next = mtod(m, uint8_t *); + len = min(data_len, AES_BLOCK_LEN); + space_next = len > space ? len - space : 0; + KASSERT(m->m_len >= space_next, + ("not enough data in following buffer, " + "m_len %u need %u\n", m->m_len, space_next)); + + xor_block(b+space, pos_next, space_next); + CCMP_DECRYPT(i, b, b0, pos, a, space); + xor_block(pos_next, b+space, space_next); + data_len -= len; + i++; + + pos = pos_next + space_next; + space = m->m_len - space_next; + } else { + /* + * Setup for next buffer. + */ + pos = mtod(m, uint8_t *); + space = m->m_len; + } + } + if (memcmp(mic, a, ccmp.ic_trailer) != 0) { + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] AES-CCM decrypt failed; MIC mismatch\n", + wh->i_addr2, ":"); + ctx->cc_ic->ic_stats.is_rx_ccmpmic++; + return 0; + } + return 1; +} +#undef CCMP_DECRYPT + +/* + * Module glue. + */ +static int +ccmp_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_crypto_register(&ccmp); + return 0; + case MOD_UNLOAD: + if (nrefs) { + printf("wlan_ccmp: still in use (%u dynamic refs)\n", + nrefs); + return EBUSY; + } + ieee80211_crypto_unregister(&ccmp); + return 0; + } + return EINVAL; +} + +static moduledata_t ccmp_mod = { + "wlan_ccmp", + ccmp_modevent, + 0 +}; +DECLARE_MODULE(wlan_ccmp, ccmp_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_ccmp, 1); +MODULE_DEPEND(wlan_ccmp, wlan, 1, 1, 1); diff --git a/sys/netproto/802_11/wlan_tkip/Makefile b/sys/netproto/802_11/wlan_tkip/Makefile new file mode 100644 index 0000000000..7d2f93b6af --- /dev/null +++ b/sys/netproto/802_11/wlan_tkip/Makefile @@ -0,0 +1,7 @@ +# $DragonFly: src/sys/netproto/802_11/wlan_tkip/Makefile,v 1.1 2006/05/18 13:51:46 sephe Exp $ + +KMOD = wlan_tkip +SRCS = ieee80211_crypto_tkip.c +KMODDEPS= wlan + +.include diff --git a/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c b/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c new file mode 100644 index 0000000000..61e9954140 --- /dev/null +++ b/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2002-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.9.2.2 2005/12/22 19:02:08 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * IEEE 802.11i TKIP crypto support. + * + * Part of this module is derived from similar code in the Host + * AP driver. The code is used with the consent of the author and + * it's license is included below. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *); +static void tkip_detach(struct ieee80211_key *); +static int tkip_setkey(struct ieee80211_key *); +static int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid); +static int tkip_enmic(struct ieee80211_key *, struct mbuf *, int); +static int tkip_decap(struct ieee80211_key *, struct mbuf *, int); +static int tkip_demic(struct ieee80211_key *, struct mbuf *, int); + +static const struct ieee80211_cipher tkip = { + .ic_name = "TKIP", + .ic_cipher = IEEE80211_CIPHER_TKIP, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = IEEE80211_WEP_CRCLEN, + .ic_miclen = IEEE80211_WEP_MICLEN, + .ic_attach = tkip_attach, + .ic_detach = tkip_detach, + .ic_setkey = tkip_setkey, + .ic_encap = tkip_encap, + .ic_decap = tkip_decap, + .ic_enmic = tkip_enmic, + .ic_demic = tkip_demic, +}; + +#define memmove(dst, src, n) ovbcopy(src, dst, n) + +struct tkip_ctx { + struct ieee80211com *tc_ic; /* for diagnostics */ + + uint16_t tx_ttak[5]; + int tx_phase1_done; + uint8_t tx_rc4key[16]; /* XXX for test module; make locals? */ + + uint16_t rx_ttak[5]; + int rx_phase1_done; + uint8_t rx_rc4key[16]; /* XXX for test module; make locals? */ + uint64_t rx_rsc; /* held until MIC verified */ +}; + +static void michael_mic(struct tkip_ctx *, const uint8_t *key, + struct mbuf *m, u_int off, size_t data_len, + uint8_t mic[IEEE80211_WEP_MICLEN]); +static int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *, + struct mbuf *, int hdr_len); +static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *, + struct mbuf *, int hdr_len); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static void * +tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct tkip_ctx *ctx; + + ctx = malloc(sizeof(struct tkip_ctx), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + return NULL; + } + + ctx->tc_ic = ic; + nrefs++; /* NB: we assume caller locking */ + return ctx; +} + +static void +tkip_detach(struct ieee80211_key *k) +{ + struct tkip_ctx *ctx = k->wk_private; + + free(ctx, M_DEVBUF); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ +} + +static int +tkip_setkey(struct ieee80211_key *k) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (128/NBBY)) { + (void) ctx; /* XXX */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 128/NBBY); + return 0; + } + k->wk_keytsc = 1; /* TSC starts at 1 */ + return 1; +} + +/* + * Add privacy headers and do any s/w encryption required. + */ +static int +tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + uint8_t *ivp; + int hdrlen; + + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { +#ifdef IEEE80211_DEBUG + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); +#endif + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] Discard frame due to countermeasures (%s)\n", + wh->i_addr2, ":", __func__); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + M_PREPEND(m, tkip.ic_header, M_NOWAIT); + if (m == NULL) + return 0; + ivp = mtod(m, uint8_t *); + memmove(ivp, ivp + tkip.ic_header, hdrlen); + ivp += hdrlen; + + ivp[0] = k->wk_keytsc >> 8; /* TSC1 */ + ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */ + ivp[2] = k->wk_keytsc >> 0; /* TSC0 */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* TSC2 */ + ivp[5] = k->wk_keytsc >> 24; /* TSC3 */ + ivp[6] = k->wk_keytsc >> 32; /* TSC4 */ + ivp[7] = k->wk_keytsc >> 40; /* TSC5 */ + + /* + * Finally, do software encrypt if neeed. + */ + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + if (!tkip_encrypt(ctx, k, m, hdrlen)) + return 0; + /* NB: tkip_encrypt handles wk_keytsc */ + } else + k->wk_keytsc++; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) { + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + struct ieee80211com *ic = ctx->tc_ic; + int hdrlen; + uint8_t mic[IEEE80211_WEP_MICLEN]; + + ic->ic_stats.is_crypto_tkipenmic++; + + hdrlen = ieee80211_hdrspace(ic, wh); + + michael_mic(ctx, k->wk_txmic, + m, hdrlen, m->m_pkthdr.len - hdrlen, mic); + return ieee80211_mbuf_append(m, tkip.ic_miclen, mic); + } + return 1; +} + +static __inline uint64_t +READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) +{ + uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + uint16_t iv16 = (b4 << 0) | (b5 << 8); + return (((uint64_t)iv16) << 32) | iv32; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. If necessary, decrypt the frame using + * the specified key. + */ +static int +tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + struct ieee80211_frame *wh; + uint8_t *ivp; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = mtod(m, struct ieee80211_frame *); + ivp = mtod(m, uint8_t *) + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] missing ExtIV for TKIP cipher\n", + wh->i_addr2, ":"); + ctx->tc_ic->ic_stats.is_rx_tkipformat++; + return 0; + } + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] discard frame due to countermeasures (%s)\n", + wh->i_addr2, ":", __func__); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + + ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]); + if (ctx->rx_rsc <= k->wk_keyrsc) { + /* + * Replay violation; notify upper layer. + */ + ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc); + ctx->tc_ic->ic_stats.is_rx_tkipreplay++; + return 0; + } + /* + * NB: We can't update the rsc in the key until MIC is verified. + * + * We assume we are not preempted between doing the check above + * and updating wk_keyrsc when stripping the MIC in tkip_demic. + * Otherwise we might process another packet and discard it as + * a replay. + */ + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !tkip_decrypt(ctx, k, m, hdrlen)) + return 0; + + /* + * Copy up 802.11 header and strip crypto bits. + */ + memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *), hdrlen); + m_adj(m, tkip.ic_header); + m_adj(m, -tkip.ic_trailer); + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) { + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + struct ieee80211com *ic = ctx->tc_ic; + int hdrlen = ieee80211_hdrspace(ic, wh); + uint8_t mic[IEEE80211_WEP_MICLEN]; + uint8_t mic0[IEEE80211_WEP_MICLEN]; + + ic->ic_stats.is_crypto_tkipdemic++; + + michael_mic(ctx, k->wk_rxmic, + m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen), + mic); + m_copydata(m, m->m_pkthdr.len - tkip.ic_miclen, + tkip.ic_miclen, mic0); + if (memcmp(mic, mic0, tkip.ic_miclen)) { + /* NB: 802.11 layer handles statistic and debug msg */ + ieee80211_notify_michael_failure(ic, wh, + k->wk_rxkeyix != IEEE80211_KEYIX_NONE ? + k->wk_rxkeyix : k->wk_keyix); + return 0; + } + } + /* + * Strip MIC from the tail. + */ + m_adj(m, -tkip.ic_miclen); + + /* + * Ok to update rsc now that MIC has been verified. + */ + k->wk_keyrsc = ctx->rx_rsc; + + return 1; +} + +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +static const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static __inline uint16_t +RotR1(uint16_t val) +{ + return (val >> 1) | (val << 15); +} + +static __inline uint8_t +Lo8(uint16_t val) +{ + return val & 0xff; +} + +static __inline uint8_t +Hi8(uint16_t val) +{ + return val >> 8; +} + +static __inline uint16_t +Lo16(uint32_t val) +{ + return val & 0xffff; +} + +static __inline uint16_t +Hi16(uint32_t val) +{ + return val >> 16; +} + +static __inline uint16_t +Mk16(uint8_t hi, uint8_t lo) +{ + return lo | (((uint16_t) hi) << 8); +} + +static __inline uint16_t +Mk16_le(const uint16_t *v) +{ + return le16toh(*v); +} + +static const uint16_t Sbox[256] = { + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + +static __inline uint16_t +_S_(uint16_t v) +{ + uint16_t t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + +#define PHASE1_LOOP_COUNT 8 + +static void +tkip_mixing_phase1(uint16_t *TTAK, const uint8_t *TK, const uint8_t *TA, + uint32_t IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + +#ifndef _BYTE_ORDER +#error "Don't know native byte order" +#endif + +static void +tkip_mixing_phase2(uint8_t *WEPSeed, const uint8_t *TK, const uint16_t *TTAK, + uint16_t IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + uint16_t *PPK = (uint16_t *) &WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((const uint16_t *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((const uint16_t *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((const uint16_t *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((const uint16_t *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((const uint16_t *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((const uint16_t *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((const uint16_t *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((const uint16_t *) &TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((const uint16_t *) &TK[0])) >> 1); + +#if _BYTE_ORDER == _BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + +static void +wep_encrypt(uint8_t *key, struct mbuf *m0, u_int off, size_t data_len, + uint8_t icv[IEEE80211_WEP_CRCLEN]) +{ + uint32_t i, j, k, crc; + size_t buflen; + uint8_t S[256]; + uint8_t *pos; + struct mbuf *m; +#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + m = m0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + m = m->m_next; + if (m == NULL) { + KASSERT(data_len == 0, + ("out of buffers with data_len %zu\n", data_len)); + break; + } + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + icv[k] ^= S[(S[i] + S[j]) & 0xff]; + } +} + +static int +wep_decrypt(uint8_t *key, struct mbuf *m, u_int off, size_t data_len) +{ + uint32_t i, j, k, crc; + uint8_t S[256]; + uint8_t *pos, icv[4]; + size_t buflen; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + crc = ~0; + i = j = 0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + m = m->m_next; + if (m == NULL) { + KASSERT(data_len == 0, + ("out of buffers with data_len %zu\n", data_len)); + break; + } + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with the + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return -1; + } + } + + return 0; +} + + +static __inline uint32_t +rotl(uint32_t val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + + +static __inline uint32_t +rotr(uint32_t val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + + +static __inline uint32_t +xswap(uint32_t val) +{ + return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); +} + + +#define michael_block(l, r) \ +do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ +} while (0) + + +static __inline uint32_t +get_le32_split(uint8_t b0, uint8_t b1, uint8_t b2, + uint8_t b3) +{ + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); +} + +static __inline uint32_t +get_le32(const uint8_t *p) +{ + return get_le32_split(p[0], p[1], p[2], p[3]); +} + + +static __inline void +put_le32(uint8_t *p, uint32_t v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + +/* + * Craft pseudo header used to calculate the MIC. + */ +static void +michael_mic_hdr(const struct ieee80211_frame *wh0, uint8_t hdr[16]) +{ + const struct ieee80211_frame_addr4 *wh = + (const struct ieee80211_frame_addr4 *) wh0; + + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4); + break; + } + + if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe *) wh; + hdr[12] = qwh->i_qos[0] & IEEE80211_QOS_TID; + } else + hdr[12] = 0; + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + +static void +michael_mic(struct tkip_ctx *ctx, const uint8_t *key, + struct mbuf *m, u_int off, size_t data_len, + uint8_t mic[IEEE80211_WEP_MICLEN]) +{ + uint8_t hdr[16]; + uint32_t l, r; + const uint8_t *data; + u_int space; + + michael_mic_hdr(mtod(m, struct ieee80211_frame *), hdr); + + l = get_le32(key); + r = get_le32(key + 4); + + /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */ + l ^= get_le32(hdr); + michael_block(l, r); + l ^= get_le32(&hdr[4]); + michael_block(l, r); + l ^= get_le32(&hdr[8]); + michael_block(l, r); + l ^= get_le32(&hdr[12]); + michael_block(l, r); + + /* first buffer has special handling */ + data = mtod(m, const uint8_t *) + off; + space = m->m_len - off; + for (;;) { + if (space > data_len) + space = data_len; + /* collect 32-bit blocks from current buffer */ + while (space >= sizeof(uint32_t)) { + l ^= get_le32(data); + michael_block(l, r); + data += sizeof(uint32_t), space -= sizeof(uint32_t); + data_len -= sizeof(uint32_t); + } + if (data_len < sizeof(uint32_t)) + break; + m = m->m_next; + if (m == NULL) { + KASSERT(0, ("out of data, data_len %zu\n", data_len)); + break; + } + if (space != 0) { + const uint8_t *data_next; + /* + * Block straddles buffers, split references. + */ + data_next = mtod(m, const uint8_t *); + KASSERT(m->m_len >= sizeof(uint32_t) - space, + ("not enough data in following buffer, " + "m_len %u need %zu\n", m->m_len, + sizeof(uint32_t) - space)); + switch (space) { + case 1: + l ^= get_le32_split(data[0], data_next[0], + data_next[1], data_next[2]); + data = data_next + 3; + space = m->m_len - 3; + break; + case 2: + l ^= get_le32_split(data[0], data[1], + data_next[0], data_next[1]); + data = data_next + 2; + space = m->m_len - 2; + break; + case 3: + l ^= get_le32_split(data[0], data[1], + data[2], data_next[0]); + data = data_next + 1; + space = m->m_len - 1; + break; + } + michael_block(l, r); + data_len -= sizeof(uint32_t); + } else { + /* + * Setup for next buffer. + */ + data = mtod(m, const uint8_t *); + space = m->m_len; + } + } + /* Last block and padding (0x5a, 4..7 x 0) */ + switch (data_len) { + case 0: + l ^= get_le32_split(0x5a, 0, 0, 0); + break; + case 1: + l ^= get_le32_split(data[0], 0x5a, 0, 0); + break; + case 2: + l ^= get_le32_split(data[0], data[1], 0x5a, 0); + break; + case 3: + l ^= get_le32_split(data[0], data[1], data[2], 0x5a); + break; + } + michael_block(l, r); + /* l ^= 0; */ + michael_block(l, r); + + put_le32(mic, l); + put_le32(mic + 4, r); +} + +static int +tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, + struct mbuf *m, int hdrlen) +{ + struct ieee80211_frame *wh; + uint8_t icv[IEEE80211_WEP_CRCLEN]; + + ctx->tc_ic->ic_stats.is_crypto_tkip++; + + wh = mtod(m, struct ieee80211_frame *); + if (!ctx->tx_phase1_done) { + tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2, + (uint32_t)(key->wk_keytsc >> 16)); + ctx->tx_phase1_done = 1; + } + tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak, + (uint16_t)key->wk_keytsc); + + wep_encrypt(ctx->tx_rc4key, + m, hdrlen + tkip.ic_header, + m->m_pkthdr.len - (hdrlen + tkip.ic_header), + icv); + + /* XXX check return */ + ieee80211_mbuf_append(m, IEEE80211_WEP_CRCLEN, icv); + + key->wk_keytsc++; + if ((uint16_t)(key->wk_keytsc) == 0) + ctx->tx_phase1_done = 0; + return 1; +} + +static int +tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, + struct mbuf *m, int hdrlen) +{ + struct ieee80211_frame *wh; + uint32_t iv32; + uint16_t iv16; + + ctx->tc_ic->ic_stats.is_crypto_tkip++; + + wh = mtod(m, struct ieee80211_frame *); + /* NB: tkip_decap already verified header and left seq in rx_rsc */ + iv16 = (uint16_t)ctx->rx_rsc; + iv32 = (uint32_t)(ctx->rx_rsc >> 16); + + if (iv32 != (uint32_t)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) { + tkip_mixing_phase1(ctx->rx_ttak, key->wk_key, + wh->i_addr2, iv32); + ctx->rx_phase1_done = 1; + } + tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16); + + /* NB: m is unstripped; deduct headers + ICV to get payload */ + if (wep_decrypt(ctx->rx_rc4key, + m, hdrlen + tkip.ic_header, + m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) { + if (iv32 != (uint32_t)(key->wk_keyrsc >> 16)) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + ctx->rx_phase1_done = 0; + } + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] TKIP ICV mismatch on decrypt\n", + wh->i_addr2, ":"); + ctx->tc_ic->ic_stats.is_rx_tkipicv++; + return 0; + } + return 1; +} + +/* + * Module glue. + */ +static int +tkip_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_crypto_register(&tkip); + return 0; + case MOD_UNLOAD: + if (nrefs) { + printf("wlan_tkip: still in use (%u dynamic refs)\n", + nrefs); + return EBUSY; + } + ieee80211_crypto_unregister(&tkip); + return 0; + } + return EINVAL; +} + +static moduledata_t tkip_mod = { + "wlan_tkip", + tkip_modevent, + 0 +}; +DECLARE_MODULE(wlan_tkip, tkip_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_tkip, 1); +MODULE_DEPEND(wlan_tkip, wlan, 1, 1, 1); diff --git a/sys/netproto/802_11/wlan_wep/Makefile b/sys/netproto/802_11/wlan_wep/Makefile new file mode 100644 index 0000000000..9c09180e8f --- /dev/null +++ b/sys/netproto/802_11/wlan_wep/Makefile @@ -0,0 +1,7 @@ +# $DragonFly: src/sys/netproto/802_11/wlan_wep/Makefile,v 1.1 2006/05/18 13:51:46 sephe Exp $ + +KMOD = wlan_wep +SRCS = ieee80211_crypto_wep.c +KMODDEPS= wlan + +.include diff --git a/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c b/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c new file mode 100644 index 0000000000..35fb13a372 --- /dev/null +++ b/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2002-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/ieee80211_crypto_wep.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * IEEE 802.11 WEP crypto support. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static void *wep_attach(struct ieee80211com *, struct ieee80211_key *); +static void wep_detach(struct ieee80211_key *); +static int wep_setkey(struct ieee80211_key *); +static int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid); +static int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen); +static int wep_enmic(struct ieee80211_key *, struct mbuf *, int); +static int wep_demic(struct ieee80211_key *, struct mbuf *, int); + +static const struct ieee80211_cipher wep = { + .ic_name = "WEP", + .ic_cipher = IEEE80211_CIPHER_WEP, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, + .ic_trailer = IEEE80211_WEP_CRCLEN, + .ic_miclen = 0, + .ic_attach = wep_attach, + .ic_detach = wep_detach, + .ic_setkey = wep_setkey, + .ic_encap = wep_encap, + .ic_decap = wep_decap, + .ic_enmic = wep_enmic, + .ic_demic = wep_demic, +}; + +static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); +static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); + +struct wep_ctx { + struct ieee80211com *wc_ic; /* for diagnostics */ + uint32_t wc_iv; /* initial vector for crypto */ +}; + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static void * +wep_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct wep_ctx *ctx; + + ctx = malloc(sizeof(struct wep_ctx), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + return NULL; + } + + ctx->wc_ic = ic; + get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); + nrefs++; /* NB: we assume caller locking */ + return ctx; +} + +static void +wep_detach(struct ieee80211_key *k) +{ + struct wep_ctx *ctx = k->wk_private; + + FREE(ctx, M_DEVBUF); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ +} + +static int +wep_setkey(struct ieee80211_key *k) +{ + return k->wk_keylen >= 40/NBBY; +} + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +wep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) +{ + struct wep_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->wc_ic; + uint32_t iv; + uint8_t *ivp; + int hdrlen; + + hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); + + /* + * Copy down 802.11 header and add the IV + KeyID. + */ + M_PREPEND(m, wep.ic_header, M_NOWAIT); + if (m == NULL) + return 0; + ivp = mtod(m, uint8_t *); + ovbcopy(ivp + wep.ic_header, ivp, hdrlen); + ivp += hdrlen; + + /* + * XXX + * IV must not duplicate during the lifetime of the key. + * But no mechanism to renew keys is defined in IEEE 802.11 + * for WEP. And the IV may be duplicated at other stations + * because the session key itself is shared. So we use a + * pseudo random IV for now, though it is not the right way. + * + * NB: Rather than use a strictly random IV we select a + * random one to start and then increment the value for + * each frame. This is an explicit tradeoff between + * overhead and security. Given the basic insecurity of + * WEP this seems worthwhile. + */ + + /* + * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: + * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 + */ + iv = ctx->wc_iv; + if ((iv & 0xff00) == 0xff00) { + int B = (iv & 0xff0000) >> 16; + if (3 <= B && B < 16) + iv += 0x0100; + } + ctx->wc_iv = iv + 1; + + /* + * NB: Preserve byte order of IV for packet + * sniffers; it doesn't matter otherwise. + */ +#if _BYTE_ORDER == _BIG_ENDIAN + ivp[0] = iv >> 0; + ivp[1] = iv >> 8; + ivp[2] = iv >> 16; +#else + ivp[2] = iv >> 0; + ivp[1] = iv >> 8; + ivp[0] = iv >> 16; +#endif + ivp[3] = keyid; + + /* + * Finally, do software encrypt if neeed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !wep_encrypt(k, m, hdrlen)) + return 0; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +wep_enmic(struct ieee80211_key *k, struct mbuf *m, int force) +{ + return 1; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. If necessary, decrypt the frame using + * the specified key. + */ +static int +wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) +{ + struct wep_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + + wh = mtod(m, struct ieee80211_frame *); + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !wep_decrypt(k, m, hdrlen)) { + IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] WEP ICV mismatch on decrypt\n", + wh->i_addr2, ":"); + ctx->wc_ic->ic_stats.is_rx_wepfail++; + return 0; + } + + /* + * Copy up 802.11 header and strip crypto bits. + */ + ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen); + m_adj(m, wep.ic_header); + m_adj(m, -wep.ic_trailer); + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +wep_demic(struct ieee80211_key *k, struct mbuf *skb, int force) +{ + return 1; +} + +static const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static int +wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ +#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + struct wep_ctx *ctx = key->wk_private; + struct mbuf *m = m0; + uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + uint8_t icv[IEEE80211_WEP_CRCLEN]; + uint32_t i, j, k, crc; + size_t buflen, data_len; + uint8_t S[256]; + uint8_t *pos; + u_int off, keylen; + + ctx->wc_ic->ic_stats.is_crypto_wep++; + + /* NB: this assumes the header was pulled up */ + memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; + for (i = 0; i < 256; i++) { + j = (j + S[i] + rc4key[i % keylen]) & 0xff; + S_SWAP(i, j); + } + + off = hdrlen + wep.ic_header; + data_len = m->m_pkthdr.len - off; + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + if (m->m_next == NULL) { + if (data_len != 0) { /* out of data */ + IEEE80211_DPRINTF(ctx->wc_ic, + IEEE80211_MSG_CRYPTO, + "[%6D] out of data for WEP " + "(data_len %zu)\n", + mtod(m0, struct ieee80211_frame *)->i_addr2, + ":", data_len); + return 0; + } + break; + } + m = m->m_next; + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + icv[k] ^= S[(S[i] + S[j]) & 0xff]; + } + return ieee80211_mbuf_append(m0, IEEE80211_WEP_CRCLEN, icv); +#undef S_SWAP +} + +static int +wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ +#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + struct wep_ctx *ctx = key->wk_private; + struct mbuf *m = m0; + uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + uint8_t icv[IEEE80211_WEP_CRCLEN]; + uint32_t i, j, k, crc; + size_t buflen, data_len; + uint8_t S[256]; + uint8_t *pos; + u_int off, keylen; + + ctx->wc_ic->ic_stats.is_crypto_wep++; + + /* NB: this assumes the header was pulled up */ + memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; + for (i = 0; i < 256; i++) { + j = (j + S[i] + rc4key[i % keylen]) & 0xff; + S_SWAP(i, j); + } + + off = hdrlen + wep.ic_header; + data_len = m->m_pkthdr.len - (off + wep.ic_trailer), + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + m = m->m_next; + if (m == NULL) { + if (data_len != 0) { /* out of data */ + IEEE80211_DPRINTF(ctx->wc_ic, + IEEE80211_MSG_CRYPTO, + "[%s] out of data for WEP " + "(data_len %zu)\n", + mtod(m0, struct ieee80211_frame *)->i_addr2, + ":", data_len); + return 0; + } + break; + } + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + /* XXX assumes ICV is contiguous in mbuf */ + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return 0; + } + } + return 1; +#undef S_SWAP +} + +/* + * Module glue. + */ +static int +wep_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_crypto_register(&wep); + return 0; + case MOD_UNLOAD: + if (nrefs) { + printf("wlan_wep: still in use (%u dynamic refs)\n", + nrefs); + return EBUSY; + } + ieee80211_crypto_unregister(&wep); + return 0; + } + return EINVAL; +} + +static moduledata_t wep_mod = { + "wlan_wep", + wep_modevent, + 0 +}; +DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_wep, 1); +MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); diff --git a/sys/netproto/802_11/wlan_xauth/Makefile b/sys/netproto/802_11/wlan_xauth/Makefile new file mode 100644 index 0000000000..23ac054b28 --- /dev/null +++ b/sys/netproto/802_11/wlan_xauth/Makefile @@ -0,0 +1,7 @@ +# $DragonFly: src/sys/netproto/802_11/wlan_xauth/Makefile,v 1.1 2006/05/18 13:51:46 sephe Exp $ + +KMOD = wlan_xauth +SRCS = ieee80211_xauth.c +KMODDEPS= wlan + +.include diff --git a/sys/netproto/802_11/wlan_xauth/ieee80211_xauth.c b/sys/netproto/802_11/wlan_xauth/ieee80211_xauth.c new file mode 100644 index 0000000000..a6d5ce57a6 --- /dev/null +++ b/sys/netproto/802_11/wlan_xauth/ieee80211_xauth.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2004 Video54 Technologies, Inc. + * Copyright (c) 2004-2005 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * 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: src/sys/net80211/ieee80211_xauth.c,v 1.2 2004/12/31 22:42:38 sam Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_xauth/ieee80211_xauth.c,v 1.1 2006/05/18 13:51:46 sephe Exp $ + */ + +/* + * External authenticator placeholder module. + * + * This support is optional; it is only used when the 802.11 layer's + * authentication mode is set to use 802.1x or WPA is enabled separately + * (for WPA-PSK). If compiled as a module this code does not need + * to be present unless 802.1x/WPA is in use. + * + * The authenticator hooks into the 802.11 layer. At present we use none + * of the available callbacks--the user mode authenticator process works + * entirely from messages about stations joining and leaving. + */ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +/* + * One module handles everything for now. May want + * to split things up for embedded applications. + */ +static const struct ieee80211_authenticator xauth = { + .ia_name = "external", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +/* + * Module glue. + */ +static int +wlan_xauth_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth); + ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth); + return 0; + case MOD_UNLOAD: + ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X); + ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA); + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_xauth_mod = { + "wlan_xauth", + wlan_xauth_modevent, + 0 +}; +DECLARE_MODULE(wlan_xauth, wlan_xauth_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_xauth, 1); +MODULE_DEPEND(wlan_xauth, wlan, 1, 1, 1); diff --git a/usr.sbin/iwicontrol/iwicontrol.c b/usr.sbin/iwicontrol/iwicontrol.c index d7ee3f02b9..c656968f4f 100644 --- a/usr.sbin/iwicontrol/iwicontrol.c +++ b/usr.sbin/iwicontrol/iwicontrol.c @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/usr.sbin/iwicontrol/iwicontrol.c,v 1.2 2005/12/05 01:04:01 swildner Exp $ + * $DragonFly: src/usr.sbin/iwicontrol/iwicontrol.c,v 1.3 2006/05/18 13:51:45 sephe Exp $ */ #include @@ -52,8 +52,7 @@ #include #define SIOCSLOADFW _IOW('i', 137, struct ifreq) -#define SIOCSLOADIBSSFW _IOW('i', 138, struct ifreq) -#define SIOCSKILLFW _IOW('i', 139, struct ifreq) +#define SIOCSKILLFW _IOW('i', 138, struct ifreq) struct firmware { char *boot; @@ -221,8 +220,7 @@ load_firmware(const char *iface, const char *path, const char *mode) snprintf(filename, sizeof filename, "%s/iwi-%s.fw", path, mode); mmap_file(filename, &fw.main, &fw.main_size); - if (do_req(iface, strstr(mode,"ibss") - ? SIOCSLOADIBSSFW : SIOCSLOADFW, &fw) == -1) + if (do_req(iface, SIOCSLOADFW, &fw) == -1) err(EX_OSERR, "Can't load firmware to driver"); } -- 2.41.0