diff options
author | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
commit | 3569e353ca63336d80ab0143dd9669b0b9e6b123 (patch) | |
tree | bc7985c57e7ecfa1ac03e48c406a25430dba634b /sys/net80211 | |
parent | 682b4ae9be70192e298129ada878af3486683aaf (diff) | |
download | FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.zip FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.tar.gz |
Multi-bss (aka vap) support for 802.11 devices.
Note this includes changes to all drivers and moves some device firmware
loading to use firmware(9) and a separate module (e.g. ral). Also there
no longer are separate wlan_scan* modules; this functionality is now
bundled into the wlan module.
Supported by: Hobnob and Marvell
Reviewed by: many
Obtained from: Atheros (some bits)
Diffstat (limited to 'sys/net80211')
52 files changed, 18015 insertions, 8515 deletions
diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h index 9f50e3c4..d41659f 100644 --- a/sys/net80211/_ieee80211.h +++ b/sys/net80211/_ieee80211.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,16 +28,31 @@ #ifndef _NET80211__IEEE80211_H_ #define _NET80211__IEEE80211_H_ +/* + * 802.11 implementation definitions. + * + * NB: this file is used by applications. + */ + +/* + * PHY type; mostly used to identify FH phys. + */ 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 */ - IEEE80211_T_HT, /* high throughput, full GI */ + IEEE80211_T_HT, /* high throughput */ }; #define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ -/* XXX not really a mode; there are really multiple PHY's */ +/* + * PHY mode; this is not really a mode as multi-mode devices + * have multiple PHY's. Mode is mostly used as a shorthand + * for constraining which channels to consider in setting up + * operation. Modes used to be used more extensively when + * channels were identified as IEEE channel numbers. + */ enum ieee80211_phymode { IEEE80211_MODE_AUTO = 0, /* autoselect */ IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ @@ -52,13 +67,18 @@ enum ieee80211_phymode { }; #define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+1) +/* + * Operating mode. Devices do not necessarily support + * all modes; they indicate which are supported in their + * capabilities. + */ enum ieee80211_opmode { - IEEE80211_M_STA = 1, /* infrastructure station */ IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ + IEEE80211_M_STA = 1, /* infrastructure station */ + IEEE80211_M_WDS = 2, /* WDS link */ IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ - IEEE80211_M_HOSTAP = 6, /* Software Access Point */ - IEEE80211_M_MONITOR = 8, /* Monitor mode */ - IEEE80211_M_WDS = 2 /* WDS link */ + IEEE80211_M_HOSTAP = 4, /* Software Access Point */ + IEEE80211_M_MONITOR = 5, /* Monitor mode */ }; #define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1) @@ -72,7 +92,11 @@ enum ieee80211_protmode { }; /* - * Authentication mode. + * Authentication mode. The open and shared key authentication + * modes are implemented within the 802.11 layer. 802.1x and + * WPA/802.11i are implemented in user mode by setting the + * 802.11 layer into IEEE80211_AUTH_8021X and deferring + * authentication to user space programs. */ enum ieee80211_authmode { IEEE80211_AUTH_NONE = 0, @@ -265,18 +289,28 @@ struct ieee80211_channel { #define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */ /* + * 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 but may not be less than IEEE80211_AID_MIN. + */ +#define IEEE80211_AID_DEF 128 +#define IEEE80211_AID_MIN 16 + +/* * 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]; + uint8_t rs_nrates; + uint8_t rs_rates[IEEE80211_RATE_MAXSIZE]; }; /* - * 802.11n variant of ieee80211_rateset. Instead + * 802.11n variant of ieee80211_rateset. Instead of * legacy rates the entries are MCS rates. We define * the structure such that it can be used interchangeably * with an ieee80211_rateset (modulo structure size). @@ -284,27 +318,58 @@ struct ieee80211_rateset { #define IEEE80211_HTRATE_MAXSIZE 127 struct ieee80211_htrateset { - uint8_t rs_nrates; - uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE]; + uint8_t rs_nrates; + uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE]; }; #define IEEE80211_RATE_MCS 0x80 /* - * Roaming state visible to user space. There are two - * thresholds that control whether roaming is considered; - * when either is exceeded the 802.11 layer will check - * the scan cache for another AP. If the cache is stale - * then a scan may be triggered. + * Per-mode transmit parameters/controls visible to user space. + * These can be used to set fixed transmit rate for all operating + * modes or on a per-client basis according to the capabilities + * of the client (e.g. an 11b client associated to an 11g ap). + * + * MCS are distinguished from legacy rates by or'ing in 0x80. + */ +struct ieee80211_txparam { + uint8_t ucastrate; /* ucast data rate (legacy/MCS|0x80) */ + uint8_t mgmtrate; /* mgmt frame rate (legacy/MCS|0x80) */ + uint8_t mcastrate; /* multicast rate (legacy/MCS|0x80) */ + uint8_t maxretry; /* max unicast data retry count */ +}; + +/* + * Per-mode roaming state visible to user space. There are two + * thresholds that control whether roaming is considered; when + * either is exceeded the 802.11 layer will check the scan cache + * for another AP. If the cache is stale then a scan may be + * triggered. + */ +struct ieee80211_roamparam { + int8_t rssi; /* rssi thresh (.5 dBm) */ + uint8_t rate; /* tx rate thresh (.5 Mb/s or MCS) */ + uint16_t pad; /* reserve */ +}; + +/* + * Regulatory Information. + */ +struct ieee80211_regdomain { + uint16_t regdomain; /* SKU */ + uint16_t country; /* ISO country code */ + uint8_t location; /* I (indoor), O (outdoor), other */ + uint8_t ecm; /* Extended Channel Mode */ + char isocc[2]; /* country code string */ + short pad[2]; +}; + +/* + * MIMO antenna/radio state. */ -struct ieee80211_roam { - int8_t rssi11a; /* rssi thresh for 11a bss */ - int8_t rssi11b; /* for 11g sta in 11b bss */ - int8_t rssi11bOnly; /* for 11b sta */ - uint8_t pad1; - uint8_t rate11a; /* rate thresh for 11a bss */ - uint8_t rate11b; /* for 11g sta in 11b bss */ - uint8_t rate11bOnly; /* for 11b sta */ - uint8_t pad2; +struct ieee80211_mimo_info { + int8_t rssi[3]; /* per-antenna rssi */ + int8_t noise[3]; /* per-antenna noise floor */ + uint32_t evm[3]; /* EVM data */ }; #endif /* _NET80211__IEEE80211_H_ */ diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index dbeb7a3..952f420 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 generic handler */ +#include "opt_wlan.h" #include <sys/param.h> #include <sys/systm.h> @@ -38,10 +39,13 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/if_media.h> +#include <net/if_types.h> #include <net/ethernet.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> #include <net/bpf.h> @@ -57,6 +61,20 @@ const char *ieee80211_phymode_name[] = { "11na", /* IEEE80211_MODE_11NA */ "11ng", /* IEEE80211_MODE_11NG */ }; +static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); +static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); +static int ieee80211_media_setup(struct ieee80211com *ic, + struct ifmedia *media, int caps, int addsta, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); +static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); +static int ieee80211com_media_change(struct ifnet *); +static int media_status(enum ieee80211_opmode, + const struct ieee80211_channel *); + +MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); /* * Default supported rates for 802.11 operation (in IEEE .5Mb units). @@ -75,66 +93,6 @@ static const struct ieee80211_rateset ieee80211_rateset_11g = { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; #undef B -static int media_status(enum ieee80211_opmode , - const struct ieee80211_channel *); - -/* 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 struct mtx ieee80211_vap_mtx; -MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); - -static void -ieee80211_add_vap(struct ieee80211com *ic) -{ -#define N(a) (sizeof(a)/sizeof(a[0])) - int i; - uint8_t b; - - mtx_lock(&ieee80211_vap_mtx); - 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); - mtx_unlock(&ieee80211_vap_mtx); -#undef N -} - -static void -ieee80211_remove_vap(struct ieee80211com *ic) -{ - mtx_lock(&ieee80211_vap_mtx); - 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); - mtx_unlock(&ieee80211_vap_mtx); -} - -/* - * 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; -} - /* * Fill in 802.11 available channel set, mark * all available channels as active, and pick @@ -153,6 +111,7 @@ ieee80211_chan_init(struct ieee80211com *ic) KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX, ("invalid number of channels specified: %u", ic->ic_nchans)); memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); + memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; @@ -186,9 +145,13 @@ ieee80211_chan_init(struct ieee80211com *ic) memcpy(ic->ic_chan_active, ic->ic_chan_avail, sizeof(ic->ic_chan_avail)); - ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + /* sort channel table to allow lookup optimizations */ + ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); + + /* invalidate any previous state */ ic->ic_bsschan = IEEE80211_CHAN_ANYC; ic->ic_prevchan = NULL; + ic->ic_csa_newchan = NULL; /* arbitrarily pick the first channel */ ic->ic_curchan = &ic->ic_channels[0]; @@ -206,54 +169,44 @@ ieee80211_chan_init(struct ieee80211com *ic) #undef DEFAULTRATES } +static void +null_update_mcast(struct ifnet *ifp) +{ + if_printf(ifp, "need multicast update callback\n"); +} + +static void +null_update_promisc(struct ifnet *ifp) +{ + if_printf(ifp, "need promiscuous mode update callback\n"); +} + +/* + * Attach/setup the common net80211 state. Called by + * the driver on attach to prior to creating any vap's. + */ void ieee80211_ifattach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; + struct sockaddr_dl *sdl; + struct ifaddr *ifa; - ether_ifattach(ifp, ic->ic_myaddr); - ifp->if_output = ieee80211_output; - - bpfattach2(ifp, DLT_IEEE802_11, - sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); - - /* override the 802.3 setting */ - ifp->if_hdrlen = ic->ic_headroom - + sizeof(struct ieee80211_qosframe_addr4) - + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN - + IEEE80211_WEP_EXTIVLEN; - /* XXX no way to recalculate on ifdetach */ - if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { - /* XXX sanity check... */ - max_linkhdr = ALIGN(ifp->if_hdrlen); - max_hdr = max_linkhdr + max_protohdr; - max_datalen = MHLEN - max_hdr; - } + KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); + IEEE80211_LOCK_INIT(ic, "ieee80211com"); + TAILQ_INIT(&ic->ic_vaps); /* * Fill in 802.11 available channel set, mark all * available channels as active, and pick a default * channel if not already specified. */ - ieee80211_chan_init(ic); + ieee80211_media_init(ic); - if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */ - ic->ic_flags |= IEEE80211_F_BGSCAN; -#if 0 - /* XXX not until WME+WPA issues resolved */ - if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */ - ic->ic_flags |= IEEE80211_F_WME; -#endif - if (ic->ic_caps & IEEE80211_C_BURST) - ic->ic_flags |= IEEE80211_F_BURST; - ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */ + ic->ic_update_mcast = null_update_mcast; + ic->ic_update_promisc = null_update_promisc; ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; - ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; - ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; - IEEE80211_LOCK_INIT(ic, "ieee80211com"); - IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); - ic->ic_lintval = ic->ic_bintval; ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; @@ -263,30 +216,42 @@ ieee80211_ifattach(struct ieee80211com *ic) ieee80211_proto_attach(ic); ieee80211_ht_attach(ic); ieee80211_scan_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; - - KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed")); - ifp->if_llsoftc = ic; + ieee80211_regdomain_attach(ic); + + ieee80211_sysctl_attach(ic); + + ifp->if_addrlen = IEEE80211_ADDR_LEN; + ifp->if_hdrlen = 0; + if_attach(ifp); + ifp->if_mtu = IEEE80211_MTU_MAX; + ifp->if_broadcastaddr = ieee80211broadcastaddr; + + ifa = ifaddr_byindex(ifp->if_index); + KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ + sdl->sdl_alen = IEEE80211_ADDR_LEN; + IEEE80211_ADDR_COPY(LLADDR(sdl), ic->ic_myaddr); } +/* + * Detach net80211 state on device detach. Tear down + * all vap's and reclaim all common state prior to the + * device state going away. Note we may call back into + * driver; it must be prepared for this. + */ void ieee80211_ifdetach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; - ieee80211_remove_vap(ic); + /* XXX ieee80211_stop_all? */ + while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) + ieee80211_vap_destroy(vap); ieee80211_sysctl_detach(ic); + ieee80211_regdomain_detach(ic); ieee80211_scan_detach(ic); ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ @@ -297,10 +262,386 @@ ieee80211_ifdetach(struct ieee80211com *ic) ifmedia_removeall(&ic->ic_media); IEEE80211_LOCK_DESTROY(ic); - IEEE80211_BEACON_LOCK_DESTROY(ic); + if_detach(ifp); +} + +/* + * 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 +default_reset(struct ieee80211vap *vap, u_long cmd) +{ + return ENETRESET; +} + +/* + * Prepare a vap for use. Drivers use this call to + * setup net80211 state in new vap's prior attaching + * them with ieee80211_vap_attach (below). + */ +int +ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ +#define IEEE80211_C_OPMODE \ + (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \ + IEEE80211_C_MONITOR | IEEE80211_C_WDS) + struct ifnet *ifp; + + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", + __func__); + return ENOMEM; + } + if_initname(ifp, name, unit); + ifp->if_softc = vap; /* back pointer */ + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_start = ieee80211_start; + ifp->if_ioctl = ieee80211_ioctl; + ifp->if_watchdog = NULL; /* NB: no watchdog routine */ + ifp->if_init = ieee80211_init; + /* NB: input+output filled in by ether_ifattach */ + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + vap->iv_ifp = ifp; + vap->iv_ic = ic; + vap->iv_flags = ic->ic_flags; /* propagate common flags */ + vap->iv_flags_ext = ic->ic_flags_ext; + vap->iv_flags_ven = ic->ic_flags_ven; + vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; + vap->iv_htcaps = ic->ic_htcaps; + vap->iv_opmode = opmode; + switch (opmode) { + case IEEE80211_M_STA: + /* auto-enable s/w beacon miss support */ + if (flags & IEEE80211_CLONE_NOBEACONS) + vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; + break; + case IEEE80211_M_IBSS: + vap->iv_caps |= IEEE80211_C_IBSS; + break; + case IEEE80211_M_AHDEMO: + vap->iv_caps |= IEEE80211_C_AHDEMO; + break; + case IEEE80211_M_HOSTAP: + vap->iv_caps |= IEEE80211_C_HOSTAP; + break; + case IEEE80211_M_MONITOR: + vap->iv_caps |= IEEE80211_C_MONITOR; + break; + case IEEE80211_M_WDS: + vap->iv_caps |= IEEE80211_C_WDS; + /* + * WDS links must specify the bssid of the far end. + * For legacy operation this is a static relationship. + * For non-legacy operation the station must associate + * and be authorized to pass traffic. Plumbing the + * vap to the proper node happens when the vap + * transitions to RUN state. + */ + IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); + vap->iv_flags |= IEEE80211_F_DESBSSID; + if (flags & IEEE80211_CLONE_WDSLEGACY) + vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; + break; + } + /* + * Enable various functionality by default if we're + * capable; the driver can override us if it knows better. + */ + if (vap->iv_caps & IEEE80211_C_WME) + vap->iv_flags |= IEEE80211_F_WME; + if (vap->iv_caps & IEEE80211_C_BURST) + vap->iv_flags |= IEEE80211_F_BURST; + if (vap->iv_caps & IEEE80211_C_FF) + vap->iv_flags |= IEEE80211_F_FF; + if (vap->iv_caps & IEEE80211_C_TURBOP) + vap->iv_flags |= IEEE80211_F_TURBOP; + /* NB: bg scanning only makes sense for station mode right now */ + if (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_caps & IEEE80211_C_BGSCAN)) + vap->iv_flags |= IEEE80211_F_BGSCAN; + vap->iv_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */ + /* XXX out of caps, just ena */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + vap->iv_flags_ext |= IEEE80211_FEXT_DFS; + + vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; + vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; + /* + * Install a default reset method for the ioctl support; + * the driver can override this. + */ + vap->iv_reset = default_reset; + + IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); + + ieee80211_sysctl_vattach(vap); + ieee80211_crypto_vattach(vap); + ieee80211_node_vattach(vap); + ieee80211_power_vattach(vap); + ieee80211_proto_vattach(vap); + ieee80211_ht_vattach(vap); + ieee80211_scan_vattach(vap); + ieee80211_regdomain_vattach(vap); + + return 0; +#undef IEEE80211_C_OPMODE +} + +/* + * Activate a vap. State should have been prepared with a + * call to ieee80211_vap_setup and by the driver. On return + * from this call the vap is ready for use. + */ +int +ieee80211_vap_attach(struct ieee80211vap *vap, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211com *ic = vap->iv_ic; + struct ifmediareq imr; + int maxrate; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", + __func__, ieee80211_opmode_name[vap->iv_opmode], + ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); - bpfdetach(ifp); + /* + * Do late attach work that cannot happen until after + * the driver has had a chance to override defaults. + */ + ieee80211_node_latevattach(vap); + ieee80211_power_latevattach(vap); + + maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, + vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); + ieee80211_media_status(ifp, &imr); + /* NB: strip explicit mode; we're actually in autoselect */ + ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK); + if (maxrate) + ifp->if_baudrate = IF_Mbps(maxrate); + + ether_ifattach(ifp, vap->iv_myaddr); + /* hook output method setup by ether_ifattach */ + vap->iv_output = ifp->if_output; + ifp->if_output = ieee80211_output; + /* NB: if_mtu set by ether_ifattach to ETHERMTU */ + bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf); + + IEEE80211_LOCK(ic); + TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); + ieee80211_syncflag_locked(ic, IEEE80211_F_WME); + ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); + ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); + ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + IEEE80211_UNLOCK(ic); + + return 1; +} + +/* + * Tear down vap state and reclaim the ifnet. + * The driver is assumed to have prepared for + * this; e.g. by turning off interrupts for the + * underlying device. + */ +void +ieee80211_vap_detach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", + __func__, ieee80211_opmode_name[vap->iv_opmode], + ic->ic_ifp->if_xname); + + IEEE80211_LOCK(ic); + /* block traffic from above */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + /* + * Evil hack. Clear the backpointer from the ifnet to the + * vap so any requests from above will return an error or + * be ignored. In particular this short-circuits requests + * by the bridge to turn off promiscuous mode as a result + * of calling ether_ifdetach. + */ + ifp->if_softc = NULL; + /* + * Stop the vap before detaching the ifnet. Ideally we'd + * do this in the other order so the ifnet is inaccessible + * while we cleanup internal state but that is hard. + */ + ieee80211_stop_locked(vap); + + /* XXX accumulate iv_stats in ic_stats? */ + TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); + ieee80211_syncflag_locked(ic, IEEE80211_F_WME); + ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); + ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); + ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + IEEE80211_UNLOCK(ic); + + /* XXX can't hold com lock */ + /* NB: bpfattach is called by ether_ifdetach and claims all taps */ ether_ifdetach(ifp); + + ifmedia_removeall(&vap->iv_media); + + ieee80211_regdomain_vdetach(vap); + ieee80211_scan_vdetach(vap); + ieee80211_ht_vdetach(vap); + /* NB: must be before ieee80211_node_vdetach */ + ieee80211_proto_vdetach(vap); + ieee80211_crypto_vdetach(vap); + ieee80211_power_vdetach(vap); + ieee80211_node_vdetach(vap); + ieee80211_sysctl_vdetach(vap); +} + +/* + * Synchronize flag bit state in the parent ifnet structure + * according to the state of all vap ifnet's. This is used, + * for example, to handle IFF_PROMISC and IFF_ALLMULTI. + */ +void +ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; + int bit, oflags; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_ifp->if_flags & flag) { + /* + * XXX the bridge sets PROMISC but we don't want to + * enable it on the device, discard here so all the + * drivers don't need to special-case it + */ + if (flag == IFF_PROMISC && + vap->iv_opmode == IEEE80211_M_HOSTAP) + continue; + bit = 1; + break; + } + oflags = ifp->if_flags; + if (bit) + ifp->if_flags |= flag; + else + ifp->if_flags &= ~flag; + if ((ifp->if_flags ^ oflags) & flag) { + /* XXX should we return 1/0 and let caller do this? */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (flag == IFF_PROMISC) + ic->ic_update_promisc(ifp); + else if (flag == IFF_ALLMULTI) + ic->ic_update_mcast(ifp); + } + } +} + +/* + * Synchronize flag bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags |= flag; + else + ic->ic_flags &= ~flag; +} + +void +ieee80211_syncflag(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags &= ~flag; + } else + vap->iv_flags |= flag; + ieee80211_syncflag_locked(ic, flag); + IEEE80211_UNLOCK(ic); +} + +/* + * Synchronize flag bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags_ext & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags_ext |= flag; + else + ic->ic_flags_ext &= ~flag; +} + +void +ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags_ext &= ~flag; + } else + vap->iv_flags_ext |= flag; + ieee80211_syncflag_ext_locked(ic, flag); + IEEE80211_UNLOCK(ic); } static __inline int @@ -416,7 +757,7 @@ ieee80211_ieee2mhz(u_int chan, u_int flags) /* * Locate a channel given a frequency+flags. We cache - * the previous lookup to optimize swithing between two + * the previous lookup to optimize switching between two * channels--as happens with dynamic turbo. */ struct ieee80211_channel * @@ -467,80 +808,58 @@ ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) } static void -addmedia(struct ieee80211com *ic, int mode, int mword) +addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) { -#define TURBO(m) ((m) | IFM_IEEE80211_TURBO) #define ADD(_ic, _s, _o) \ - ifmedia_add(&(_ic)->ic_media, \ + ifmedia_add(media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) static const u_int mopts[IEEE80211_MODE_MAX] = { - IFM_AUTO, /* IEEE80211_MODE_AUTO */ - IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */ - IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */ - IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */ - IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */ - TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */ - TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */ - TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */ - IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */ - IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */ + 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, + IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + IFM_IEEE80211_11NA, + IFM_IEEE80211_11NG, }; u_int mopt; - KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); mopt = mopts[mode]; - KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO, - ("no media mapping for mode %u", mode)); - - ADD(ic, mword, mopt); /* e.g. 11a auto */ - if (ic->ic_caps & IEEE80211_C_IBSS) - ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); - if (ic->ic_caps & IEEE80211_C_HOSTAP) - ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); - if (ic->ic_caps & IEEE80211_C_AHDEMO) - ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_caps & IEEE80211_C_MONITOR) - ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); + if (addsta) + ADD(ic, mword, mopt); /* STA mode has no cap */ + if (caps & IEEE80211_C_IBSS) + ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); + if (caps & IEEE80211_C_HOSTAP) + ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); + if (caps & IEEE80211_C_AHDEMO) + ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (caps & IEEE80211_C_MONITOR) + ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); + if (caps & IEEE80211_C_WDS) + ADD(media, mword, mopt | IFM_IEEE80211_WDS); #undef ADD -#undef TURBO } /* * Setup the media data structures according to the channel and - * rate tables. This must be called by the driver after - * ieee80211_attach and before most anything else. + * rate tables. */ -void -ieee80211_media_init(struct ieee80211com *ic, +static int +ieee80211_media_setup(struct ieee80211com *ic, + struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { - struct ifnet *ifp = ic->ic_ifp; int i, j, mode, rate, maxrate, mword, r; const struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; - /* NB: this works because the structure is initialized to zero */ - if (LIST_EMPTY(&ic->ic_media.ifm_list)) { - /* - * Do late attach work that must wait for any subclass - * (i.e. driver) work such as overriding methods. - */ - ieee80211_node_lateattach(ic); - } else { - /* - * We are re-initializing the channel list; clear - * the existing media state as the media routines - * don't suppress duplicates. - */ - ifmedia_removeall(&ic->ic_media); - ieee80211_chan_init(ic); - } - ieee80211_power_lateattach(ic); - /* * Fill in media characteristics. */ - ifmedia_init(&ic->ic_media, 0, media_change, media_stat); + ifmedia_init(media, 0, media_change, media_stat); maxrate = 0; /* * Add media for legacy operating modes. @@ -549,7 +868,7 @@ ieee80211_media_init(struct ieee80211com *ic, for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; - addmedia(ic, mode, IFM_AUTO); + addmedia(media, caps, addsta, mode, IFM_AUTO); if (mode == IEEE80211_MODE_AUTO) continue; rs = &ic->ic_sup_rates[mode]; @@ -558,7 +877,7 @@ ieee80211_media_init(struct ieee80211com *ic, mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; - addmedia(ic, mode, mword); + addmedia(media, caps, addsta, mode, mword); /* * Add legacy rate to the collection of all rates. */ @@ -582,7 +901,8 @@ ieee80211_media_init(struct ieee80211com *ic, if (mword == 0) continue; /* NB: remove media options from mword */ - addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); + addmedia(media, caps, addsta, + IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); } /* * Add HT/11n media. Note that we do not have enough @@ -593,24 +913,51 @@ ieee80211_media_init(struct ieee80211com *ic, for (; mode < IEEE80211_MODE_MAX; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; - addmedia(ic, mode, IFM_AUTO); - addmedia(ic, mode, IFM_IEEE80211_MCS); + addmedia(media, caps, addsta, mode, IFM_AUTO); + addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { - addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); + addmedia(media, caps, addsta, + IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); /* XXX could walk htrates */ /* XXX known array size */ - if (ieee80211_htrates[15] > maxrate) - maxrate = ieee80211_htrates[15]; + if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) + maxrate = ieee80211_htrates[15].ht40_rate_400ns; + } + return maxrate; +} + +void +ieee80211_media_init(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + int maxrate; + + /* NB: this works because the structure is initialized to zero */ + if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { + /* + * We are re-initializing the channel list; clear + * the existing media state as the media routines + * don't suppress duplicates. + */ + ifmedia_removeall(&ic->ic_media); } + ieee80211_chan_init(ic); + /* + * Recalculate media settings in case new channel list changes + * the set of available modes. + */ + maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, + ieee80211com_media_change, ieee80211com_media_status); /* NB: strip explicit mode; we're actually in autoselect */ ifmedia_set(&ic->ic_media, media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK); - if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); + + /* XXX need to propagate new media settings to vap's */ } const struct ieee80211_rateset * @@ -701,216 +1048,133 @@ ieee80211_announce_channels(struct ieee80211com *ic) } } -/* - * 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; -} - -static int -findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) -{ -#define IEEERATE(_ic,_m,_i) \ - ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) - int i, nrates = ic->ic_sup_rates[mode].rs_nrates; - for (i = 0; i < nrates; i++) - if (IEEERATE(ic, mode, i) == rate) - return i; - return -1; -#undef IEEERATE -} - -/* - * Convert a media specification to a rate index and possibly a mode - * (if the rate is fixed and the mode is specified as ``auto'' then - * we need to lock down the mode so the index is meanginful). - */ static int -checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +media2mode(const struct ieee80211com *ic, + const struct ifmedia_entry *ime, enum ieee80211_phymode *mode) { - - /* - * Check the rate table for the specified/current phy. - */ - if (mode == IEEE80211_MODE_AUTO) { - int i; - /* - * In autoselect mode search for the rate. - */ - for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { - if (isset(ic->ic_modecaps, i) && - findrate(ic, i, rate) != -1) - return 1; - } - return 0; - } else { - /* - * Mode is fixed, check for rate. - */ - return (findrate(ic, mode, rate) != -1); - } -} - -/* - * Handle a media change request. - */ -int -ieee80211_media_change(struct ifnet *ifp) -{ - struct ieee80211com *ic; - struct ifmedia_entry *ime; - enum ieee80211_opmode newopmode; - enum ieee80211_phymode newphymode; - int 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. - */ switch (IFM_MODE(ime->ifm_media)) { case IFM_IEEE80211_11A: - newphymode = IEEE80211_MODE_11A; + *mode = IEEE80211_MODE_11A; break; case IFM_IEEE80211_11B: - newphymode = IEEE80211_MODE_11B; + *mode = IEEE80211_MODE_11B; break; case IFM_IEEE80211_11G: - newphymode = IEEE80211_MODE_11G; + *mode = IEEE80211_MODE_11G; break; case IFM_IEEE80211_FH: - newphymode = IEEE80211_MODE_FH; + *mode = IEEE80211_MODE_FH; break; case IFM_IEEE80211_11NA: - newphymode = IEEE80211_MODE_11NA; + *mode = IEEE80211_MODE_11NA; break; case IFM_IEEE80211_11NG: - newphymode = IEEE80211_MODE_11NG; + *mode = IEEE80211_MODE_11NG; break; case IFM_AUTO: - newphymode = IEEE80211_MODE_AUTO; + *mode = IEEE80211_MODE_AUTO; break; default: - return EINVAL; + return 0; } /* * Turbo mode is an ``option''. * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { - if (newphymode == IEEE80211_MODE_11A) { + if (*mode == IEEE80211_MODE_11A) { if (ic->ic_flags & IEEE80211_F_TURBOP) - newphymode = IEEE80211_MODE_TURBO_A; + *mode = IEEE80211_MODE_TURBO_A; else - newphymode = IEEE80211_MODE_STURBO_A; - } else if (newphymode == IEEE80211_MODE_11G) - newphymode = IEEE80211_MODE_TURBO_G; + *mode = IEEE80211_MODE_STURBO_A; + } else if (*mode == IEEE80211_MODE_11G) + *mode = IEEE80211_MODE_TURBO_G; else - return EINVAL; + return 0; } /* XXX HT40 +/- */ - /* - * Next, the fixed/variable rate. - */ - newrate = ic->ic_fixed_rate; - if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { - /* - * Convert media subtype to rate. - */ - newrate = ieee80211_media2rate(ime->ifm_media); - if (newrate == 0 || !checkrate(ic, newphymode, newrate)) - return EINVAL; - } else - newrate = IEEE80211_FIXED_RATE_NONE; + return 1; +} + +/* + * Handle a media change request on the underlying + * interface; we accept mode changes only. + */ +int +ieee80211com_media_change(struct ifnet *ifp) +{ + struct ieee80211com *ic = ifp->if_l2com; + struct ifmedia_entry *ime = ic->ic_media.ifm_cur; + enum ieee80211_phymode newphymode; + int error = 0; /* - * Deduce new operating mode but don't install it just yet. + * First, identify the phy mode. */ - if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == - (IFM_IEEE80211_ADHOC|IFM_FLAG0)) - newopmode = IEEE80211_M_AHDEMO; - else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) - newopmode = IEEE80211_M_HOSTAP; - else if (ime->ifm_media & IFM_IEEE80211_ADHOC) - newopmode = IEEE80211_M_IBSS; - else if (ime->ifm_media & IFM_IEEE80211_MONITOR) - newopmode = IEEE80211_M_MONITOR; - else - newopmode = IEEE80211_M_STA; + if (!media2mode(ic, ime, &newphymode)) + return EINVAL; + /* NB: mode must be supported, no need to check */ /* * Handle phy mode change. */ - if (ic->ic_des_mode != newphymode) { /* change phy mode */ - ic->ic_des_mode = newphymode; - error = ENETRESET; - } + IEEE80211_LOCK(ic); + if (ic->ic_curmode != newphymode) { /* change phy mode */ + struct ieee80211vap *vap; - /* - * Committed to changes, install the rate setting. - */ - if (ic->ic_fixed_rate != newrate) { - ic->ic_fixed_rate = newrate; /* set fixed tx rate */ - error = ENETRESET; + (void) ieee80211_setmode(ic, newphymode); + /* + * Propagate new state to each vap. + */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + } } + IEEE80211_UNLOCK(ic); + return error; +} - /* - * Handle operating mode change. - */ - if (ic->ic_opmode != newopmode) { - ic->ic_opmode = newopmode; - switch (newopmode) { - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - case IEEE80211_M_STA: - case IEEE80211_M_MONITOR: - case IEEE80211_M_WDS: - ic->ic_flags &= ~IEEE80211_F_IBSSON; - break; - case IEEE80211_M_IBSS: - ic->ic_flags |= IEEE80211_F_IBSSON; - break; - } +static int +findrate(const struct ieee80211com *ic, enum ieee80211_phymode m, int r) +{ + int i, nrates; + + for (i = 0, nrates = ic->ic_sup_rates[m].rs_nrates; i < nrates; i++) + if ((ic->ic_sup_rates[m].rs_rates[i] & IEEE80211_RATE_VAL) == r) + return i; + return -1; +} + +/* + * Handle a media change request on the vap interface. + */ +int +ieee80211_media_change(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ifmedia_entry *ime = vap->iv_media.ifm_cur; + struct ieee80211com *ic = vap->iv_ic; + int newrate; + + /* XXX this won't work unless ic_curmode is != IEEE80211_MODE_AUTO */ + if (ic->ic_curmode == IEEE80211_MODE_AUTO) + return EINVAL; + if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { /* - * Yech, slot time may change depending on the - * operating mode so reset it to be sure everything - * is setup appropriately. + * NB: this can only be used to specify a legacy rate. */ - ieee80211_reset_erp(ic); - ieee80211_wme_initparams(ic); /* after opmode change */ - error = ENETRESET; + newrate = ieee80211_media2rate(ime->ifm_media); + if (newrate == 0) + return EINVAL; + if (findrate(ic, ic->ic_curmode, newrate) == -1) + return EINVAL; + } else { + newrate = IEEE80211_FIXED_RATE_NONE; } -#ifdef notdef - if (error == 0) - ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); -#endif - return error; + if (newrate != vap->iv_txparms[ic->ic_curmode].ucastrate) { + vap->iv_txparms[ic->ic_curmode].ucastrate = newrate; + return ENETRESET; + } + return 0; } /* @@ -939,7 +1203,7 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; break; case IEEE80211_M_WDS: - /* should not come here */ + status |= IFM_IEEE80211_WDS; break; } if (IEEE80211_IS_CHAN_HTA(chan)) { @@ -959,53 +1223,70 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) if (IEEE80211_IS_CHAN_TURBO(chan)) status |= IFM_IEEE80211_TURBO; - +#if 0 + if (IEEE80211_IS_CHAN_HT20(chan)) + status |= IFM_IEEE80211_HT20; + if (IEEE80211_IS_CHAN_HT40(chan)) + status |= IFM_IEEE80211_HT40; +#endif return status; } +static void +ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap; + + imr->ifm_status = IFM_AVALID; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_ifp->if_flags & IFF_UP) { + imr->ifm_status |= IFM_ACTIVE; + break; + } + imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); + if (imr->ifm_status & IFM_ACTIVE) + imr->ifm_current = imr->ifm_active; +} + void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct ieee80211com *ic; + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; enum ieee80211_phymode mode; - const 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; /* * NB: use the current channel's mode to lock down a xmit * rate only when running; otherwise we may have a mismatch * in which case the rate will not be convertible. */ - if (ic->ic_state == IEEE80211_S_RUN) { + if (vap->iv_state == IEEE80211_S_RUN) { imr->ifm_status |= IFM_ACTIVE; mode = ieee80211_chan2mode(ic->ic_curchan); } else mode = IEEE80211_MODE_AUTO; - imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); + imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); /* * Calculate a current rate if possible. */ - if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { + if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { /* * A fixed rate is set, report that. */ imr->ifm_active |= ieee80211_rate2media(ic, - ic->ic_fixed_rate, mode); - } else if (ic->ic_opmode == IEEE80211_M_STA) { + vap->iv_txparms[mode].ucastrate, mode); + } else if (vap->iv_opmode == IEEE80211_M_STA) { /* * In station mode report the current transmit rate. - * XXX HT rate */ - rs = &ic->ic_bss->ni_rates; imr->ifm_active |= ieee80211_rate2media(ic, - rs->rs_rates[ic->ic_bss->ni_txrate], mode); + vap->iv_bss->ni_txrate, mode); } else imr->ifm_active |= IFM_AUTO; + if (imr->ifm_status & IFM_ACTIVE) + imr->ifm_current = imr->ifm_active; } /* @@ -1024,11 +1305,10 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) * and used instead. */ if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) - ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode); + ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); ic->ic_curmode = mode; ieee80211_reset_erp(ic); /* reset ERP state */ - ieee80211_wme_initparams(ic); /* reset WME stat */ return 0; } diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 97f9365..3e91068 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -696,9 +696,13 @@ struct ieee80211_country_ie { uint8_t schan; /* starting channel */ uint8_t nchan; /* number channels */ uint8_t maxtxpwr; /* tx power cap */ - } __packed band[10]; /* sub bands */ + } __packed band[1]; /* sub bands (NB: var size) */ } __packed; +#define IEEE80211_COUNTRY_MAX_BANDS 84 /* max possible bands */ +#define IEEE80211_COUNTRY_MAX_SIZE \ + (sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1)) + /* * 802.11h Channel Switch Announcement (CSA). */ @@ -889,6 +893,9 @@ enum { #define IEEE80211_WEP_IVLEN 3 /* 24bit */ #define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ #define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ +#define IEEE80211_WEP_TOTLEN (IEEE80211_WEP_IVLEN + \ + IEEE80211_WEP_KIDLEN + \ + IEEE80211_WEP_CRCLEN) #define IEEE80211_WEP_NKID 4 /* number of key ids */ /* @@ -924,12 +931,12 @@ enum { /* * 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. + * than is feasible so we use a default of IEEE80211_AID_DEF. + * This number may be overridden by the driver and/or by + * user configuration but may not be less than IEEE80211_AID_MIN + * (see _ieee80211.h for implementation-specific settings). */ #define IEEE80211_AID_MAX 2007 -#define IEEE80211_AID_DEF 128 #define IEEE80211_AID(b) ((b) &~ 0xc000) diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c index c5305d0..13407a4 100644 --- a/sys/net80211/ieee80211_acl.c +++ b/sys/net80211/ieee80211_acl.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 MAC ACL support. * - * When this module is loaded the sender address of each received + * When this module is loaded the sender address of each auth mgt * 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 @@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$"); * and if found the frame is either accepted (ACL_POLICY_ALLOW) * or rejected (ACL_POLICY_DENT). */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -57,6 +59,12 @@ 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 */ + /* + * NB: ACL_POLICY_RADIUS must be the same value as + * IEEE80211_MACCMD_POLICY_RADIUS because of the way + * acl_getpolicy() works. + */ + ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */ }; #define ACL_HASHSIZE 32 @@ -72,7 +80,7 @@ struct aclstate { int as_nacls; TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; - struct ieee80211com *as_ic; + struct ieee80211vap *as_vap; }; /* simple hash is enough for variation of macaddr */ @@ -81,10 +89,13 @@ struct aclstate { MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); -static int acl_free_all(struct ieee80211com *); +static int acl_free_all(struct ieee80211vap *); + +/* number of references from net80211 layer */ +static int nrefs = 0; static int -acl_attach(struct ieee80211com *ic) +acl_attach(struct ieee80211vap *vap) { struct aclstate *as; @@ -95,20 +106,24 @@ acl_attach(struct ieee80211com *ic) ACL_LOCK_INIT(as, "acl"); TAILQ_INIT(&as->as_list); as->as_policy = ACL_POLICY_OPEN; - as->as_ic = ic; - ic->ic_as = as; + as->as_vap = vap; + vap->iv_as = as; + nrefs++; /* NB: we assume caller locking */ return 1; } static void -acl_detach(struct ieee80211com *ic) +acl_detach(struct ieee80211vap *vap) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; - acl_free_all(ic); - ic->ic_as = NULL; + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ + + acl_free_all(vap); + vap->iv_as = NULL; ACL_LOCK_DESTROY(as); - FREE(as, M_DEVBUF); + FREE(as, M_80211_ACL); } static __inline struct acl * @@ -137,12 +152,13 @@ _acl_free(struct aclstate *as, struct acl *acl) } static int -acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; switch (as->as_policy) { case ACL_POLICY_OPEN: + case ACL_POLICY_RADIUS: return 1; case ACL_POLICY_ALLOW: return _find_acl(as, mac) != NULL; @@ -153,15 +169,15 @@ acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) } static int -acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; struct acl *acl, *new; int hash; MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); if (new == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: add %s failed, no memory\n", ether_sprintf(mac)); /* XXX statistic */ return ENOMEM; @@ -173,7 +189,7 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { ACL_UNLOCK(as); FREE(new, M_80211_ACL); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: add %s failed, already present\n", ether_sprintf(mac)); return EEXIST; @@ -185,15 +201,15 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) as->as_nacls++; ACL_UNLOCK(as); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: add %s\n", ether_sprintf(mac)); return 0; } static int -acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; struct acl *acl; ACL_LOCK(as); @@ -202,7 +218,7 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) _acl_free(as, acl); ACL_UNLOCK(as); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: remove %s%s\n", ether_sprintf(mac), acl == NULL ? ", not present" : ""); @@ -210,12 +226,12 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) } static int -acl_free_all(struct ieee80211com *ic) +acl_free_all(struct ieee80211vap *vap) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; struct acl *acl; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); ACL_LOCK(as); while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) @@ -226,11 +242,11 @@ acl_free_all(struct ieee80211com *ic) } static int -acl_setpolicy(struct ieee80211com *ic, int policy) +acl_setpolicy(struct ieee80211vap *vap, int policy) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: set policy to %u\n", policy); switch (policy) { @@ -243,6 +259,9 @@ acl_setpolicy(struct ieee80211com *ic, int policy) case IEEE80211_MACCMD_POLICY_DENY: as->as_policy = ACL_POLICY_DENY; break; + case IEEE80211_MACCMD_POLICY_RADIUS: + as->as_policy = ACL_POLICY_RADIUS; + break; default: return EINVAL; } @@ -250,24 +269,24 @@ acl_setpolicy(struct ieee80211com *ic, int policy) } static int -acl_getpolicy(struct ieee80211com *ic) +acl_getpolicy(struct ieee80211vap *vap) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; return as->as_policy; } static int -acl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq) +acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) { return EINVAL; } static int -acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq) +acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) { - struct aclstate *as = ic->ic_as; + struct aclstate *as = vap->iv_as; struct acl *acl; struct ieee80211req_maclist *ap; int error, space, i; @@ -283,7 +302,7 @@ acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq) return 0; /* NB: must not error */ } MALLOC(ap, struct ieee80211req_maclist *, space, - M_TEMP, M_NOWAIT); + M_TEMP, M_NOWAIT); if (ap == NULL) return ENOMEM; i = 0; @@ -317,31 +336,4 @@ static const struct ieee80211_aclator mac = { .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); +IEEE80211_ACL_MODULE(wlan_acl, mac, 1); diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c new file mode 100644 index 0000000..ce9c4cb --- /dev/null +++ b/sys/net80211/ieee80211_adhoc.c @@ -0,0 +1,877 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 IBSS mode support. + */ +#include "opt_inet.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_llc.h> +#include <net/ethernet.h> + +#include <net/bpf.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_adhoc.h> +#include <net80211/ieee80211_input.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +static void adhoc_vattach(struct ieee80211vap *); +static int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int adhoc_input(struct ieee80211_node *, struct mbuf *, + int rssi, int noise, uint32_t rstamp); +static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *, + int subtype, int rssi, int noise, uint32_t rstamp); + +void +ieee80211_adhoc_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach; + ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach; +} + +void +ieee80211_adhoc_detach(struct ieee80211com *ic) +{ +} + +static void +adhoc_vdetach(struct ieee80211vap *vap) +{ +} + +static void +adhoc_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = adhoc_newstate; + vap->iv_input = adhoc_input; + vap->iv_recv_mgmt = adhoc_recv_mgmt; + vap->iv_opdetach = adhoc_vdetach; +} + +/* + * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler. + */ +static int +adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ +#ifdef IEEE80211_DEBUG + struct ieee80211com *ic = vap->iv_ic; +#endif + struct ieee80211_node *ni; + enum ieee80211_state ostate; + + IEEE80211_LOCK_ASSERT(vap->iv_ic); + + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate], arg); + vap->iv_state = nstate; /* state transition */ + if (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(vap); /* background scan */ + ni = vap->iv_bss; /* NB: no reference held */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) + callout_stop(&vap->iv_swbmiss); + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + default: + break; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + } + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_RUN: /* beacon miss */ + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && + !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { + /* + * Already have a channel; bypass the + * scan and startup immediately. + */ + ieee80211_create_ibss(vap, vap->iv_des_chan); + break; + } + /* + * Initiate a scan. We can come here as a result + * of an IEEE80211_IOC_SCAN_REQ too in which case + * the vap will be marked with IEEE80211_FEXT_SCANREQ + * and the scan request parameters will be present + * in iv_scanreq. Otherwise we do the default. + */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { + ieee80211_check_scan(vap, + vap->iv_scanreq_flags, + vap->iv_scanreq_duration, + vap->iv_scanreq_mindwell, + vap->iv_scanreq_maxdwell, + vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; + } else + ieee80211_check_scan_current(vap); + break; + case IEEE80211_S_SCAN: + /* + * This can happen because of a change in state + * that requires a reset. Trigger a new scan + * unless we're in manual roaming mode in which + * case an application must issue an explicit request. + */ + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + default: + goto invalid; + } + break; + case IEEE80211_S_RUN: + if (vap->iv_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } + switch (ostate) { + case IEEE80211_S_SCAN: +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap)) { + ieee80211_note(vap, + "synchronized with %s ssid ", + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(vap->iv_bss->ni_essid, + ni->ni_esslen); + /* XXX MCS/HT */ + printf(" channel %d start %uMb\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), + IEEE80211_RATE2MBS(ni->ni_txrate)); + } +#endif + break; + default: + goto invalid; + } + /* + * 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_S_SLEEP: + ieee80211_sta_pwrsave(vap, 0); + break; + default: + invalid: + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "%s: invalid state transition %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); + break; + } + return 0; +} + +/* + * 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 ieee80211vap *vap, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return 1; + } + return 1; +} + +/* + * Process a received frame. The node associated with the sender + * should be supplied. If nothing was found in the node table then + * the caller is assumed to supply a reference to iv_bss instead. + * The RSSI and a timestamp are also supplied. The RSSI data is used + * during AP scanning to select a AP to associate with; it can have + * any units so long as values have consistent units and higher values + * mean ``better signal''. The receive timestamp is currently not used + * by the 802.11 layer. + */ +static int +adhoc_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp) +{ +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct ether_header *eh; + int hdrspace, need_tap; + uint8_t dir, type, subtype, qos; + uint8_t *bssid; + uint16_t rxseq; + + if (m->m_flags & M_AMPDU) { + /* + * Fastpath for A-MPDU reorder q resubmission. Frames + * w/ M_AMPDU marked have already passed through here + * but were received out of order and been held on the + * reorder queue. When resubmitted they are marked + * with the M_AMPDU flag and we can bypass most of the + * normal processing. + */ + wh = mtod(m, struct ieee80211_frame *); + type = IEEE80211_FC0_TYPE_DATA; + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + subtype = IEEE80211_FC0_SUBTYPE_QOS; + hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ + goto resubmit_ampdu; + } + + KASSERT(ni != NULL, ("null node")); + ni->ni_inact = ni->ni_inact_reload; + + need_tap = 1; /* mbuf need to be tapped. */ + type = -1; /* undefined */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_pkthdr.len); + vap->iv_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) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); + vap->iv_stats.is_rx_badversion++; + goto err; + } + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + 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(vap, + IEEE80211_MSG_ANY, ni->ni_macaddr, + NULL, "too short (2): len %u", + m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } + bssid = wh->i_addr3; + } + /* + * Validate the bssid. + */ + if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && + !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { + /* not interested in */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); + vap->iv_stats.is_rx_wrongbss++; + goto out; + } + /* + * Data frame, cons up a node when it doesn't + * exist. This should probably done after an ACL check. + */ + if (type == IEEE80211_FC0_TYPE_DATA && + ni == vap->iv_bss && + !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(vap, wh->i_addr2); + if (ni == NULL) { + /* NB: stat kept for alloc failure */ + goto err; + } + } + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + if (HAS_SEQ(type)) { + uint8_t tid = ieee80211_gettid(wh); + if (IEEE80211_QOS_HAS_SEQ(wh) && + TID_TO_WME_AC(tid) >= WME_AC_VI) + ic->ic_wme.wme_hipri_traffic++; + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && + (wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { + /* duplicate, discard */ + IEEE80211_DISCARD_MAC(vap, 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); + vap->iv_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + goto out; + } + ni->ni_rxseqs[tid] = rxseq; + } + } + + 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(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "data too short: expecting %u", hdrspace); + vap->iv_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + /* XXX no power-save support */ + + /* + * Handle A-MPDU re-ordering. The station must be + * associated and negotiated HT. The frame must be + * a QoS frame (not QoS null data) and not previously + * processed for A-MPDU re-ordering. If the frame is + * to be processed directly then ieee80211_ampdu_reorder + * will return 0; otherwise it has consumed the mbuf + * and we should do nothing more with it. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + subtype == IEEE80211_FC0_SUBTYPE_QOS && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + + /* + * 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 ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "WEP", "%s", "PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + IEEE80211_NODE_STAT(ni, rx_noprivacy); + goto out; + } + key = ieee80211_crypto_decap(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 { + /* XXX M_WEP and IEEE80211_F_PRIVACY */ + key = NULL; + } + + /* + * Save QoS bits for use below--before we strip the header. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { + qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? + ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : + ((struct ieee80211_qosframe *)wh)->i_qos[0]; + } else + qos = 0; + + /* + * Next up, any fragmentation. + */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + m = ieee80211_defrag(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(vap, key, m, 0)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "demic error"); + vap->iv_stats.is_rx_demicfail++; + IEEE80211_NODE_STAT(ni, rx_demicfail); + goto out; + } + + /* copy to listener after decrypt */ + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + need_tap = 0; + + /* + * Finally, strip the 802.11 header. + */ + m = ieee80211_decap(vap, m, hdrspace); + if (m == NULL) { + /* XXX mask bit to check for both */ + /* don't count Null data frames as errors */ + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || + subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) + goto out; + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "decap error"); + vap->iv_stats.is_rx_decap++; + IEEE80211_NODE_STAT(ni, rx_decap); + goto err; + } + 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(vap, IEEE80211_MSG_INPUT, + eh->ether_shost, "data", + "unauthorized port: ether type 0x%x len %u", + eh->ether_type, m->m_pkthdr.len); + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && + (key == NULL && (m->m_flags & M_WEP) == 0) && + eh->ether_type != htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + vap->iv_stats.is_rx_unencrypted++; + IEEE80211_NODE_STAT(ni, rx_unencrypted); + goto out; + } + } + /* XXX require HT? */ + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + m->m_pkthdr.len >= 3*FF_LLC_SIZE) { + struct llc *llc; + + /* + * Check for fast-frame tunnel encapsulation. + */ + if (m->m_len < FF_LLC_SIZE && + (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "m_pullup(llc) failed"); + vap->iv_stats.is_rx_tooshort++; + return IEEE80211_FC0_TYPE_DATA; + } + llc = (struct llc *)(mtod(m, uint8_t *) + + sizeof(struct ether_header)); + if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { + m_adj(m, FF_LLC_SIZE); + m = ieee80211_decap_fastframe(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + } +#undef FF_LLC_SIZE + if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL) + ieee80211_deliver_data(ni->ni_wdsvap, ni, m); + else + ieee80211_deliver_data(vap, ni, m); + return IEEE80211_FC0_TYPE_DATA; + + case IEEE80211_FC0_TYPE_MGT: + vap->iv_stats.is_rx_mgmt++; + IEEE80211_NODE_STAT(ni, rx_mgmt); + if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto err; + } + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "mgt", "too short: len %u", + m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } +#ifdef IEEE80211_DEBUG + if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || + ieee80211_msg_dumppkts(vap)) { + if_printf(ifp, "received %s from %s rssi %d\n", + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(wh->i_addr2), rssi); + } +#endif + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "WEP set but not permitted"); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + /* NB: only IBSS mode gets mgt frames */ + if (vap->iv_opmode == IEEE80211_M_IBSS) + vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); + m_freem(m); + return IEEE80211_FC0_TYPE_MGT; + + case IEEE80211_FC0_TYPE_CTL: + vap->iv_stats.is_rx_ctl++; + IEEE80211_NODE_STAT(ni, rx_ctrl); + goto out; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "bad", "frame type 0x%x", type); + /* should not come here */ + break; + } +err: + ifp->if_ierrors++; +out: + if (m != NULL) { + if (bpf_peers_present(vap->iv_rawbpf) && need_tap) + bpf_mtap(vap->iv_rawbpf, m); + m_freem(m); + } + return type; +#undef SEQ_LEQ +} + +static int +is11bclient(const uint8_t *rates, const uint8_t *xrates) +{ + static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); + int i; + + /* NB: the 11b clients we care about will not have xrates */ + if (xrates != NULL || rates == NULL) + return 0; + for (i = 0; i < rates[1]; i++) { + int r = rates[2+i] & IEEE80211_RATE_VAL; + if (r > 2*11 || ((1<<r) & brates) == 0) + return 0; + } + return 1; +} + +static void +adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, + int subtype, int rssi, int noise, uint32_t rstamp) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm, *sfrm; + uint8_t *ssid, *rates, *xrates; + + 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 to discover neighbors. + */ + if (ieee80211_parse_beacon(ni, m0, &scan) != 0) + return; + /* + * Count frame now that we know it's to be processed. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + vap->iv_stats.is_rx_beacon++; /* XXX remove */ + IEEE80211_NODE_STAT(ni, rx_beacons); + } else + IEEE80211_NODE_STAT(ni, rx_proberesp); + /* + * 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(vap, 1); + ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; + } + ieee80211_add_scan(vap, &scan, wh, + subtype, rssi, noise, 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(vap, 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) { + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + } + } + break; + } + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { + /* frame must be directed */ + vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ + return; + } + + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + ssid = rates = xrates = NULL; + sfrm = frm; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1], return); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); + IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); + if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, + "%s", "no ssid with ssid suppression enabled"); + vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ + return; + } + + /* XXX find a better class or define it's own */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, + "%s", "recv probe req"); + /* + * Some legacy 11b clients cannot hack a complete + * probe response frame. When the request includes + * only a bare-bones rate set, communicate this to + * the transmit side. + */ + ieee80211_send_proberesp(vap, wh->i_addr2, + is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); + break; + + case IEEE80211_FC0_SUBTYPE_ACTION: { + const struct ieee80211_action *ia; + + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + /* + * action frame format: + * [1] category + * [1] action + * [tlv] parameters + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action), return); + ia = (const struct ieee80211_action *) frm; + + vap->iv_stats.is_rx_action++; + IEEE80211_NODE_STAT(ni, rx_action); + + /* verify frame payloads but defer processing */ + /* XXX maybe push this to method */ + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_BA: + switch (ia->ia_action) { + case IEEE80211_ACTION_BA_ADDBA_REQUEST: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbarequest), + return); + break; + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbaresponse), + return); + break; + case IEEE80211_ACTION_BA_DELBA: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_delba), + return); + break; + } + break; + case IEEE80211_ACTION_CAT_HT: + switch (ia->ia_action) { + case IEEE80211_ACTION_HT_TXCHWIDTH: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ht_txchwidth), + return); + break; + } + break; + } + ic->ic_recv_action(ni, frm, efrm); + break; + } + + case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_DISASSOC: + vap->iv_stats.is_rx_mgtdiscard++; + return; + + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); + vap->iv_stats.is_rx_badsubtype++; + break; + } +} +#undef IEEE80211_VERIFY_LENGTH +#undef IEEE80211_VERIFY_ELEMENT diff --git a/sys/net80211/ieee80211_adhoc.h b/sys/net80211/ieee80211_adhoc.h new file mode 100644 index 0000000..d8e19e5 --- /dev/null +++ b/sys/net80211/ieee80211_adhoc.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_ADHOC_H_ +#define _NET80211_IEEE80211_ADHOC_H_ + +/* + * Adhoc-mode (ibss+ahdemo) implementation definitions. + */ +void ieee80211_adhoc_attach(struct ieee80211com *); +void ieee80211_adhoc_detach(struct ieee80211com *); +#endif /* !_NET80211_IEEE80211_STA_H_ */ diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c index 5b34768..a759e64 100644 --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -28,6 +28,8 @@ __FBSDID("$FreeBSD$"); * INRIA Sophia - Projet Planete * http://www-sop.inria.fr/rapports/sophia/RR-5208.html */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> @@ -51,66 +53,90 @@ __FBSDID("$FreeBSD$"); ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) #define is_enough(amn) \ ((amn)->amn_txcnt > 10) -#define is_min_rate(ni) \ - ((ni)->ni_txrate == 0) -#define is_max_rate(ni) \ - ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1) -#define increase_rate(ni) \ - ((ni)->ni_txrate++) -#define decrease_rate(ni) \ - ((ni)->ni_txrate--) -#define reset_cnt(amn) \ - do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0) + +static void amrr_sysctlattach(struct ieee80211_amrr *amrr, + struct sysctl_ctx_list *ctx, struct sysctl_oid *tree); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +void +ieee80211_amrr_setinterval(struct ieee80211_amrr *amrr, int msecs) +{ + int t; + + if (msecs < 100) + msecs = 100; + t = msecs_to_ticks(msecs); + amrr->amrr_interval = (t < 1) ? 1 : t; +} void ieee80211_amrr_init(struct ieee80211_amrr *amrr, - struct ieee80211com *ic, int amin, int amax) + struct ieee80211vap *vap, int amin, int amax, int interval) { /* XXX bounds check? */ amrr->amrr_min_success_threshold = amin; amrr->amrr_max_success_threshold = amax; - amrr->amrr_ic = ic; + ieee80211_amrr_setinterval(amrr, interval); + + amrr_sysctlattach(amrr, vap->iv_sysctl, vap->iv_oid); +} + +void +ieee80211_amrr_cleanup(struct ieee80211_amrr *amrr) +{ } void ieee80211_amrr_node_init(struct ieee80211_amrr *amrr, - struct ieee80211_amrr_node *amn) + struct ieee80211_amrr_node *amn, struct ieee80211_node *ni) { + const struct ieee80211_rateset *rs = &ni->ni_rates; + + amn->amn_amrr = amrr; amn->amn_success = 0; amn->amn_recovery = 0; amn->amn_txcnt = amn->amn_retrycnt = 0; amn->amn_success_threshold = amrr->amrr_min_success_threshold; + + /* pick initial rate */ + for (amn->amn_rix = rs->rs_nrates - 1; + amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72; + amn->amn_rix--) + ; + ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + amn->amn_ticks = ticks; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR initial rate %d", ni->ni_txrate); } -/* - * Update ni->ni_txrate. - */ -void -ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni, - struct ieee80211_amrr_node *amn) +static int +amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, + struct ieee80211_node *ni) { - int need_change = 0; + int rix = amn->amn_rix; + + KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); - if (is_success(amn) && is_enough(amn)) { + if (is_success(amn)) { amn->amn_success++; if (amn->amn_success >= amn->amn_success_threshold && - !is_max_rate(ni)) { + rix + 1 < ni->ni_rates.rs_nrates) { amn->amn_recovery = 1; amn->amn_success = 0; - increase_rate(ni); - IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL, - "AMRR increasing rate %d (txcnt=%d " - "retrycnt=%d)\n", - ni->ni_rates.rs_rates[ni->ni_txrate] & - IEEE80211_RATE_VAL, + rix++; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", + ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); - need_change = 1; } else { amn->amn_recovery = 0; } } else if (is_failure(amn)) { amn->amn_success = 0; - if (!is_min_rate(ni)) { + if (rix > 0) { if (amn->amn_recovery) { amn->amn_success_threshold *= 2; if (amn->amn_success_threshold > @@ -121,44 +147,80 @@ ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni, amn->amn_success_threshold = amrr->amrr_min_success_threshold; } - decrease_rate(ni); - IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL, - "AMRR decreasing rate %d (txcnt=%d " - "retrycnt=%d)\n", - ni->ni_rates.rs_rates[ni->ni_txrate] & - IEEE80211_RATE_VAL, + rix--; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", + ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); - need_change = 1; } amn->amn_recovery = 0; } - if (is_enough(amn) || need_change) - reset_cnt(amn); + /* reset counters */ + amn->amn_txcnt = 0; + amn->amn_retrycnt = 0; + + return rix; } /* - * Module glue. + * Return the rate index to use in sending a data frame. + * Update our internal state if it's been long enough. + * If the rate changes we also update ni_txrate to match. */ +int +ieee80211_amrr_choose(struct ieee80211_node *ni, + struct ieee80211_amrr_node *amn) +{ + struct ieee80211_amrr *amrr = amn->amn_amrr; + int rix; + + if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { + rix = amrr_update(amrr, amn, ni); + if (rix != amn->amn_rix) { + /* update public rate */ + ni->ni_txrate = + ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL; + amn->amn_rix = rix; + } + amn->amn_ticks = ticks; + } else + rix = amn->amn_rix; + return rix; +} + static int -amrr_modevent(module_t mod, int type, void *unused) +amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) { - switch (type) { - case MOD_LOAD: - if (bootverbose) - printf("wlan_amrr: <AMRR Transmit Rate Control Algorithm>\n"); - return 0; - case MOD_UNLOAD: - return 0; - } - return EINVAL; + struct ieee80211_amrr *amrr = arg1; + int msecs = ticks_to_msecs(amrr->amrr_interval); + int error; + + error = sysctl_handle_int(oidp, &msecs, 0, req); + if (error || !req->newptr) + return error; + ieee80211_amrr_setinterval(amrr, msecs); + return 0; +} + +static void +amrr_sysctlattach(struct ieee80211_amrr *amrr, + struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) +{ + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, amrr, + 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); + /* XXX bounds check values */ + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "amrr_max_sucess_threshold", CTLFLAG_RW, + &amrr->amrr_max_success_threshold, 0, ""); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "amrr_min_sucess_threshold", CTLFLAG_RW, + &amrr->amrr_min_success_threshold, 0, ""); } -static moduledata_t amrr_mod = { - "wlan_amrr", - amrr_modevent, - 0 -}; -DECLARE_MODULE(wlan_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan_amrr, 1); -MODULE_DEPEND(wlan_amrr, wlan, 1, 1, 1); +/* + * Module glue. + */ +IEEE80211_RATE_MODULE(amrr, 1); diff --git a/sys/net80211/ieee80211_amrr.h b/sys/net80211/ieee80211_amrr.h index 947d617..c03f699 100644 --- a/sys/net80211/ieee80211_amrr.h +++ b/sys/net80211/ieee80211_amrr.h @@ -32,12 +32,12 @@ /* * Rate control settings. */ -struct ieee80211com; +struct ieee80211vap; struct ieee80211_amrr { u_int amrr_min_success_threshold; u_int amrr_max_success_threshold; - struct ieee80211com *amrr_ic; + int amrr_interval; /* update interval (ticks) */ }; #define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD 1 @@ -47,18 +47,55 @@ struct ieee80211_amrr { * Rate control state for a given node. */ struct ieee80211_amrr_node { + struct ieee80211_amrr *amn_amrr;/* backpointer */ + int amn_rix; /* current rate index */ + int amn_ticks; /* time of last update */ + /* statistics */ + u_int amn_txcnt; u_int amn_success; - u_int amn_recovery; u_int amn_success_threshold; - u_int amn_txcnt; + u_int amn_recovery; u_int amn_retrycnt; }; -void ieee80211_amrr_init(struct ieee80211_amrr *, - struct ieee80211com *ic, int, int); +void ieee80211_amrr_init(struct ieee80211_amrr *, struct ieee80211vap *, + int, int, int); +void ieee80211_amrr_cleanup(struct ieee80211_amrr *); +void ieee80211_amrr_setinterval(struct ieee80211_amrr *, int); void ieee80211_amrr_node_init(struct ieee80211_amrr *, + struct ieee80211_amrr_node *, struct ieee80211_node *); +int ieee80211_amrr_choose(struct ieee80211_node *, struct ieee80211_amrr_node *); -void ieee80211_amrr_choose(struct ieee80211_amrr *, struct ieee80211_node *, - struct ieee80211_amrr_node *); +#define IEEE80211_AMRR_SUCCESS 1 +#define IEEE80211_AMRR_FAILURE 0 + +/* + * Update statistics with tx complete status. Ok is non-zero + * if the packet is known to be ACK'd. Retries has the number + * retransmissions (i.e. xmit attempts - 1). + */ +static __inline void +ieee80211_amrr_tx_complete(struct ieee80211_amrr_node *amn, + int ok, int retries) +{ + amn->amn_txcnt++; + if (ok) + amn->amn_success++; + amn->amn_retrycnt += retries; +} + +/* + * Set tx count/retry statistics explicitly. Intended for + * drivers that poll the device for statistics maintained + * in the device. + */ +static __inline void +ieee80211_amrr_tx_update(struct ieee80211_amrr_node *amn, + int txcnt, int success, int retrycnt) +{ + amn->amn_txcnt = txcnt; + amn->amn_success = success; + amn->amn_retrycnt = retrycnt; +} #endif /* _NET80211_IEEE80211_AMRR_H_ */ diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c index 83d7c3f..d644d0c 100644 --- a/sys/net80211/ieee80211_crypto.c +++ b/sys/net80211/ieee80211_crypto.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,11 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 generic crypto support. */ +#include "opt_wlan.h" + #include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> @@ -41,23 +45,25 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> +MALLOC_DEFINE(M_80211_CRYPTO, "80211crypto", "802.11 crypto state"); + +static int _ieee80211_crypto_delkey(struct ieee80211vap *, + struct ieee80211_key *); + /* * 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, +null_key_alloc(struct ieee80211vap *vap, 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])) { + if (!(&vap->iv_nw_keys[0] <= k && + k < &vap->iv_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 @@ -72,23 +78,23 @@ null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k, return 0; *keyix = 0; /* NB: use key index 0 for ucast key */ } else { - *keyix = k - ic->ic_nw_keys; + *keyix = k - vap->iv_nw_keys; } *rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */ return 1; } static int -null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +null_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { return 1; } static int -null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, +null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, const uint8_t mac[IEEE80211_ADDR_LEN]) { return 1; } -static void null_key_update(struct ieee80211com *ic) {} +static void null_key_update(struct ieee80211vap *vap) {} /* * Write-arounds for common operations. @@ -100,70 +106,86 @@ cipher_detach(struct ieee80211_key *key) } static __inline void * -cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key) +cipher_attach(struct ieee80211vap *vap, struct ieee80211_key *key) { - return key->wk_cipher->ic_attach(ic, key); + return key->wk_cipher->ic_attach(vap, key); } /* * Wrappers for driver key management methods. */ static __inline int -dev_key_alloc(struct ieee80211com *ic, +dev_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *key, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { - return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix); + return vap->iv_key_alloc(vap, key, keyix, rxkeyix); } static __inline int -dev_key_delete(struct ieee80211com *ic, +dev_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *key) { - return ic->ic_crypto.cs_key_delete(ic, key); + return vap->iv_key_delete(vap, key); } static __inline int -dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key, +dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key, const uint8_t mac[IEEE80211_ADDR_LEN]) { - return ic->ic_crypto.cs_key_set(ic, key, mac); + return vap->iv_key_set(vap, key, mac); } /* - * Setup crypto support. + * Setup crypto support for a device/shared instance. */ void ieee80211_crypto_attach(struct ieee80211com *ic) { - struct ieee80211_crypto_state *cs = &ic->ic_crypto; + /* NB: we assume everything is pre-zero'd */ + ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none; +} + +/* + * Teardown crypto support. + */ +void +ieee80211_crypto_detach(struct ieee80211com *ic) +{ +} + +/* + * Setup crypto support for a vap. + */ +void +ieee80211_crypto_vattach(struct ieee80211vap *vap) +{ 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; + vap->iv_max_keyix = IEEE80211_WEP_NKID; + vap->iv_def_txkey = IEEE80211_KEYIX_NONE; for (i = 0; i < IEEE80211_WEP_NKID; i++) - ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i], + ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i], IEEE80211_KEYIX_NONE); /* * Initialize the driver key support routines to noop entries. * This is useful especially for the cipher test modules. */ - 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; + vap->iv_key_alloc = null_key_alloc; + vap->iv_key_set = null_key_set; + vap->iv_key_delete = null_key_delete; + vap->iv_key_update_begin = null_key_update; + vap->iv_key_update_end = null_key_update; } /* - * Teardown crypto support. + * Teardown crypto support for a vap. */ void -ieee80211_crypto_detach(struct ieee80211com *ic) +ieee80211_crypto_vdetach(struct ieee80211vap *vap) { - ieee80211_crypto_delglobalkeys(ic); + ieee80211_crypto_delglobalkeys(vap); } /* @@ -213,12 +235,14 @@ ieee80211_crypto_available(u_int cipher) } /* XXX well-known names! */ -static const char *cipher_modnames[] = { +static const char *cipher_modnames[IEEE80211_CIPHER_MAX] = { "wlan_wep", /* IEEE80211_CIPHER_WEP */ "wlan_tkip", /* IEEE80211_CIPHER_TKIP */ "wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */ "wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */ + "#4", /* reserved */ "wlan_ckip", /* IEEE80211_CIPHER_CKIP */ + "wlan_none", /* IEEE80211_CIPHER_NONE */ }; /* @@ -231,14 +255,14 @@ static const char *cipher_modnames[] = { * routines assume wk_cipher is setup. * * Locking must be handled by the caller using: - * ieee80211_key_update_begin(ic); - * ieee80211_key_update_end(ic); + * ieee80211_key_update_begin(vap); + * ieee80211_key_update_end(vap); */ int -ieee80211_crypto_newkey(struct ieee80211com *ic, +ieee80211_crypto_newkey(struct ieee80211vap *vap, int cipher, int flags, struct ieee80211_key *key) { -#define N(a) (sizeof(a) / sizeof(a[0])) + struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_cipher *cip; ieee80211_keyix keyix, rxkeyix; void *keyctx; @@ -248,9 +272,9 @@ ieee80211_crypto_newkey(struct ieee80211com *ic, * 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++; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, + "%s: invalid cipher %u\n", __func__, cipher); + vap->iv_stats.is_crypto_badcipher++; return 0; } cip = ciphers[cipher]; @@ -261,25 +285,21 @@ ieee80211_crypto_newkey(struct ieee80211com *ic, * than numbers and craft a module name based on the cipher * name; e.g. wlan_cipher_<cipher-name>. */ - 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]; - } + IEEE80211_DPRINTF(vap, 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]; 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] : "<unknown>"); - ic->ic_stats.is_crypto_nocipher++; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, + "%s: unable to load cipher %u, module %s\n", + __func__, cipher, cipher_modnames[cipher]); + vap->iv_stats.is_crypto_nocipher++; return 0; } } @@ -290,8 +310,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic, * If the hardware does not support the cipher then * fallback to a host-based implementation. */ - if ((ic->ic_caps & (1<<cipher)) == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + if ((ic->ic_cryptocaps & (1<<cipher)) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: no h/w support for cipher %s, falling back to s/w\n", __func__, cip->ic_name); flags |= IEEE80211_KEY_SWCRYPT; @@ -302,8 +322,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic, * the cipher modules honor it. */ if (cipher == IEEE80211_CIPHER_TKIP && - (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + (ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIPMIC) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: no h/w support for TKIP MIC, falling back to s/w\n", __func__); flags |= IEEE80211_KEY_SWMIC; @@ -327,13 +347,13 @@ again: * fails and we try to restore previous state. */ key->wk_flags = flags; - keyctx = cip->ic_attach(ic, key); + keyctx = cip->ic_attach(vap, key); if (keyctx == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + IEEE80211_DPRINTF(vap, 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++; + vap->iv_stats.is_crypto_attachfail++; return 0; } cipher_detach(key); @@ -354,7 +374,7 @@ again: * 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)) { + if (!dev_key_alloc(vap, key, &keyix, &rxkeyix)) { /* * Driver has no room; fallback to doing crypto * in the host. We change the flags and start the @@ -364,8 +384,8 @@ again: * continues to use it. */ if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { - ic->ic_stats.is_crypto_swfallback++; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + vap->iv_stats.is_crypto_swfallback++; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: no h/w resources for cipher %s, " "falling back to s/w\n", __func__, cip->ic_name); @@ -375,8 +395,8 @@ again: flags |= IEEE80211_KEY_SWMIC; goto again; } - ic->ic_stats.is_crypto_keyfail++; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + vap->iv_stats.is_crypto_keyfail++; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: unable to setup cipher %s\n", __func__, cip->ic_name); return 0; @@ -385,24 +405,24 @@ again: 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_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key) { ieee80211_keyix keyix; KASSERT(key->wk_cipher != NULL, ("No cipher!")); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + IEEE80211_DPRINTF(vap, 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); + key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc, + key->wk_keylen); keyix = key->wk_keyix; if (keyix != IEEE80211_KEYIX_NONE) { @@ -410,17 +430,17 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) * Remove hardware entry. */ /* XXX key cache */ - if (!dev_key_delete(ic, key)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + if (!dev_key_delete(vap, key)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: driver did not delete key index %u\n", __func__, keyix); - ic->ic_stats.is_crypto_delkey++; + vap->iv_stats.is_crypto_delkey++; /* XXX recovery? */ } } cipher_detach(key); memset(key, 0, sizeof(*key)); - ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE); + ieee80211_crypto_resetkey(vap, key, IEEE80211_KEYIX_NONE); return 1; } @@ -428,13 +448,13 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) * Remove the specified key. */ int -ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key) { int status; - ieee80211_key_update_begin(ic); - status = _ieee80211_crypto_delkey(ic, key); - ieee80211_key_update_end(ic); + ieee80211_key_update_begin(vap); + status = _ieee80211_crypto_delkey(vap, key); + ieee80211_key_update_end(vap); return status; } @@ -442,66 +462,67 @@ ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) * Clear the global key table. */ void -ieee80211_crypto_delglobalkeys(struct ieee80211com *ic) +ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap) { int i; - ieee80211_key_update_begin(ic); + ieee80211_key_update_begin(vap); for (i = 0; i < IEEE80211_WEP_NKID; i++) - (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]); - ieee80211_key_update_end(ic); + (void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]); + ieee80211_key_update_end(vap); } /* * 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); + * ieee80211_key_update_begin(vap); + * ieee80211_key_update_end(vap); */ int -ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key, +ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key, const uint8_t macaddr[IEEE80211_ADDR_LEN]) { const struct ieee80211_cipher *cip = key->wk_cipher; KASSERT(cip != NULL, ("No cipher!")); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: %s keyix %u flags 0x%x mac %s rsc %ju tsc %ju len %u\n", __func__, cip->ic_name, key->wk_keyix, key->wk_flags, ether_sprintf(macaddr), - key->wk_keyrsc, key->wk_keytsc, key->wk_keylen); + key->wk_keyrsc[IEEE80211_NONQOS_TID], 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, + IEEE80211_DPRINTF(vap, 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++; + vap->iv_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, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: no key index; should not happen!\n", __func__); - ic->ic_stats.is_crypto_setkey_nokey++; + vap->iv_stats.is_crypto_setkey_nokey++; return 0; } - return dev_key_set(ic, key, macaddr); + return dev_key_set(vap, key, macaddr); } /* * Add privacy headers appropriate for the specified key. */ struct ieee80211_key * -ieee80211_crypto_encap(struct ieee80211com *ic, - struct ieee80211_node *ni, struct mbuf *m) +ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_key *k; struct ieee80211_frame *wh; const struct ieee80211_cipher *cip; @@ -516,16 +537,16 @@ ieee80211_crypto_encap(struct ieee80211com *ic, wh = mtod(m, struct ieee80211_frame *); if (IEEE80211_IS_MULTICAST(wh->i_addr1) || IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { - if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] no default transmit key (%s) deftxkey %u\n", - ether_sprintf(wh->i_addr1), __func__, - ic->ic_def_txkey); - ic->ic_stats.is_tx_nodefkey++; + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, + wh->i_addr1, + "no default transmit key (%s) deftxkey %u", + __func__, vap->iv_def_txkey); + vap->iv_stats.is_tx_nodefkey++; return NULL; } - keyid = ic->ic_def_txkey; - k = &ic->ic_nw_keys[ic->ic_def_txkey]; + keyid = vap->iv_def_txkey; + k = &vap->iv_nw_keys[vap->iv_def_txkey]; } else { keyid = 0; k = &ni->ni_ucastkey; @@ -539,25 +560,24 @@ ieee80211_crypto_encap(struct ieee80211com *ic, * 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) +ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen) { #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 ieee80211vap *vap = ni->ni_vap; 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, + IEEE80211_DPRINTF(vap, 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? */ + vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */ return NULL; } @@ -568,11 +588,10 @@ ieee80211_crypto_decap(struct ieee80211com *ic, * 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]; + m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid); if (IEEE80211_IS_MULTICAST(wh->i_addr1) || IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) - k = &ic->ic_nw_keys[keyid >> 6]; + k = &vap->iv_nw_keys[keyid >> 6]; else k = &ni->ni_ucastkey; @@ -582,10 +601,9 @@ ieee80211_crypto_decap(struct ieee80211com *ic, 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, - "[%s] unable to pullup %s header\n", - ether_sprintf(wh->i_addr2), cip->ic_name); - ic->ic_stats.is_rx_wepfail++; /* XXX */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "unable to pullup %s header", cip->ic_name); + vap->iv_stats.is_rx_wepfail++; /* XXX */ return NULL; } diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h index ee99caa..999f139 100644 --- a/sys/net80211/ieee80211_crypto.h +++ b/sys/net80211/ieee80211_crypto.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,15 @@ struct ieee80211_wepkey { uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; }; +struct ieee80211_rsnparms { + uint8_t rsn_mcastcipher; /* mcast/group cipher */ + uint8_t rsn_mcastkeylen; /* mcast key length */ + uint8_t rsn_ucastcipher; /* selected unicast cipher */ + uint8_t rsn_ucastkeylen; /* unicast key length */ + uint8_t rsn_keymgmt; /* selected key mgmt algo */ + uint16_t rsn_caps; /* capabilities */ +}; + struct ieee80211_cipher; /* @@ -76,7 +85,8 @@ struct ieee80211_key { 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 */ + /* key receive sequence counter */ + uint64_t wk_keyrsc[IEEE80211_TID_SIZE]; uint64_t wk_keytsc; /* key transmit sequence counter */ const struct ieee80211_cipher *wk_cipher; void *wk_private; /* private cipher state */ @@ -84,61 +94,54 @@ struct ieee80211_key { #define IEEE80211_KEY_COMMON /* common flags passed in by apps */\ (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP) +#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1) + /* * 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. + * of implications in any reordering. Beware that 4 is used + * only to indicate h/w TKIP MIC support in driver capabilities; + * there is no separate cipher support (it's rolled into the + * TKIP cipher support). */ #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_TKIPMIC 4 /* TKIP MIC capability */ #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) +/* capability bits in ic_cryptocaps/iv_cryptocaps */ +#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP) +#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP) +#define IEEE80211_CRYPTO_AES_OCB (1<<IEEE80211_CIPHER_AES_OCB) +#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM) +#define IEEE80211_CRYPTO_TKIPMIC (1<<IEEE80211_CIPHER_TKIPMIC) +#define IEEE80211_CRYPTO_CKIP (1<<IEEE80211_CIPHER_CKIP) #if defined(__KERNEL__) || defined(_KERNEL) struct ieee80211com; +struct ieee80211vap; 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 *); -}; +MALLOC_DECLARE(M_80211_CRYPTO); void ieee80211_crypto_attach(struct ieee80211com *); void ieee80211_crypto_detach(struct ieee80211com *); -int ieee80211_crypto_newkey(struct ieee80211com *, +void ieee80211_crypto_vattach(struct ieee80211vap *); +void ieee80211_crypto_vdetach(struct ieee80211vap *); +int ieee80211_crypto_newkey(struct ieee80211vap *, int cipher, int flags, struct ieee80211_key *); -int ieee80211_crypto_delkey(struct ieee80211com *, +int ieee80211_crypto_delkey(struct ieee80211vap *, 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 *); +int ieee80211_crypto_setkey(struct ieee80211vap *, + struct ieee80211_key *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +void ieee80211_crypto_delglobalkeys(struct ieee80211vap *); /* * Template for a supported cipher. Ciphers register with the @@ -152,7 +155,7 @@ struct 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_attach)(struct ieee80211vap *, struct ieee80211_key *); void (*ic_detach)(struct ieee80211_key *); int (*ic_setkey)(struct ieee80211_key *); int (*ic_encap)(struct ieee80211_key *, struct mbuf *, @@ -170,16 +173,16 @@ 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); +struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *, + struct mbuf *); +struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *, + struct mbuf *, int); /* * Check and remove any MIC. */ static __inline int -ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k, +ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k, struct mbuf *m, int force) { const struct ieee80211_cipher *cip = k->wk_cipher; @@ -190,7 +193,7 @@ ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k, * Add any MIC. */ static __inline int -ieee80211_crypto_enmic(struct ieee80211com *ic, +ieee80211_crypto_enmic(struct ieee80211vap *vap, struct ieee80211_key *k, struct mbuf *m, int force) { const struct ieee80211_cipher *cip = k->wk_cipher; @@ -203,11 +206,11 @@ ieee80211_crypto_enmic(struct ieee80211com *ic, * key data) is properly setup before a key is used. */ static __inline void -ieee80211_crypto_resetkey(struct ieee80211com *ic, +ieee80211_crypto_resetkey(struct ieee80211vap *vap, 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_private = k->wk_cipher->ic_attach(vap, k); k->wk_keyix = k->wk_rxkeyix = ix; k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; } @@ -215,10 +218,10 @@ ieee80211_crypto_resetkey(struct ieee80211com *ic, /* * Crypt-related notification methods. */ -void ieee80211_notify_replay_failure(struct ieee80211com *, +void ieee80211_notify_replay_failure(struct ieee80211vap *, const struct ieee80211_frame *, const struct ieee80211_key *, - u_int64_t rsc); -void ieee80211_notify_michael_failure(struct ieee80211com *, + uint64_t rsc); +void ieee80211_notify_michael_failure(struct ieee80211vap *, const struct ieee80211_frame *, u_int keyix); #endif /* defined(__KERNEL__) || defined(_KERNEL) */ #endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c index 525fe9a..d3b1af5 100644 --- a/sys/net80211/ieee80211_crypto_ccmp.c +++ b/sys/net80211/ieee80211_crypto_ccmp.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$"); * AP driver. The code is used with the consent of the author and * it's license is included below. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -53,11 +55,12 @@ __FBSDID("$FreeBSD$"); #define AES_BLOCK_LEN 16 struct ccmp_ctx { - struct ieee80211com *cc_ic; /* for diagnostics */ + struct ieee80211vap *cc_vap; /* for diagnostics+statistics */ + struct ieee80211com *cc_ic; rijndael_ctx cc_aes; }; -static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *); +static void *ccmp_attach(struct ieee80211vap *, 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); @@ -89,17 +92,18 @@ static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn, static int nrefs = 0; static void * -ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k) +ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ccmp_ctx *ctx; MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx), - M_DEVBUF, M_NOWAIT | M_ZERO); + M_80211_CRYPTO, M_NOWAIT | M_ZERO); if (ctx == NULL) { - ic->ic_stats.is_crypto_nomem++; + vap->iv_stats.is_crypto_nomem++; return NULL; } - ctx->cc_ic = ic; + ctx->cc_vap = vap; + ctx->cc_ic = vap->iv_ic; nrefs++; /* NB: we assume caller locking */ return ctx; } @@ -109,7 +113,7 @@ ccmp_detach(struct ieee80211_key *k) { struct ccmp_ctx *ctx = k->wk_private; - FREE(ctx, M_DEVBUF); + FREE(ctx, M_80211_CRYPTO); KASSERT(nrefs > 0, ("imbalanced attach/detach")); nrefs--; /* NB: we assume caller locking */ } @@ -120,7 +124,7 @@ 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, + IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, "%s: Invalid key length %u, expecting %u\n", __func__, k->wk_keylen, 128/NBBY); return 0; @@ -200,8 +204,9 @@ static int ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) { struct ccmp_ctx *ctx = k->wk_private; + struct ieee80211vap *vap = ctx->cc_vap; struct ieee80211_frame *wh; - uint8_t *ivp; + uint8_t *ivp, tid; uint64_t pn; /* @@ -214,19 +219,19 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) /* * No extended IV; discard frame. */ - IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, - "[%s] Missing ExtIV for AES-CCM cipher\n", - ether_sprintf(wh->i_addr2)); - ctx->cc_ic->ic_stats.is_rx_ccmpformat++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s", "missing ExtIV for AES-CCM cipher"); + vap->iv_stats.is_rx_ccmpformat++; return 0; } + tid = ieee80211_gettid(wh); pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); - if (pn <= k->wk_keyrsc) { + if (pn <= k->wk_keyrsc[tid]) { /* * Replay violation. */ - ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn); - ctx->cc_ic->ic_stats.is_rx_ccmpreplay++; + ieee80211_notify_replay_failure(vap, wh, k, pn); + vap->iv_stats.is_rx_ccmpreplay++; return 0; } @@ -251,7 +256,7 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) /* * Ok to update rsc now. */ - k->wk_keyrsc = pn; + k->wk_keyrsc[tid] = pn; return 1; } @@ -406,7 +411,7 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN]; uint8_t *pos; - ctx->cc_ic->ic_stats.is_crypto_ccmp++; + ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header); @@ -544,6 +549,7 @@ static int ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen) { struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211vap *vap = ctx->cc_vap; 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]; @@ -553,7 +559,7 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen uint8_t *pos; u_int space; - ctx->cc_ic->ic_stats.is_crypto_ccmp++; + ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer); @@ -616,10 +622,9 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen } } if (memcmp(mic, a, ccmp.ic_trailer) != 0) { - IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, - "[%s] AES-CCM decrypt failed; MIC mismatch\n", - ether_sprintf(wh->i_addr2)); - ctx->cc_ic->ic_stats.is_rx_ccmpmic++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s", "AES-CCM decrypt failed; MIC mismatch"); + vap->iv_stats.is_rx_ccmpmic++; return 0; } return 1; diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c index 7fbb53d..b1ffbb4 100644 --- a/sys/net80211/ieee80211_crypto_none.c +++ b/sys/net80211/ieee80211_crypto_none.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,10 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 NULL crypto support. */ +#include "opt_wlan.h" + #include <sys/param.h> +#include <sys/kernel.h> #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/module.h> @@ -42,7 +45,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> -static void *none_attach(struct ieee80211com *, struct ieee80211_key *); +static void *none_attach(struct ieee80211vap *, 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); @@ -66,9 +69,9 @@ const struct ieee80211_cipher ieee80211_cipher_none = { }; static void * -none_attach(struct ieee80211com *ic, struct ieee80211_key *k) +none_attach(struct ieee80211vap *vap, struct ieee80211_key *k) { - return ic; /* for diagnostics+stats */ + return vap; /* for diagnostics+stats */ } static void @@ -87,7 +90,7 @@ none_setkey(struct ieee80211_key *k) static int none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) { - struct ieee80211com *ic = k->wk_private; + struct ieee80211vap *vap = k->wk_private; #ifdef IEEE80211_DEBUG struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); #endif @@ -96,17 +99,16 @@ none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) * The specified key is not setup; this can * happen, at least, when changing keys. */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] key id %u is not set (encap)\n", - ether_sprintf(wh->i_addr1), keyid>>6); - ic->ic_stats.is_tx_badcipher++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1, + "key id %u is not set (encap)", keyid>>6); + vap->iv_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; + struct ieee80211vap *vap = k->wk_private; #ifdef IEEE80211_DEBUG struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); const uint8_t *ivp = (const uint8_t *)&wh[1]; @@ -117,27 +119,26 @@ none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) * happen, at least, when changing keys. */ /* XXX useful to know dst too */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] key id %u is not set (decap)\n", - ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6); - ic->ic_stats.is_rx_badkeyid++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "key id %u is not set (decap)", ivp[IEEE80211_WEP_IVLEN] >> 6); + vap->iv_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; + struct ieee80211vap *vap = k->wk_private; - ic->ic_stats.is_tx_badcipher++; + vap->iv_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; + struct ieee80211vap *vap = k->wk_private; - ic->ic_stats.is_rx_badkeyid++; + vap->iv_stats.is_rx_badkeyid++; return 0; } diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c index 327306a..398246f 100644 --- a/sys/net80211/ieee80211_crypto_tkip.c +++ b/sys/net80211/ieee80211_crypto_tkip.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$"); * AP driver. The code is used with the consent of the author and * it's license is included below. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -49,7 +51,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> -static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *); +static void *tkip_attach(struct ieee80211vap *, 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); @@ -77,10 +79,9 @@ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t __u32; typedef uint32_t u32; -#define memmove(dst, src, n) ovbcopy(src, dst, n) struct tkip_ctx { - struct ieee80211com *tc_ic; /* for diagnostics */ + struct ieee80211vap *tc_vap; /* for diagnostics+statistics */ u16 tx_ttak[5]; int tx_phase1_done; @@ -104,18 +105,18 @@ static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *, static int nrefs = 0; static void * -tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k) +tkip_attach(struct ieee80211vap *vap, struct ieee80211_key *k) { struct tkip_ctx *ctx; MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx), - M_DEVBUF, M_NOWAIT | M_ZERO); + M_80211_CRYPTO, M_NOWAIT | M_ZERO); if (ctx == NULL) { - ic->ic_stats.is_crypto_nomem++; + vap->iv_stats.is_crypto_nomem++; return NULL; } - ctx->tc_ic = ic; + ctx->tc_vap = vap; nrefs++; /* NB: we assume caller locking */ return ctx; } @@ -125,7 +126,7 @@ tkip_detach(struct ieee80211_key *k) { struct tkip_ctx *ctx = k->wk_private; - FREE(ctx, M_DEVBUF); + FREE(ctx, M_80211_CRYPTO); KASSERT(nrefs > 0, ("imbalanced attach/detach")); nrefs--; /* NB: we assume caller locking */ } @@ -137,7 +138,7 @@ tkip_setkey(struct ieee80211_key *k) if (k->wk_keylen != (128/NBBY)) { (void) ctx; /* XXX */ - IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + IEEE80211_DPRINTF(ctx->tc_vap, IEEE80211_MSG_CRYPTO, "%s: Invalid key length %u, expecting %u\n", __func__, k->wk_keylen, 128/NBBY); return 0; @@ -153,22 +154,22 @@ 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; + struct ieee80211vap *vap = ctx->tc_vap; + struct ieee80211com *ic = vap->iv_ic; uint8_t *ivp; int hdrlen; /* * Handle TKIP counter measures requirement. */ - if (ic->ic_flags & IEEE80211_F_COUNTERM) { + if (vap->iv_flags & IEEE80211_F_COUNTERM) { #ifdef IEEE80211_DEBUG struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); #endif - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] Discard frame due to countermeasures (%s)\n", - ether_sprintf(wh->i_addr2), __func__); - ic->ic_stats.is_crypto_tkipcm++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "discard frame due to countermeasures (%s)", __func__); + vap->iv_stats.is_crypto_tkipcm++; return 0; } hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); @@ -215,11 +216,12 @@ tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force) if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) { struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); - struct ieee80211com *ic = ctx->tc_ic; + struct ieee80211vap *vap = ctx->tc_vap; + struct ieee80211com *ic = vap->iv_ic; int hdrlen; uint8_t mic[IEEE80211_WEP_MICLEN]; - ic->ic_stats.is_crypto_tkipenmic++; + vap->iv_stats.is_crypto_tkipenmic++; hdrlen = ieee80211_hdrspace(ic, wh); @@ -247,9 +249,9 @@ 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 ieee80211vap *vap = ctx->tc_vap; struct ieee80211_frame *wh; - uint8_t *ivp; + uint8_t *ivp, tid; /* * Header should have extended IV and sequence number; @@ -261,30 +263,29 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) /* * No extended IV; discard frame. */ - IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, - "[%s] missing ExtIV for TKIP cipher\n", - ether_sprintf(wh->i_addr2)); - ctx->tc_ic->ic_stats.is_rx_tkipformat++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s", "missing ExtIV for TKIP cipher"); + vap->iv_stats.is_rx_tkipformat++; return 0; } /* * Handle TKIP counter measures requirement. */ - if (ic->ic_flags & IEEE80211_F_COUNTERM) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] discard frame due to countermeasures (%s)\n", - ether_sprintf(wh->i_addr2), __func__); - ic->ic_stats.is_crypto_tkipcm++; + if (vap->iv_flags & IEEE80211_F_COUNTERM) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "discard frame due to countermeasures (%s)", __func__); + vap->iv_stats.is_crypto_tkipcm++; return 0; } + tid = ieee80211_gettid(wh); ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]); - if (ctx->rx_rsc <= k->wk_keyrsc) { + if (ctx->rx_rsc <= k->wk_keyrsc[tid]) { /* * 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++; + ieee80211_notify_replay_failure(vap, wh, k, ctx->rx_rsc); + vap->iv_stats.is_rx_tkipreplay++; return 0; } /* @@ -322,15 +323,17 @@ static int tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force) { struct tkip_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + uint8_t tid; + wh = mtod(m, struct ieee80211_frame *); 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); + struct ieee80211vap *vap = ctx->tc_vap; + int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh); u8 mic[IEEE80211_WEP_MICLEN]; u8 mic0[IEEE80211_WEP_MICLEN]; - ic->ic_stats.is_crypto_tkipdemic++; + vap->iv_stats.is_crypto_tkipdemic++; michael_mic(ctx, k->wk_rxmic, m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen), @@ -339,7 +342,7 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force) 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, + ieee80211_notify_michael_failure(vap, wh, k->wk_rxkeyix != IEEE80211_KEYIX_NONE ? k->wk_rxkeyix : k->wk_keyix); return 0; @@ -353,7 +356,8 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force) /* * Ok to update rsc now that MIC has been verified. */ - k->wk_keyrsc = ctx->rx_rsc; + tid = ieee80211_gettid(wh); + k->wk_keyrsc[tid] = ctx->rx_rsc; return 1; } @@ -904,7 +908,7 @@ tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, struct ieee80211_frame *wh; uint8_t icv[IEEE80211_WEP_CRCLEN]; - ctx->tc_ic->ic_stats.is_crypto_tkip++; + ctx->tc_vap->iv_stats.is_crypto_tkip++; wh = mtod(m, struct ieee80211_frame *); if (!ctx->tx_phase1_done) { @@ -932,17 +936,20 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, struct mbuf *m, int hdrlen) { struct ieee80211_frame *wh; + struct ieee80211vap *vap = ctx->tc_vap; u32 iv32; u16 iv16; + u8 tid; - ctx->tc_ic->ic_stats.is_crypto_tkip++; + vap->iv_stats.is_crypto_tkip++; wh = mtod(m, struct ieee80211_frame *); /* NB: tkip_decap already verified header and left seq in rx_rsc */ iv16 = (u16) ctx->rx_rsc; iv32 = (u32) (ctx->rx_rsc >> 16); - if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) { + tid = ieee80211_gettid(wh); + if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16) || !ctx->rx_phase1_done) { tkip_mixing_phase1(ctx->rx_ttak, key->wk_key, wh->i_addr2, iv32); ctx->rx_phase1_done = 1; @@ -953,15 +960,14 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, if (wep_decrypt(ctx->rx_rc4key, m, hdrlen + tkip.ic_header, m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) { - if (iv32 != (u32)(key->wk_keyrsc >> 16)) { + if (iv32 != (u32)(key->wk_keyrsc[tid] >> 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, - "[%s] TKIP ICV mismatch on decrypt\n", - ether_sprintf(wh->i_addr2)); - ctx->tc_ic->ic_stats.is_rx_tkipicv++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s", "TKIP ICV mismatch on decrypt"); + vap->iv_stats.is_rx_tkipicv++; return 0; } return 1; diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c index 81d15cc..df988a6 100644 --- a/sys/net80211/ieee80211_crypto_wep.c +++ b/sys/net80211/ieee80211_crypto_wep.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 WEP crypto support. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -45,7 +47,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> -static void *wep_attach(struct ieee80211com *, struct ieee80211_key *); +static void *wep_attach(struct ieee80211vap *, 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); @@ -72,7 +74,8 @@ 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 */ + struct ieee80211vap *wc_vap; /* for diagnostics+statistics */ + struct ieee80211com *wc_ic; uint32_t wc_iv; /* initial vector for crypto */ }; @@ -80,18 +83,19 @@ struct wep_ctx { static int nrefs = 0; static void * -wep_attach(struct ieee80211com *ic, struct ieee80211_key *k) +wep_attach(struct ieee80211vap *vap, struct ieee80211_key *k) { struct wep_ctx *ctx; MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx), - M_DEVBUF, M_NOWAIT | M_ZERO); + M_80211_CRYPTO, M_NOWAIT | M_ZERO); if (ctx == NULL) { - ic->ic_stats.is_crypto_nomem++; + vap->iv_stats.is_crypto_nomem++; return NULL; } - ctx->wc_ic = ic; + ctx->wc_vap = vap; + ctx->wc_ic = vap->iv_ic; get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); nrefs++; /* NB: we assume caller locking */ return ctx; @@ -102,7 +106,7 @@ wep_detach(struct ieee80211_key *k) { struct wep_ctx *ctx = k->wk_private; - FREE(ctx, M_DEVBUF); + FREE(ctx, M_80211_CRYPTO); KASSERT(nrefs > 0, ("imbalanced attach/detach")); nrefs--; /* NB: we assume caller locking */ } @@ -208,6 +212,7 @@ static int wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) { struct wep_ctx *ctx = k->wk_private; + struct ieee80211vap *vap = ctx->wc_vap; struct ieee80211_frame *wh; wh = mtod(m, struct ieee80211_frame *); @@ -219,10 +224,9 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) */ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && !wep_decrypt(k, m, hdrlen)) { - IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, - "[%s] WEP ICV mismatch on decrypt\n", - ether_sprintf(wh->i_addr2)); - ctx->wc_ic->ic_stats.is_rx_wepfail++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s", "WEP ICV mismatch on decrypt"); + vap->iv_stats.is_rx_wepfail++; return 0; } @@ -305,6 +309,7 @@ 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 ieee80211vap *vap = ctx->wc_vap; struct mbuf *m = m0; uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; uint8_t icv[IEEE80211_WEP_CRCLEN]; @@ -314,7 +319,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) uint8_t *pos; u_int off, keylen; - ctx->wc_ic->ic_stats.is_crypto_wep++; + vap->iv_stats.is_crypto_wep++; /* NB: this assumes the header was pulled up */ memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); @@ -351,12 +356,12 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) } if (m->m_next == 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", + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, ether_sprintf(mtod(m0, struct ieee80211_frame *)->i_addr2), + "out of data for WEP (data_len %zu)", data_len); + /* XXX stat */ return 0; } break; @@ -387,6 +392,7 @@ 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 ieee80211vap *vap = ctx->wc_vap; struct mbuf *m = m0; uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; uint8_t icv[IEEE80211_WEP_CRCLEN]; @@ -396,7 +402,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) uint8_t *pos; u_int off, keylen; - ctx->wc_ic->ic_stats.is_crypto_wep++; + vap->iv_stats.is_crypto_wep++; /* NB: this assumes the header was pulled up */ memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); @@ -435,11 +441,9 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 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", - ether_sprintf(mtod(m0, - struct ieee80211_frame *)->i_addr2), + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, + mtod(m0, struct ieee80211_frame *)->i_addr2, + "out of data for WEP (data_len %zu)", data_len); return 0; } diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c new file mode 100644 index 0000000..5b78283 --- /dev/null +++ b/sys/net80211/ieee80211_ddb.c @@ -0,0 +1,789 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_ddb.h" +#include "opt_wlan.h" + +#ifdef DDB +/* + * IEEE 802.11 DDB support + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +#include <ddb/ddb.h> + +#define IEEE80211_MSG_BITS \ + "\20\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \ + "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1X\22POWER" \ + "\23STATE\24OUTPUT\25SCAN\26AUTH\27ASSOC\30NODE\31ELEMID\32XRATE" \ + "\33INPUT\34CRYPTO\35DUPMPKTS\36DEBUG\3711N" + +#define IEEE80211_F_BITS \ + "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN\11ASCAN\12SIBSS" \ + "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY\21TXPOW_FIXED" \ + "\22IBSSON\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \ + "\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \ + "\37DOTH\40DWDS" + +#define IEEE80211_FEXT_BITS \ + "\20\1NONHT_PR\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\12NONEPR_PR"\ + "\13SWBMISS\14DFS\15DOTD\22WDSLEGACY\23PROBECHAN\24HT\25AMDPU_TX" \ + "\26AMPDU_TX\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20" \ + "\34SHORTGI40\35HTCOMPAT" + +#define IEEE80211_FVEN_BITS "\20" + +#define IEEE80211_C_BITS \ + "\20\7FF\10TURBOP\11IBSS\12PMGT" \ + "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \ + "\21MONITOR\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \ + "\37TXFRAG" + +#define IEEE80211_C_CRYPTO_BITS \ + "\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT" + +#define IEEE80211_C_HTCAP_BITS \ + "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \ + "\21AMPDU\22AMSDU\23HT" + +/* NB: policy bits not included */ +#define IEEE80211_CHAN_BITS \ + "\20\5TURBO\6CCK\7OFDM\0102GHZ\0115GHZ\12PASSIVE\13DYN\14GFSK" \ + "\15STURBO\16HALF\17QUARTER\20HT20\21HT40U\22HT40D\23DFS" + +#define IEEE80211_NODE_BITS \ + "\20\1AUTH\2QOS\3ERP\5PWR_MGT\6AREF\7HT\10HTCOMPAT\11WPS\12TSN" \ + "\13AMPDU_RX\14AMPDU_TX" + +#define IEEE80211_ERP_BITS \ + "\20\1NON_ERP_PRESENT\2USE_PROTECTION\3LONG_PREAMBLE" + +#define IEEE80211_CAPINFO_BITS \ + "\20\1ESS\2IBSS\3CF_POLLABLE\4CF_POLLREQ\5PRIVACY\6SHORT_PREAMBLE" \ + "\7PBCC\10CHNL_AGILITY\11SPECTRUM_MGMT\13SHORT_SLOTTIME\14RSN" \ + "\16DSSOFDM" + +#define IEEE80211_HTCAP_BITS \ + "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \ + "\13DELBA\14AMSDU(7935)\15DSSSCCK40\16PSMP\1740INTOLERANT" \ + "\20LSIGTXOPPROT" + +#define IEEE80211_AGGR_BITS \ + "\20\1IMMEDIATE\2XCHGPEND\3RUNNING\4SETUP\5NAK" + +static void _db_show_sta(const struct ieee80211_node *); +static void _db_show_vap(const struct ieee80211vap *, int); +static void _db_show_com(const struct ieee80211com *, + int showvaps, int showsta, int showprocs); + +static void _db_show_channel(const char *tag, const struct ieee80211_channel *); +static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *); +static void _db_show_appie(const char *tag, const struct ieee80211_appie *); +static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *); +static void _db_show_roamparams(const char *tag, const void *arg, + const struct ieee80211_roamparam *rp); +static void _db_show_txparams(const char *tag, const void *arg, + const struct ieee80211_txparam *tp); +static void _db_show_stats(const struct ieee80211_stats *); + +DB_SHOW_COMMAND(sta, db_show_sta) +{ + if (!have_addr) { + db_printf("usage: show sta <addr>\n"); + return; + } + _db_show_sta((const struct ieee80211_node *) addr); +} + +DB_SHOW_COMMAND(vap, db_show_vap) +{ + int i, showprocs = 0; + + if (!have_addr) { + db_printf("usage: show vap <addr>\n"); + return; + } + for (i = 0; modif[i] != '\0'; i++) + switch (modif[i]) { + case 'a': + showprocs = 1; + break; + case 'p': + showprocs = 1; + break; + } + _db_show_vap((const struct ieee80211vap *) addr, showprocs); +} + +DB_SHOW_COMMAND(com, db_show_com) +{ + const struct ieee80211com *ic; + int i, showprocs = 0, showvaps = 0, showsta = 0; + + if (!have_addr) { + db_printf("usage: show com <addr>\n"); + return; + } + for (i = 0; modif[i] != '\0'; i++) + switch (modif[i]) { + case 'a': + showsta = showvaps = showprocs = 1; + break; + case 's': + showsta = 1; + break; + case 'v': + showvaps = 1; + break; + case 'p': + showprocs = 1; + break; + } + + ic = (const struct ieee80211com *) addr; + _db_show_com(ic, showvaps, showsta, showprocs); +} + +DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps) +{ + const struct ifnet *ifp; + int i, showall = 0; + + for (i = 0; modif[i] != '\0'; i++) + switch (modif[i]) { + case 'a': + showall = 1; + break; + } + + TAILQ_FOREACH(ifp, &ifnet, if_list) + if (ifp->if_type == IFT_IEEE80211) { + const struct ieee80211com *ic = ifp->if_l2com; + + if (!showall) { + const struct ieee80211vap *vap; + db_printf("%s: com %p vaps:", + ifp->if_xname, ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + db_printf(" %s(%p)", + vap->iv_ifp->if_xname, vap); + db_printf("\n"); + } else + _db_show_com(ic, 1, 1, 1); + } +} + +static void +_db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap) +{ + db_printf("%stxampdu[%d]: %p flags %b ac %u\n", + sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS, tap->txa_ac); + db_printf("%s token %u qbytes %d qframes %d seqstart %u start %u wnd %u\n", + sep, tap->txa_token, tap->txa_qbytes, tap->txa_qframes, + tap->txa_seqstart, tap->txa_start, tap->txa_wnd); + db_printf("%s attempts %d nextrequest %d\n", + sep, tap->txa_attempts, tap->txa_nextrequest); + /* XXX packet q + timer */ +} + +static void +_db_show_rxampdu(const char *sep, int ix, const struct ieee80211_rx_ampdu *rap) +{ + db_printf("%srxampdu[%d]: %p flags 0x%x tid %u\n", + sep, ix, rap, rap->rxa_flags, ix /*XXX */); + db_printf("%s qbytes %d qframes %d seqstart %u start %u wnd %u\n", + sep, rap->rxa_qbytes, rap->rxa_qframes, + rap->rxa_seqstart, rap->rxa_start, rap->rxa_wnd); + db_printf("%s age %d nframes %d\n", + sep, rap->rxa_age, rap->rxa_nframes); +} + +static void +_db_show_sta(const struct ieee80211_node *ni) +{ + int i; + + db_printf("0x%p: mac %s refcnt %d\n", ni, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); + db_printf("\tvap %p wdsvap %p ic %p table %p\n", + ni->ni_vap, ni->ni_wdsvap, ni->ni_ic, ni->ni_table); + db_printf("\tflags=%b\n", ni->ni_flags, IEEE80211_NODE_BITS); + db_printf("\tscangen %u authmode %u ath_flags 0x%x ath_defkeyix %u\n", + ni->ni_scangen, ni->ni_authmode, + ni->ni_ath_flags, ni->ni_ath_defkeyix); + db_printf("\tassocid 0x%x txpower %u vlan %u\n", + ni->ni_associd, ni->ni_txpower, ni->ni_vlan); + db_printf("\tjointime %d (%lu secs) challenge %p\n", + ni->ni_jointime, (unsigned long)(time_uptime - ni->ni_jointime), + ni->ni_challenge); + db_printf("\ties: data %p len %d\n", ni->ni_ies.data, ni->ni_ies.len); + db_printf("\t[wpa_ie %p rsn_ie %p wme_ie %p ath_ie %p\n", + ni->ni_ies.wpa_ie, ni->ni_ies.rsn_ie, ni->ni_ies.wme_ie, + ni->ni_ies.ath_ie); + db_printf("\t htcap_ie %p htinfo_ie %p]\n", + ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie); + db_printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n", + ni->ni_txseqs[IEEE80211_NONQOS_TID], + ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxfragstamp); + db_printf("\trxfrag[0] %p rxfrag[1] %p rxfrag[2] %p\n", + ni->ni_rxfrag[0], ni->ni_rxfrag[1], ni->ni_rxfrag[2]); + db_printf("\trstamp %u avgrssi 0x%x (rssi %d) noise %d\n", + ni->ni_rstamp, ni->ni_avgrssi, + IEEE80211_RSSI_GET(ni->ni_avgrssi), ni->ni_noise); + db_printf("\tintval %u capinfo %b\n", + ni->ni_intval, ni->ni_capinfo, IEEE80211_CAPINFO_BITS); + db_printf("\tbssid %s", ether_sprintf(ni->ni_bssid)); + _db_show_ssid(" essid ", 0, ni->ni_esslen, ni->ni_essid); + db_printf("\n"); + _db_show_channel("\tchannel", ni->ni_chan); + db_printf("\n"); + db_printf("\terp %b dtim_period %u dtim_count %u\n", + ni->ni_erp, IEEE80211_ERP_BITS, + ni->ni_dtim_period, ni->ni_dtim_count); + + db_printf("\thtcap %b htparam 0x%x htctlchan %u ht2ndchan %u\n", + ni->ni_htcap, IEEE80211_HTCAP_BITS, + ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan); + db_printf("\thtopmode 0x%x htstbc 0x%x reqcw %u chw %u\n", + ni->ni_htopmode, ni->ni_htstbc, ni->ni_reqcw, ni->ni_chw); + + /* XXX ampdu state */ + for (i = 0; i < WME_NUM_AC; i++) + if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP) + _db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]); + for (i = 0; i < WME_NUM_TID; i++) + if (ni->ni_rx_ampdu[i].rxa_nframes) + _db_show_rxampdu("\t", i, &ni->ni_rx_ampdu[i]); + + db_printf("\tinact %u inact_reload %u txrate %u\n", + ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate); + /* XXX savedq */ + /* XXX wdsq */ +} + +static void +_db_show_vap(const struct ieee80211vap *vap, int showprocs) +{ + const struct ieee80211com *ic = vap->iv_ic; + int i; + + db_printf("%p:", vap); + db_printf(" bss %p", vap->iv_bss); + db_printf(" myaddr %s", ether_sprintf(vap->iv_myaddr)); + db_printf("\n"); + + db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]); + db_printf(" state %s", ieee80211_state_name[vap->iv_state]); + db_printf(" ifp %p", vap->iv_ifp); + db_printf("\n"); + + db_printf("\tic %p", vap->iv_ic); + db_printf(" media %p", &vap->iv_media); + db_printf(" bpf_if %p", vap->iv_rawbpf); + db_printf(" mgtsend %p", &vap->iv_mgtsend); +#if 0 + struct sysctllog *iv_sysctl; /* dynamic sysctl context */ +#endif + db_printf("\n"); + db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS); + + db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS); + db_printf("\tflags_ext=%b\n", vap->iv_flags_ext, IEEE80211_FEXT_BITS); + db_printf("\tflags_ven=%b\n", vap->iv_flags_ven, IEEE80211_FVEN_BITS); + db_printf("\tcaps=%b\n", vap->iv_caps, IEEE80211_C_BITS); + db_printf("\thtcaps=%b\n", vap->iv_htcaps, IEEE80211_C_HTCAP_BITS); + + _db_show_stats(&vap->iv_stats); + + db_printf("\tinact_init %d", vap->iv_inact_init); + db_printf(" inact_auth %d", vap->iv_inact_auth); + db_printf(" inact_run %d", vap->iv_inact_run); + db_printf(" inact_probe %d", vap->iv_inact_probe); + db_printf("\n"); + + db_printf("\tdes_nssid %d", vap->iv_des_nssid); + if (vap->iv_des_nssid) + _db_show_ssid(" des_ssid[%u] ", 0, + vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid); + db_printf(" des_bssid %s", ether_sprintf(vap->iv_des_bssid)); + db_printf("\n"); + db_printf("\tdes_mode %d", vap->iv_des_mode); + _db_show_channel(" des_chan", vap->iv_des_chan); + db_printf("\n"); +#if 0 + int iv_nicknamelen; /* XXX junk */ + uint8_t iv_nickname[IEEE80211_NWID_LEN]; +#endif + db_printf("\tbgscanidle %u", vap->iv_bgscanidle); + db_printf(" bgscanintvl %u", vap->iv_bgscanintvl); + db_printf(" scanvalid %u", vap->iv_scanvalid); + db_printf("\n"); + db_printf("\tscanreq_duration %u", vap->iv_scanreq_duration); + db_printf(" scanreq_mindwell %u", vap->iv_scanreq_mindwell); + db_printf(" scanreq_maxdwell %u", vap->iv_scanreq_maxdwell); + db_printf("\n"); + db_printf(" scanreq_flags 0x%x", vap->iv_scanreq_flags); + db_printf("\tscanreq_nssid %d", vap->iv_scanreq_nssid); + for (i = 0; i < vap->iv_scanreq_nssid; i++) + _db_show_ssid(" scanreq_ssid[%u]", i, + vap->iv_scanreq_ssid[i].len, vap->iv_scanreq_ssid[i].ssid); + db_printf(" roaming %d", vap->iv_roaming); + db_printf("\n"); + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) + if (isset(ic->ic_modecaps, i)) { + _db_show_roamparams("\troamparms[%s]", + ieee80211_phymode_name[i], &vap->iv_roamparms[i]); + db_printf("\n"); + } + + db_printf("\tbmissthreshold %u", vap->iv_bmissthreshold); + db_printf(" bmiss_max %u", vap->iv_bmiss_count); + db_printf(" bmiss_max %d", vap->iv_bmiss_max); + db_printf("\n"); + db_printf("\tswbmiss_count %u", vap->iv_swbmiss_count); + db_printf(" swbmiss_period %u", vap->iv_swbmiss_period); + db_printf(" swbmiss %p", &vap->iv_swbmiss); + db_printf("\n"); + + db_printf("\tampdu_rxmax %d", vap->iv_ampdu_rxmax); + db_printf(" ampdu_density %d", vap->iv_ampdu_density); + db_printf(" ampdu_limit %d", vap->iv_ampdu_limit); + db_printf(" amsdu_limit %d", vap->iv_amsdu_limit); + db_printf("\n"); + + db_printf("\tmax_aid %u", vap->iv_max_aid); + db_printf(" aid_bitmap %p", vap->iv_aid_bitmap); + db_printf("\n"); + db_printf("\tsta_assoc %u", vap->iv_sta_assoc); + db_printf(" ps_sta %u", vap->iv_ps_sta); + db_printf(" ps_pending %u", vap->iv_ps_pending); + db_printf(" tim_len %u", vap->iv_tim_len); + db_printf(" tim_bitmap %p", vap->iv_tim_bitmap); + db_printf("\n"); + db_printf("\tdtim_period %u", vap->iv_dtim_period); + db_printf(" dtim_count %u", vap->iv_dtim_count); + db_printf(" set_tim %p", vap->iv_set_tim); + db_printf(" csa_count %d", vap->iv_csa_count); + db_printf("\n"); + + db_printf("\trtsthreshold %u", vap->iv_rtsthreshold); + db_printf(" fragthreshold %u", vap->iv_fragthreshold); + db_printf(" inact_timer %d", vap->iv_inact_timer); + db_printf("\n"); + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) + if (isset(ic->ic_modecaps, i)) { + _db_show_txparams("\ttxparms[%s]", + ieee80211_phymode_name[i], &vap->iv_txparms[i]); + db_printf("\n"); + } + + /* application-specified IE's to attach to mgt frames */ + _db_show_appie("\tappie_beacon", vap->iv_appie_beacon); + _db_show_appie("\tappie_probereq", vap->iv_appie_probereq); + _db_show_appie("\tappie_proberesp", vap->iv_appie_proberesp); + _db_show_appie("\tappie_assocreq", vap->iv_appie_assocreq); + _db_show_appie("\tappie_asscoresp", vap->iv_appie_assocresp); + _db_show_appie("\tappie_wpa", vap->iv_appie_wpa); + if (vap->iv_wpa_ie != NULL || vap->iv_rsn_ie != NULL) { + if (vap->iv_wpa_ie != NULL) + db_printf("\twpa_ie %p", vap->iv_wpa_ie); + if (vap->iv_rsn_ie != NULL) + db_printf("\trsn_ie %p", vap->iv_rsn_ie); + db_printf("\n"); + } + db_printf("\tmax_keyix %u", vap->iv_max_keyix); + db_printf(" def_txkey %d", vap->iv_def_txkey); + db_printf("\n"); + for (i = 0; i < IEEE80211_WEP_NKID; i++) + _db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]); + + db_printf("\tauth %p", vap->iv_auth); + db_printf(" ec %p", vap->iv_ec); + + db_printf(" acl %p", vap->iv_acl); + db_printf(" as %p", vap->iv_as); + db_printf("\n"); + + if (showprocs) { + db_printf("\tiv_key_alloc %p\n", vap->iv_key_alloc); + db_printf("\tiv_key_delete %p\n", vap->iv_key_delete); + db_printf("\tiv_key_set %p\n", vap->iv_key_set); + db_printf("\tiv_key_update_begin %p\n", vap->iv_key_update_begin); + db_printf("\tiv_key_update_end %p\n", vap->iv_key_update_end); + db_printf("\tiv_opdetach %p\n", vap->iv_opdetach); + db_printf("\tiv_input %p\n", vap->iv_input); + db_printf("\tiv_recv_mgmt %p\n", vap->iv_recv_mgmt); + db_printf("\tiv_deliver_data %p\n", vap->iv_deliver_data); + db_printf("\tiv_bmiss %p\n", vap->iv_bmiss); + db_printf("\tiv_reset %p\n", vap->iv_reset); + db_printf("\tiv_update_beacon %p\n", vap->iv_update_beacon); + db_printf("\tiv_newstate %p\n", vap->iv_newstate); + db_printf("\tiv_output %p\n", vap->iv_output); + } +} + +static void +_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs) +{ + struct ieee80211vap *vap; + + db_printf("%p:", ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + db_printf(" %s(%p)", vap->iv_ifp->if_xname, vap); + db_printf("\n"); + db_printf("\tifp %p", ic->ic_ifp); + db_printf(" comlock %p", &ic->ic_comlock); + db_printf("\n"); + _db_show_stats(&ic->ic_stats); + db_printf("\theadroom %d", ic->ic_headroom); + db_printf(" phytype %d", ic->ic_phytype); + db_printf(" opmode %s", ieee80211_opmode_name[ic->ic_opmode]); + db_printf("\n"); + db_printf("\tmedia %p", &ic->ic_media); + db_printf(" myaddr %s", ether_sprintf(ic->ic_myaddr)); + db_printf(" inact %p", &ic->ic_inact); + db_printf("\n"); + + db_printf("\tflags=%b\n", ic->ic_flags, IEEE80211_F_BITS); + db_printf("\tflags_ext=%b\n", ic->ic_flags_ext, IEEE80211_FEXT_BITS); + db_printf("\tflags_ven=%b\n", ic->ic_flags_ven, IEEE80211_FVEN_BITS); + db_printf("\tcaps=%b\n", ic->ic_caps, IEEE80211_C_BITS); + db_printf("\tcryptocaps=%b\n", + ic->ic_cryptocaps, IEEE80211_C_CRYPTO_BITS); + db_printf("\thtcaps=%b\n", ic->ic_htcaps, IEEE80211_HTCAP_BITS); + +#if 0 + uint8_t ic_modecaps[2]; /* set of mode capabilities */ +#endif + db_printf("\tcurmode %u", ic->ic_curmode); + db_printf(" promisc %u", ic->ic_promisc); + db_printf(" allmulti %u", ic->ic_allmulti); + db_printf(" nrunning %u", ic->ic_nrunning); + db_printf("\n"); + db_printf("\tbintval %u", ic->ic_bintval); + db_printf(" lintval %u", ic->ic_lintval); + db_printf(" holdover %u", ic->ic_holdover); + db_printf(" txpowlimit %u", ic->ic_txpowlimit); + db_printf("\n"); +#if 0 + struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; +#endif + /* + * Channel state: + * + * ic_channels is the set of available channels for the device; + * it is setup by the driver + * ic_nchans is the number of valid entries in ic_channels + * ic_chan_avail is a bit vector of these channels used to check + * whether a channel is available w/o searching the channel table. + * ic_chan_active is a (potentially) constrained subset of + * ic_chan_avail that reflects any mode setting or user-specified + * limit on the set of channels to use/scan + * ic_curchan is the current channel the device is set to; it may + * be different from ic_bsschan when we are off-channel scanning + * or otherwise doing background work + * ic_bsschan is the channel selected for operation; it may + * be undefined (IEEE80211_CHAN_ANYC) + * ic_prevchan is a cached ``previous channel'' used to optimize + * lookups when switching back+forth between two channels + * (e.g. for dynamic turbo) + */ + db_printf("\tnchans %d", ic->ic_nchans); +#if 0 + struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; + uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; + uint8_t ic_chan_active[IEEE80211_CHAN_BYTES]; + uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; +#endif + db_printf("\n"); + _db_show_channel("\tcurchan", ic->ic_curchan); + db_printf("\n"); + _db_show_channel("\tbsschan", ic->ic_bsschan); + db_printf("\n"); + _db_show_channel("\tprevchan", ic->ic_prevchan); + db_printf("\n"); + db_printf("\tregdomain %p", &ic->ic_regdomain); + db_printf("\n"); + + _db_show_channel("\tcsa_newchan", ic->ic_csa_newchan); + db_printf(" csa_count %d", ic->ic_csa_count); + db_printf( "dfs %p", &ic->ic_dfs); + db_printf("\n"); + + db_printf("\tscan %p", ic->ic_scan); + db_printf(" lastdata %d", ic->ic_lastdata); + db_printf(" lastscan %d", ic->ic_lastscan); + db_printf("\n"); + + db_printf("\tmax_keyix %d", ic->ic_max_keyix); + db_printf(" sta %p", &ic->ic_sta); + db_printf(" wme %p", &ic->ic_wme); + db_printf("\n"); + + db_printf("\tprotmode %d", ic->ic_protmode); + db_printf(" nonerpsta %u", ic->ic_nonerpsta); + db_printf(" longslotsta %u", ic->ic_longslotsta); + db_printf(" lastnonerp %d", ic->ic_lastnonerp); + db_printf("\n"); + db_printf("\tsta_assoc %u", ic->ic_sta_assoc); + db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc); + db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc); + db_printf("\n"); + db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode); + db_printf(" htprotmode %d", ic->ic_htprotmode); + db_printf(" lastnonht %d", ic->ic_lastnonht); + db_printf("\n"); + + if (showprocs) { + db_printf("\tic_vap_create %p\n", ic->ic_vap_create); + db_printf("\tic_vap_delete %p\n", ic->ic_vap_delete); +#if 0 + /* operating mode attachment */ + ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX]; +#endif + db_printf("\tic_newassoc %p\n", ic->ic_newassoc); + db_printf("\tic_getradiocaps %p\n", ic->ic_getradiocaps); + db_printf("\tic_setregdomain %p\n", ic->ic_setregdomain); + db_printf("\tic_send_mgmt %p\n", ic->ic_send_mgmt); + db_printf("\tic_raw_xmit %p\n", ic->ic_raw_xmit); + db_printf("\tic_updateslot %p\n", ic->ic_updateslot); + db_printf("\tic_update_mcast %p\n", ic->ic_update_mcast); + db_printf("\tic_update_promisc %p\n", ic->ic_update_promisc); + db_printf("\tic_node_alloc %p\n", ic->ic_node_alloc); + db_printf("\tic_node_free %p\n", ic->ic_node_free); + db_printf("\tic_node_cleanup %p\n", ic->ic_node_cleanup); + db_printf("\tic_node_getrssi %p\n", ic->ic_node_getrssi); + db_printf("\tic_node_getsignal %p\n", ic->ic_node_getsignal); + db_printf("\tic_node_getmimoinfo %p\n", ic->ic_node_getmimoinfo); + db_printf("\tic_scan_start %p\n", ic->ic_scan_start); + db_printf("\tic_scan_end %p\n", ic->ic_scan_end); + db_printf("\tic_set_channel %p\n", ic->ic_set_channel); + db_printf("\tic_scan_curchan %p\n", ic->ic_scan_curchan); + db_printf("\tic_scan_mindwell %p\n", ic->ic_scan_mindwell); + db_printf("\tic_recv_action %p\n", ic->ic_recv_action); + db_printf("\tic_send_action %p\n", ic->ic_send_action); + db_printf("\tic_addba_request %p\n", ic->ic_addba_request); + db_printf("\tic_addba_response %p\n", ic->ic_addba_response); + db_printf("\tic_addba_stop %p\n", ic->ic_addba_stop); + } + if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) { + db_printf("\n"); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + _db_show_vap(vap, showprocs); + } + if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) { + const struct ieee80211_node_table *nt = &ic->ic_sta; + const struct ieee80211_node *ni; + + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + db_printf("\n"); + _db_show_sta(ni); + } + } +} + +static void +_db_show_channel(const char *tag, const struct ieee80211_channel *c) +{ + db_printf("%s ", tag); + if (c == NULL) + db_printf("<NULL>"); + else if (c == IEEE80211_CHAN_ANYC) + db_printf("<ANY>"); + else + db_printf("[%u (%u) flags=%b maxreg %u maxpow %u minpow %u state 0x%x extieee %u]", + c->ic_freq, c->ic_ieee, + c->ic_flags, IEEE80211_CHAN_BITS, + c->ic_maxregpower, c->ic_maxpower, c->ic_minpower, + c->ic_state, c->ic_extieee); +} + +static void +_db_show_ssid(const char *tag, int ix, int len, const uint8_t *ssid) +{ + const uint8_t *p; + int i; + + db_printf(tag, ix); + + if (len > IEEE80211_NWID_LEN) + len = IEEE80211_NWID_LEN; + /* determine printable or not */ + for (i = 0, p = ssid; i < len; i++, p++) { + if (*p < ' ' || *p > 0x7e) + break; + } + if (i == len) { + db_printf("\""); + for (i = 0, p = ssid; i < len; i++, p++) + db_printf("%c", *p); + db_printf("\""); + } else { + db_printf("0x"); + for (i = 0, p = ssid; i < len; i++, p++) + db_printf("%02x", *p); + } +} + +static void +_db_show_appie(const char *tag, const struct ieee80211_appie *ie) +{ + const uint8_t *p; + int i; + + if (ie == NULL) + return; + db_printf("%s [0x", tag); + for (i = 0, p = ie->ie_data; i < ie->ie_len; i++, p++) + db_printf("%02x", *p); + db_printf("]\n"); +} + +static void +_db_show_key(const char *tag, int ix, const struct ieee80211_key *wk) +{ + static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; + const struct ieee80211_cipher *cip = wk->wk_cipher; + int keylen = wk->wk_keylen; + + if (wk->wk_keyix == IEEE80211_KEYIX_NONE) + return; + db_printf(tag, ix); + switch (cip->ic_cipher) { + case IEEE80211_CIPHER_WEP: + /* compatibility */ + db_printf(" wepkey %u:%s", wk->wk_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 */ + db_printf(" TKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_AES_OCB: + db_printf(" AES-OCB %u:%u-bit", wk->wk_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_AES_CCM: + db_printf(" AES-CCM %u:%u-bit", wk->wk_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_CKIP: + db_printf(" CKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen); + break; + case IEEE80211_CIPHER_NONE: + db_printf(" NULL %u:%u-bit", wk->wk_keyix+1, 8*keylen); + break; + default: + db_printf(" UNKNOWN (0x%x) %u:%u-bit", + cip->ic_cipher, wk->wk_keyix+1, 8*keylen); + break; + } + if (memcmp(wk->wk_key, zerodata, keylen) != 0) { + int i; + + db_printf(" <"); + for (i = 0; i < keylen; i++) + db_printf("%02x", wk->wk_key[i]); + db_printf(">"); + if (cip->ic_cipher != IEEE80211_CIPHER_WEP && + wk->wk_keyrsc[IEEE80211_NONQOS_TID] != 0) + db_printf(" rsc %ju", (uintmax_t)wk->wk_keyrsc[IEEE80211_NONQOS_TID]); + if (cip->ic_cipher != IEEE80211_CIPHER_WEP && + wk->wk_keytsc != 0) + db_printf(" tsc %ju", (uintmax_t)wk->wk_keytsc); + if (wk->wk_flags != 0) { + const char *sep = " "; + + if (wk->wk_flags & IEEE80211_KEY_XMIT) + db_printf("%stx", sep), sep = "+"; + if (wk->wk_flags & IEEE80211_KEY_RECV) + db_printf("%srx", sep), sep = "+"; + if (wk->wk_flags & IEEE80211_KEY_DEFAULT) + db_printf("%sdef", sep), sep = "+"; + } + db_printf("\n"); + } +} + +static void +printrate(const char *tag, int v) +{ + if (v == IEEE80211_FIXED_RATE_NONE) + db_printf(" %s <none>", tag); + else if (v == 11) + db_printf(" %s 5.5", tag); + else if (v & IEEE80211_RATE_MCS) + db_printf(" %s MCS%d", tag, v &~ IEEE80211_RATE_MCS); + else + db_printf(" %s %d", tag, v/2); +} + +static void +_db_show_roamparams(const char *tag, const void *arg, + const struct ieee80211_roamparam *rp) +{ + + db_printf(tag, arg); + if (rp->rssi & 1) + db_printf(" rssi %u.5", rp->rssi/2); + else + db_printf(" rssi %u", rp->rssi/2); + printrate("rate", rp->rate); +} + +static void +_db_show_txparams(const char *tag, const void *arg, + const struct ieee80211_txparam *tp) +{ + + db_printf(tag, arg); + printrate("ucastrate", tp->ucastrate); + printrate("mcastrate", tp->mcastrate); + printrate("mgmtrate", tp->mgmtrate); + db_printf(" maxretry %d", tp->maxretry); +} + +static void +_db_show_stats(const struct ieee80211_stats *is) +{ +} +#endif /* DDB */ diff --git a/sys/net80211/ieee80211_dfs.c b/sys/net80211/ieee80211_dfs.c new file mode 100644 index 0000000..0351cb8 --- /dev/null +++ b/sys/net80211/ieee80211_dfs.c @@ -0,0 +1,372 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 DFS/Radar support. + */ +#include "opt_inet.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state"); + +/* XXX public for sysctl hookup */ +int ieee80211_nol_timeout = 30*60; /* 30 minutes */ +#define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000) +int ieee80211_cac_timeout = 60; /* 60 seconds */ +#define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000) + +void +ieee80211_dfs_attach(struct ieee80211com *ic) +{ + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + + callout_init(&dfs->nol_timer, CALLOUT_MPSAFE); + callout_init(&dfs->cac_timer, CALLOUT_MPSAFE); +} + +void +ieee80211_dfs_detach(struct ieee80211com *ic) +{ + /* NB: we assume no locking is needed */ + ieee80211_dfs_reset(ic); +} + +void +ieee80211_dfs_reset(struct ieee80211com *ic) +{ + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + int i; + + /* NB: we assume no locking is needed */ + /* NB: cac_timer should be cleared by the state machine */ + callout_drain(&dfs->nol_timer); + for (i = 0; i < ic->ic_nchans; i++) + ic->ic_channels[i].ic_state = 0; + dfs->lastchan = NULL; +} + +static void +cac_timeout(void *arg) +{ + struct ieee80211vap *vap = arg; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + int i; + + if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */ + return; + /* + * When radar is detected during a CAC we are woken + * up prematurely to switch to a new channel. + * Check the channel to decide how to act. + */ + if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) { + ieee80211_notify_cac(ic, ic->ic_curchan, + IEEE80211_NOTIFY_CAC_RADAR); + + if_printf(vap->iv_ifp, + "CAC timer on channel %u (%u MHz) stopped due to radar\n", + ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq); + + /* XXX clobbers any existing desired channel */ + /* NB: dfs->newchan may be NULL, that's ok */ + vap->iv_des_chan = dfs->newchan; + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } else { + if_printf(vap->iv_ifp, + "CAC timer on channel %u (%u MHz) expired; " + "no radar detected\n", + ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq); + /* + * Mark all channels with the current frequency + * as having completed CAC; this keeps us from + * doing it again until we change channels. + */ + for (i = 0; i < ic->ic_nchans; i++) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + if (c->ic_freq == ic->ic_curchan->ic_freq) + c->ic_state |= IEEE80211_CHANSTATE_CACDONE; + } + ieee80211_notify_cac(ic, ic->ic_curchan, + IEEE80211_NOTIFY_CAC_EXPIRE); + ieee80211_cac_completeswitch(vap); + } +} + +/* + * Initiate the CAC timer. The driver is responsible + * for setting up the hardware to scan for radar on the + * channnel, we just handle timing things out. + */ +void +ieee80211_dfs_cac_start(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + + IEEE80211_LOCK_ASSERT(ic); + + callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap); + if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n", + ticks_to_secs(CAC_TIMEOUT), + ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq); + ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START); +} + +/* + * Clear the CAC timer. + */ +void +ieee80211_dfs_cac_stop(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + + IEEE80211_LOCK_ASSERT(ic); + + /* NB: racey but not important */ + if (callout_pending(&dfs->cac_timer)) { + if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n", + ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq); + ieee80211_notify_cac(ic, ic->ic_curchan, + IEEE80211_NOTIFY_CAC_STOP); + } + /* XXX cannot use drain 'cuz holding a lock */ + callout_stop(&dfs->cac_timer); +} + +void +ieee80211_dfs_cac_clear(struct ieee80211com *ic, + const struct ieee80211_channel *chan) +{ + int i; + + for (i = 0; i < ic->ic_nchans; i++) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + if (c->ic_freq == chan->ic_freq) + c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE; + } +} + +static void +dfs_timeout(void *arg) +{ + struct ieee80211com *ic = arg; + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + struct ieee80211_channel *c; + int i, oldest, now; + + IEEE80211_LOCK(ic); + now = oldest = ticks; + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (IEEE80211_IS_CHAN_RADAR(c)) { + if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) { + c->ic_state &= ~IEEE80211_CHANSTATE_RADAR; + if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) { + /* + * NB: do this here so we get only one + * msg instead of one for every channel + * table entry. + */ + if_printf(ic->ic_ifp, "radar on channel" + " %u (%u MHz) cleared after timeout\n", + c->ic_ieee, c->ic_freq); + /* notify user space */ + c->ic_state &= + ~IEEE80211_CHANSTATE_NORADAR; + ieee80211_notify_radar(ic, c); + } + } else if (dfs->nol_event[i] < oldest) + oldest = dfs->nol_event[i]; + } + } + if (oldest != now) { + /* arrange to process next channel up for a status change */ + callout_reset(&dfs->nol_timer, oldest + NOL_TIMEOUT, + dfs_timeout, ic); + } + IEEE80211_UNLOCK(ic); +} + +static void +announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan, + const struct ieee80211_channel *newchan) +{ + if (newchan == NULL) + if_printf(ifp, "radar detected on channel %u (%u MHz)\n", + curchan->ic_ieee, curchan->ic_freq); + else + if_printf(ifp, "radar detected on channel %u (%u MHz), " + "moving to channel %u (%u MHz)\n", + curchan->ic_ieee, curchan->ic_freq, + newchan->ic_ieee, newchan->ic_freq); +} + +/* + * Handle a radar detection event on a channel. The channel is + * added to the NOL list and we record the time of the event. + * Entries are aged out after NOL_TIMEOUT. If radar was + * detected while doing CAC we force a state/channel change. + * Otherwise radar triggers a channel switch using the CSA + * mechanism (when the channel is the bss channel). + */ +void +ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan) +{ + struct ieee80211_dfs_state *dfs = &ic->ic_dfs; + int i, now; + + IEEE80211_LOCK_ASSERT(ic); + + /* + * Mark all entries with this frequency. Notify user + * space and arrange for notification when the radar + * indication is cleared. Then kick the NOL processing + * thread if not already running. + */ + now = ticks; + for (i = 0; i < ic->ic_nchans; i++) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + if (c->ic_freq == chan->ic_freq) { + c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE; + c->ic_state |= IEEE80211_CHANSTATE_RADAR; + dfs->nol_event[i] = now; + } + } + ieee80211_notify_radar(ic, chan); + chan->ic_state |= IEEE80211_CHANSTATE_NORADAR; + if (!callout_pending(&dfs->nol_timer)) + callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic); + + /* + * If radar is detected on the bss channel while + * doing CAC; force a state change by scheduling the + * callout to be dispatched asap. Otherwise, if this + * event is for the bss channel then we must quiet + * traffic and schedule a channel switch. + * + * Note this allows us to receive notification about + * channels other than the bss channel; not sure + * that can/will happen but it's simple to support. + */ + if (chan == ic->ic_bsschan) { + /* XXX need a way to defer to user app */ + dfs->newchan = ieee80211_dfs_pickchannel(ic); + + announce_radar(ic->ic_ifp, chan, dfs->newchan); + + if (callout_pending(&dfs->cac_timer)) + callout_reset(&dfs->nol_timer, 0, dfs_timeout, ic); + else if (dfs->newchan != NULL) { + /* XXX mode 1, switch count 2 */ + /* XXX calculate switch count based on max + switch time and beacon interval? */ + ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2); + } else { + /* + * Spec says to stop all transmissions and + * wait on the current channel for an entry + * on the NOL to expire. + */ + /*XXX*/ + } + } else { + /* + * Issue rate-limited console msgs. + */ + if (dfs->lastchan != chan) { + dfs->lastchan = chan; + dfs->cureps = 0; + announce_radar(ic->ic_ifp, chan, NULL); + } else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) { + announce_radar(ic->ic_ifp, chan, NULL); + } + } +} + +struct ieee80211_channel * +ieee80211_dfs_pickchannel(struct ieee80211com *ic) +{ + struct ieee80211_channel *c; + int i, flags; + uint16_t v; + + /* + * Consult the scan cache first. + */ + flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL; + /* + * XXX if curchan is HT this will never find a channel + * XXX 'cuz we scan only legacy channels + */ + c = ieee80211_scan_pickchannel(ic, flags); + if (c != NULL) + return c; + /* + * No channel found in scan cache; select a compatible + * one at random (skipping channels where radar has + * been detected). + */ + get_random_bytes(&v, sizeof(v)); + v %= ic->ic_nchans; + for (i = v; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (!IEEE80211_IS_CHAN_RADAR(c) && + (c->ic_flags & flags) == flags) + return c; + } + for (i = 0; i < v; i++) { + c = &ic->ic_channels[i]; + if (!IEEE80211_IS_CHAN_RADAR(c) && + (c->ic_flags & flags) == flags) + return c; + } + if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n"); + return NULL; +} diff --git a/sys/net80211/ieee80211_dfs.h b/sys/net80211/ieee80211_dfs.h new file mode 100644 index 0000000..36d14aa --- /dev/null +++ b/sys/net80211/ieee80211_dfs.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_DFS_H_ +#define _NET80211_IEEE80211_DFS_H_ + +/* + * 802.11h/DFS definitions. + */ + +struct ieee80211_dfs_state { + int nol_event[IEEE80211_CHAN_MAX+1]; + struct callout nol_timer; /* NOL list processing */ + struct callout cac_timer; /* CAC timer */ + struct timeval lastevent; /* time of last radar event */ + int cureps; /* current events/second */ + const struct ieee80211_channel *lastchan;/* chan w/ last radar event */ + struct ieee80211_channel *newchan; /* chan selected next */ +}; + +void ieee80211_dfs_attach(struct ieee80211com *); +void ieee80211_dfs_detach(struct ieee80211com *); + +void ieee80211_dfs_reset(struct ieee80211com *); + +void ieee80211_dfs_cac_start(struct ieee80211vap *); +void ieee80211_dfs_cac_stop(struct ieee80211vap *); +void ieee80211_dfs_cac_clear(struct ieee80211com *, + const struct ieee80211_channel *); + +void ieee80211_dfs_notify_radar(struct ieee80211com *, + struct ieee80211_channel *); +struct ieee80211_channel *ieee80211_dfs_pickchannel(struct ieee80211com *); +#endif /* _NET80211_IEEE80211_DFS_H_ */ diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c index 1cb7813..d0b1b69 100644 --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 support (FreeBSD-specific code) */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -41,7 +43,9 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_clone.h> #include <net/if_media.h> +#include <net/if_types.h> #include <net/ethernet.h> #include <net/route.h> @@ -57,24 +61,112 @@ SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, extern int ieee80211_recv_bar_ena; SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena, 0, "BAR frame processing (ena/dis)"); +extern int ieee80211_nol_timeout; +SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW, + &ieee80211_nol_timeout, 0, "NOL timeout (secs)"); +extern int ieee80211_cac_timeout; +SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW, + &ieee80211_cac_timeout, 0, "CAC timeout (secs)"); + +MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state"); + +/* + * Allocate/free com structure in conjunction with ifnet; + * these routines are registered with if_register_com_alloc + * below and are called automatically by the ifnet code + * when the ifnet of the parent device is created. + */ +static void * +wlan_alloc(u_char type, struct ifnet *ifp) +{ + struct ieee80211com *ic; + + ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO); + ic->ic_ifp = ifp; + + return (ic); +} + +static void +wlan_free(void *ic, u_char type) +{ + free(ic, M_80211_COM); +} -#ifdef IEEE80211_AMPDU_AGE static int -ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS) +wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) { - extern int ieee80211_ampdu_age; - int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age); + struct ieee80211_clone_params cp; + struct ieee80211vap *vap; + struct ieee80211com *ic; + struct ifnet *ifp; int error; - error = sysctl_handle_int(oidp, &du_age, 0, req); + error = copyin(params, &cp, sizeof(cp)); + if (error) + return error; + ifp = ifunit(cp.icp_parent); + if (ifp == NULL) + return ENXIO; + if (ifp->if_type != IFT_IEEE80211) { + if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__); + return EINVAL; + } + ic = ifp->if_l2com; + vap = ic->ic_vap_create(ic, ifc->ifc_name, unit, + cp.icp_opmode, cp.icp_flags, cp.icp_bssid, + cp.icp_flags & IEEE80211_CLONE_MACADDR ? + cp.icp_macaddr : ic->ic_myaddr); + return (vap == NULL ? EIO : 0); +} + +static void +wlan_clone_destroy(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + + ic->ic_vap_delete(vap); +} +IFC_SIMPLE_DECLARE(wlan, 0); + +void +ieee80211_vap_destroy(struct ieee80211vap *vap) +{ + ifc_simple_destroy(&wlan_cloner, vap->iv_ifp); +} + +static int +ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS) +{ + int msecs = ticks_to_msecs(*(int *)arg1); + int error, t; + + error = sysctl_handle_int(oidp, &msecs, 0, req); if (error || !req->newptr) return error; - ieee80211_ampdu_age = msecs_to_ticks(ampdu_age); + t = msecs_to_ticks(msecs); + *(int *)arg1 = (t < 1) ? 1 : t; return 0; } -SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0, - ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)"); + +#ifdef IEEE80211_AMPDU_AGE +extern int ieee80211_ampdu_age; +SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW, + &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I", + "AMPDU max reorder age (ms)"); #endif +extern int ieee80211_addba_timeout; +SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW, + &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "ADDBA request timeout (ms)"); +extern int ieee80211_addba_backoff; +SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW, + &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I", + "ADDBA request backoff (ms)"); +extern int ieee80211_addba_maxtries; +SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW, + &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff"); static int ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) @@ -101,6 +193,17 @@ ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) void ieee80211_sysctl_attach(struct ieee80211com *ic) { +} + +void +ieee80211_sysctl_detach(struct ieee80211com *ic) +{ +} + +void +ieee80211_sysctl_vattach(struct ieee80211vap *vap) +{ + struct ifnet *ifp = vap->iv_ifp; struct sysctl_ctx_list *ctx; struct sysctl_oid *oid; char num[14]; /* sufficient for 32 bits */ @@ -108,57 +211,76 @@ ieee80211_sysctl_attach(struct ieee80211com *ic) MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list), M_DEVBUF, M_NOWAIT | M_ZERO); if (ctx == NULL) { - if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n", + if_printf(ifp, "%s: cannot allocate sysctl context!\n", __func__); return; } sysctl_ctx_init(ctx); - snprintf(num, sizeof(num), "%u", ic->ic_vap); + snprintf(num, sizeof(num), "%u", ifp->if_dunit); oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), OID_AUTO, num, CTLFLAG_RD, NULL, ""); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A", - "parent device"); + "%parent", CTLFLAG_RD, vap->iv_ic, 0, + ieee80211_sysctl_parent, "A", "parent device"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0, + "driver capabilities"); #ifdef IEEE80211_DEBUG - ic->ic_debug = ieee80211_debug; + vap->iv_debug = ieee80211_debug; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "debug", CTLFLAG_RW, &ic->ic_debug, 0, + "debug", CTLFLAG_RW, &vap->iv_debug, 0, "control debugging printfs"); #endif + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0, + "consecutive beacon misses before scanning"); /* XXX inherit from tunables */ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, - "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0, + "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_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, + "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_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, + "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_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, + "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_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; + if (vap->iv_htcaps & IEEE80211_HTC_HT) { + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_bk", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_BK], 0, + "BK traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_be", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_BE], 0, + "BE traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_vo", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_VO], 0, + "VO traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_vi", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_VI], 0, + "VI traffic tx aggr threshold (pps)"); + } + vap->iv_sysctl = ctx; + vap->iv_oid = oid; } void -ieee80211_sysctl_detach(struct ieee80211com *ic) +ieee80211_sysctl_vdetach(struct ieee80211vap *vap) { - if (ic->ic_sysctl != NULL) { - sysctl_ctx_free(ic->ic_sysctl); - FREE(ic->ic_sysctl, M_DEVBUF); - ic->ic_sysctl = NULL; + if (vap->iv_sysctl != NULL) { + sysctl_ctx_free(vap->iv_sysctl); + FREE(vap->iv_sysctl, M_DEVBUF); + vap->iv_sysctl = NULL; } } @@ -190,6 +312,33 @@ ieee80211_drain_ifq(struct ifqueue *ifq) } } +void +ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap) +{ + struct ieee80211_node *ni; + struct mbuf *m, **mprev; + + IF_LOCK(ifq); + mprev = &ifq->ifq_head; + while ((m = *mprev) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (ni != NULL && ni->ni_vap == vap) { + *mprev = m->m_nextpkt; /* remove from list */ + ifq->ifq_len--; + + m_freem(m); + ieee80211_free_node(ni); /* reclaim ref */ + } else + mprev = &m->m_nextpkt; + } + /* recalculate tail ptr */ + m = ifq->ifq_head; + for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt) + ; + ifq->ifq_tail = m; + IF_UNLOCK(ifq); +} + /* * As above, for mbufs allocated with m_gethdr/MGETHDR * or initialized by M_COPY_PKTHDR. @@ -290,66 +439,78 @@ get_random_bytes(void *p, size_t n) } } -void -ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc) +/* + * Helper function for events that pass just a single mac address. + */ +static void +notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN]) { - 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)); + IEEE80211_ADDR_COPY(iev.iev_addr, mac); + rt_ieee80211msg(ifp, op, &iev, sizeof(iev)); +} + +void +ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join", + (ni == vap->iv_bss) ? "bss " : ""); + + if (ni == vap->iv_bss) { + notify_macaddr(ifp, newassoc ? + RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid); 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)); + notify_macaddr(ifp, newassoc ? + RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr); } } void -ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_notify_node_leave(struct ieee80211_node *ni) { - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211_leave_event iev; + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; - if (ni == ic->ic_bss) { + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave", + (ni == vap->iv_bss) ? "bss " : ""); + + if (ni == vap->iv_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)); + notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr); } } void -ieee80211_notify_scan_done(struct ieee80211com *ic) +ieee80211_notify_scan_done(struct ieee80211vap *vap) { - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); /* dispatch wireless event indicating scan completed */ rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); } void -ieee80211_notify_replay_failure(struct ieee80211com *ic, +ieee80211_notify_replay_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const struct ieee80211_key *k, u_int64_t rsc) { - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n", - ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name, - (intmax_t) rsc, (intmax_t) k->wk_keyrsc, + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>", + k->wk_cipher->ic_name, (intmax_t) rsc, + (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID], k->wk_keyix, k->wk_rxkeyix); if (ifp != NULL) { /* NB: for cipher test modules */ @@ -362,22 +523,21 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic, iev.iev_keyix = k->wk_rxkeyix; else iev.iev_keyix = k->wk_keyix; - iev.iev_keyrsc = k->wk_keyrsc; + iev.iev_keyrsc = k->wk_keyrsc[0]; /* XXX need tid */ iev.iev_rsc = rsc; rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); } } void -ieee80211_notify_michael_failure(struct ieee80211com *ic, +ieee80211_notify_michael_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, u_int keyix) { - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] michael MIC verification failed <keyix %u>\n", - ether_sprintf(wh->i_addr2), keyix); - ic->ic_stats.is_rx_tkipmic++; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "michael MIC verification failed <keyix %u>", keyix); + vap->iv_stats.is_rx_tkipmic++; if (ifp != NULL) { /* NB: for cipher test modules */ struct ieee80211_michael_event iev; @@ -391,6 +551,107 @@ ieee80211_notify_michael_failure(struct ieee80211com *ic, } void +ieee80211_notify_wds_discover(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr); +} + +void +ieee80211_notify_csa(struct ieee80211com *ic, + const struct ieee80211_channel *c, int mode, int count) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_csa_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + iev.iev_mode = mode; + iev.iev_count = count; + rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev)); +} + +void +ieee80211_notify_radar(struct ieee80211com *ic, + const struct ieee80211_channel *c) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_radar_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev)); +} + +void +ieee80211_notify_cac(struct ieee80211com *ic, + const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_cac_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + iev.iev_type = type; + rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev)); +} + +void +ieee80211_notify_node_deauth(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth"); + + notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr); +} + +void +ieee80211_notify_node_auth(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth"); + + notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr); +} + +void +ieee80211_notify_country(struct ieee80211vap *vap, + const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2]) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_country_event iev; + + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, bssid); + iev.iev_cc[0] = cc[0]; + iev.iev_cc[1] = cc[1]; + rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev)); +} + +void +ieee80211_notify_radio(struct ieee80211com *ic, int state) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_radio_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_state = state; + rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev)); +} + +void ieee80211_load_module(const char *modname) { @@ -413,8 +674,12 @@ wlan_modevent(module_t mod, int type, void *unused) case MOD_LOAD: if (bootverbose) printf("wlan: <802.11 Link Layer>\n"); + if_clone_attach(&wlan_cloner); + if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free); return 0; case MOD_UNLOAD: + if_deregister_com_alloc(IFT_IEEE80211); + if_clone_detach(&wlan_cloner); return 0; } return EINVAL; diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 0052dfe..bccc0cf 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,12 +28,18 @@ #define _NET80211_IEEE80211_FREEBSD_H_ #ifdef _KERNEL +#include <sys/param.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/rwlock.h> + /* * Common state locking definitions. */ typedef struct mtx ieee80211_com_lock_t; #define IEEE80211_LOCK_INIT(_ic, _name) \ - mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF) + mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", \ + MTX_DEF | MTX_RECURSE) #define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock) #define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_comlock) #define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_comlock) @@ -41,43 +47,61 @@ typedef struct mtx ieee80211_com_lock_t; mtx_assert(&(_ic)->ic_comlock, MA_OWNED) /* - * Beacon locking definitions. - */ -typedef struct mtx ieee80211_beacon_lock_t; -#define IEEE80211_BEACON_LOCK_INIT(_ic, _name) \ - mtx_init(&(_ic)->ic_beaconlock, _name, "802.11 beacon lock", MTX_DEF) -#define IEEE80211_BEACON_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_beaconlock) -#define IEEE80211_BEACON_LOCK(_ic) mtx_lock(&(_ic)->ic_beaconlock) -#define IEEE80211_BEACON_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_beaconlock) -#define IEEE80211_BEACON_LOCK_ASSERT(_ic) \ - mtx_assert(&(_ic)->ic_beaconlock, MA_OWNED) - -/* * Node locking definitions. - * NB: MTX_DUPOK is because we don't generate per-interface strings. */ -typedef struct mtx ieee80211_node_lock_t; -#define IEEE80211_NODE_LOCK_INIT(_nt, _name) \ - mtx_init(&(_nt)->nt_nodelock, _name, "802.11 node table", \ - MTX_DEF | MTX_DUPOK) -#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock) -#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_nt)->nt_nodelock) -#define IEEE80211_NODE_IS_LOCKED(_nt) mtx_owned(&(_nt)->nt_nodelock) -#define IEEE80211_NODE_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_nodelock) -#define IEEE80211_NODE_LOCK_ASSERT(_nt) \ - mtx_assert(&(_nt)->nt_nodelock, MA_OWNED) +typedef struct { + char name[16]; /* e.g. "ath0_node_lock" */ + struct mtx mtx; +} ieee80211_node_lock_t; +#define IEEE80211_NODE_LOCK_INIT(_nt, _name) do { \ + ieee80211_node_lock_t *nl = &(_nt)->nt_nodelock; \ + snprintf(nl->name, sizeof(nl->name), "%s_node_lock", _name); \ + mtx_init(&nl->mtx, NULL, nl->name, MTX_DEF | MTX_RECURSE); \ +} while (0) +#define IEEE80211_NODE_LOCK_DESTROY(_nt) \ + mtx_destroy(&(_nt)->nt_nodelock.mtx) +#define IEEE80211_NODE_LOCK(_nt) \ + mtx_lock(&(_nt)->nt_nodelock.mtx) +#define IEEE80211_NODE_IS_LOCKED(_nt) \ + mtx_owned(&(_nt)->nt_nodelock.mtx) +#define IEEE80211_NODE_UNLOCK(_nt) \ + mtx_unlock(&(_nt)->nt_nodelock.mtx) +#define IEEE80211_NODE_LOCK_ASSERT(_nt) \ + mtx_assert(&(_nt)->nt_nodelock.mtx, MA_OWNED) /* - * Node table scangen locking definitions. + * Node table iteration locking definitions; this protects the + * scan generation # used to iterate over the station table + * while grabbing+releasing the node lock. */ -typedef struct mtx ieee80211_scan_lock_t; -#define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \ - mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node scangen", MTX_DEF) -#define IEEE80211_SCAN_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_scanlock) -#define IEEE80211_SCAN_LOCK(_nt) mtx_lock(&(_nt)->nt_scanlock) -#define IEEE80211_SCAN_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_scanlock) -#define IEEE80211_SCAN_LOCK_ASSERT(_nt) \ - mtx_assert(&(_nt)->nt_scanlock, MA_OWNED) +typedef struct { + char name[16]; /* e.g. "ath0_scan_lock" */ + struct mtx mtx; +} ieee80211_scan_lock_t; +#define IEEE80211_NODE_ITERATE_LOCK_INIT(_nt, _name) do { \ + ieee80211_scan_lock_t *sl = &(_nt)->nt_scanlock; \ + snprintf(sl->name, sizeof(sl->name), "%s_scan_lock", _name); \ + mtx_init(&sl->mtx, NULL, sl->name, MTX_DEF); \ +} while (0) +#define IEEE80211_NODE_ITERATE_LOCK_DESTROY(_nt) \ + mtx_destroy(&(_nt)->nt_scanlock.mtx) +#define IEEE80211_NODE_ITERATE_LOCK(_nt) \ + mtx_lock(&(_nt)->nt_scanlock.mtx) +#define IEEE80211_NODE_ITERATE_UNLOCK(_nt) \ + mtx_unlock(&(_nt)->nt_scanlock.mtx) + +#define _AGEQ_ENQUEUE(_ifq, _m, _qlen, _age) do { \ + (_m)->m_nextpkt = NULL; \ + if ((_ifq)->ifq_tail != NULL) { \ + _age -= M_AGE_GET((_ifq)->ifq_tail); \ + (_ifq)->ifq_tail->m_nextpkt = (_m); \ + } else { \ + (_ifq)->ifq_head = (_m); \ + } \ + M_AGE_SET(_m, _age); \ + (_ifq)->ifq_tail = (_m); \ + (_qlen) = ++(_ifq)->ifq_len; \ +} while (0) /* * Per-node power-save queue definitions. @@ -113,16 +137,7 @@ typedef struct mtx ieee80211_scan_lock_t; _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; \ + _AGEQ_ENQUEUE(&ni->ni_savedq, _m, _qlen, _age); \ } while (0) #define IEEE80211_TAPQ_INIT(_tap) do { \ @@ -147,6 +162,24 @@ typedef struct mtx ieee80211_scan_lock_t; } while (0) #endif /* IF_PREPEND_LIST */ +/* XXX temporary */ +#define IEEE80211_NODE_WDSQ_INIT(_ni, _name) do { \ + mtx_init(&(_ni)->ni_wdsq.ifq_mtx, _name, "802.11 wds queue", MTX_DEF);\ + (_ni)->ni_wdsq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \ +} while (0) +#define IEEE80211_NODE_WDSQ_DESTROY(_ni) do { \ + mtx_destroy(&(_ni)->ni_wdsq.ifq_mtx); \ +} while (0) +#define IEEE80211_NODE_WDSQ_QLEN(_ni) _IF_QLEN(&(_ni)->ni_wdsq) +#define IEEE80211_NODE_WDSQ_LOCK(_ni) IF_LOCK(&(_ni)->ni_wdsq) +#define IEEE80211_NODE_WDSQ_UNLOCK(_ni) IF_UNLOCK(&(_ni)->ni_wdsq) +#define _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(_ni, _m) do { \ + _IF_DEQUEUE(&(_ni)->ni_wdsq, m); \ +} while (0) +#define _IEEE80211_NODE_WDSQ_ENQUEUE(_ni, _m, _qlen, _age) do { \ + _AGEQ_ENQUEUE(&ni->ni_wdsq, _m, _qlen, _age); \ +} while (0) + /* * 802.1x MAC ACL database locking definitions. */ @@ -182,43 +215,53 @@ int ieee80211_node_dectestref(struct ieee80211_node *ni); #define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt struct ifqueue; +struct ieee80211vap; void ieee80211_drain_ifq(struct ifqueue *); +void ieee80211_flush_ifq(struct ifqueue *, struct ieee80211vap *); + +void ieee80211_vap_destroy(struct ieee80211vap *); + +#define IFNET_IS_UP_RUNNING(_ifp) \ + (((_ifp)->if_flags & IFF_UP) && \ + ((_ifp)->if_drv_flags & IFF_DRV_RUNNING)) #define msecs_to_ticks(ms) (((ms)*hz)/1000) -#define ticks_to_msecs(t) ((t) / hz) +#define ticks_to_msecs(t) (1000*(t) / hz) +#define ticks_to_secs(t) ((t) / hz) #define time_after(a,b) ((long)(b) - (long)(a) < 0) #define time_before(a,b) time_after(b,a) #define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) #define time_before_eq(a,b) time_after_eq(b,a) +#define memmove(dst, src, n) ovbcopy(src, dst, n) + struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); /* tx path usage */ #define M_LINK0 M_PROTO1 /* WEP requested */ +#define M_WDS M_PROTO2 /* WDS frame */ +#define M_EAPOL M_PROTO3 /* PAE/EAPOL frame */ #define M_PWR_SAV M_PROTO4 /* bypass PS handling */ #define M_MORE_DATA M_PROTO5 /* more data frames to follow */ -#define M_FF 0x20000 /* fast frame */ -#define M_TXCB 0x40000 /* do tx complete callback */ -#define M_80211_TX (0x60000|M_PROTO1|M_WME_AC_MASK|M_PROTO4|M_PROTO5) +#define M_FF M_PROTO6 /* fast frame */ +#define M_TXCB M_PROTO7 /* do tx complete callback */ +#define M_80211_TX \ + (M_LINK0|M_WDS|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB) /* rx path usage */ #define M_AMPDU M_PROTO1 /* A-MPDU processing done */ #define M_WEP M_PROTO2 /* WEP done by hardware */ #define M_80211_RX (M_AMPDU|M_WEP) /* - * 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. + * Store WME access control bits in the vlan tag. + * This is safe since it's done after the packet is classified + * (where we use any previous tag) and because 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) + ((m)->m_pkthdr.ether_vtag = (ac)) +#define M_WME_GETAC(m) ((m)->m_pkthdr.ether_vtag) /* * Mbufs on the power save queue are tagged with an age and @@ -246,16 +289,29 @@ struct ieee80211com; void ieee80211_sysctl_attach(struct ieee80211com *); void ieee80211_sysctl_detach(struct ieee80211com *); +void ieee80211_sysctl_vattach(struct ieee80211vap *); +void ieee80211_sysctl_vdetach(struct ieee80211vap *); void ieee80211_load_module(const char *); -#define IEEE80211_CRYPTO_MODULE(name, version) \ +/* + * A "policy module" is an adjunct module to net80211 that provides + * functionality that typically includes policy decisions. This + * modularity enables extensibility and vendor-supplied functionality. + */ +#define _IEEE80211_POLICY_MODULE(policy, name, version) \ +typedef void (*policy##_setup)(int); \ +SET_DECLARE(policy##_set, policy##_setup); \ static int \ -name##_modevent(module_t mod, int type, void *unused) \ +wlan_##name##_modevent(module_t mod, int type, void *unused) \ { \ + policy##_setup * const *iter, f; \ switch (type) { \ case MOD_LOAD: \ - ieee80211_crypto_register(&name); \ + SET_FOREACH(iter, policy##_set) { \ + f = (void*) *iter; \ + f(type); \ + } \ return 0; \ case MOD_UNLOAD: \ case MOD_QUIESCE: \ @@ -264,20 +320,100 @@ name##_modevent(module_t mod, int type, void *unused) \ nrefs); \ return EBUSY; \ } \ - if (type == MOD_UNLOAD) \ - ieee80211_crypto_unregister(&name); \ + if (type == MOD_UNLOAD) { \ + SET_FOREACH(iter, policy##_set) { \ + f = (void*) *iter; \ + f(type); \ + } \ + } \ return 0; \ } \ return EINVAL; \ } \ static moduledata_t name##_mod = { \ "wlan_" #name, \ - name##_modevent, \ + wlan_##name##_modevent, \ 0 \ }; \ DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\ MODULE_VERSION(wlan_##name, version); \ MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1) + +/* + * Crypto modules implement cipher support. + */ +#define IEEE80211_CRYPTO_MODULE(name, version) \ +_IEEE80211_POLICY_MODULE(crypto, name, version); \ +static void \ +name##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_crypto_register(&name); \ + else \ + ieee80211_crypto_unregister(&name); \ +} \ +TEXT_SET(crypto##_set, name##_modevent) + +/* + * Scanner modules provide scanning policy. + */ +#define IEEE80211_SCANNER_MODULE(name, version) \ + _IEEE80211_POLICY_MODULE(scanner, name, version) + +#define IEEE80211_SCANNER_ALG(name, alg, v) \ +static void \ +name##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_scanner_register(alg, &v); \ + else \ + ieee80211_scanner_unregister(alg, &v); \ +} \ +TEXT_SET(scanner_set, name##_modevent); \ + +/* + * ACL modules implement acl policy. + */ +#define IEEE80211_ACL_MODULE(name, alg, version) \ +_IEEE80211_POLICY_MODULE(acl, name, version); \ +static void \ +alg##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_aclator_register(&alg); \ + else \ + ieee80211_aclator_unregister(&alg); \ +} \ +TEXT_SET(acl_set, alg##_modevent); \ + +/* + * Authenticator modules handle 802.1x/WPA authentication. + */ +#define IEEE80211_AUTH_MODULE(name, version) \ + _IEEE80211_POLICY_MODULE(auth, name, version) + +#define IEEE80211_AUTH_ALG(name, alg, v) \ +static void \ +name##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_authenticator_register(alg, &v); \ + else \ + ieee80211_authenticator_unregister(alg); \ +} \ +TEXT_SET(auth_set, name##_modevent) + +/* + * Rate control modules provide tx rate control support. + */ +#define IEEE80211_RATE_MODULE(alg, version) \ +_IEEE80211_POLICY_MODULE(rate, alg, version); \ +static void \ +alg##_modevent(int type) \ +{ \ + /* XXX nothing to do until the rate control framework arrives */\ +} \ +TEXT_SET(rate##_set, alg##_modevent) #endif /* _KERNEL */ /* XXX this stuff belongs elsewhere */ @@ -310,6 +446,50 @@ struct ieee80211_michael_event { uint8_t iev_keyix; /* key id/index */ }; +struct ieee80211_wds_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_csa_event { + uint32_t iev_flags; /* channel flags */ + uint16_t iev_freq; /* setting in Mhz */ + uint8_t iev_ieee; /* IEEE channel number */ + uint8_t iev_mode; /* CSA mode */ + uint8_t iev_count; /* CSA count */ +}; + +struct ieee80211_cac_event { + uint32_t iev_flags; /* channel flags */ + uint16_t iev_freq; /* setting in Mhz */ + uint8_t iev_ieee; /* IEEE channel number */ + /* XXX timestamp? */ + uint8_t iev_type; /* IEEE80211_NOTIFY_CAC_* */ +}; + +struct ieee80211_radar_event { + uint32_t iev_flags; /* channel flags */ + uint16_t iev_freq; /* setting in Mhz */ + uint8_t iev_ieee; /* IEEE channel number */ + /* XXX timestamp? */ +}; + +struct ieee80211_auth_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_deauth_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_country_event { + uint8_t iev_addr[6]; + uint8_t iev_cc[2]; /* ISO country code */ +}; + +struct ieee80211_radio_event { + uint8_t iev_state; /* 1 on, 0 off */ +}; + #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) */ @@ -319,6 +499,14 @@ struct ieee80211_michael_event { #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) */ +#define RTM_IEEE80211_WDS 109 /* WDS discovery (ap mode) */ +#define RTM_IEEE80211_CSA 110 /* Channel Switch Announcement event */ +#define RTM_IEEE80211_RADAR 111 /* radar event */ +#define RTM_IEEE80211_CAC 112 /* Channel Availability Check event */ +#define RTM_IEEE80211_DEAUTH 113 /* station deauthenticate */ +#define RTM_IEEE80211_AUTH 114 /* station authenticate (ap mode) */ +#define RTM_IEEE80211_COUNTRY 115 /* discovered country code (sta mode) */ +#define RTM_IEEE80211_RADIO 116 /* RF kill switch state change */ /* * Structure prepended to raw packets sent through the bpf diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c new file mode 100644 index 0000000..3c15505 --- /dev/null +++ b/sys/net80211/ieee80211_hostap.c @@ -0,0 +1,2236 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 HOSTAP mode support. + */ +#include "opt_inet.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_llc.h> +#include <net/ethernet.h> + +#include <net/bpf.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_hostap.h> +#include <net80211/ieee80211_input.h> +#include <net80211/ieee80211_wds.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +static void hostap_vattach(struct ieee80211vap *); +static int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int hostap_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp); +static void hostap_deliver_data(struct ieee80211vap *, + struct ieee80211_node *, struct mbuf *); +static void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *, + int subtype, int rssi, int noise, uint32_t rstamp); +static void hostap_recv_pspoll(struct ieee80211_node *, struct mbuf *); + +void +ieee80211_hostap_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach; +} + +void +ieee80211_hostap_detach(struct ieee80211com *ic) +{ +} + +static void +hostap_vdetach(struct ieee80211vap *vap) +{ +} + +static void +hostap_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = hostap_newstate; + vap->iv_input = hostap_input; + vap->iv_recv_mgmt = hostap_recv_mgmt; + vap->iv_opdetach = hostap_vdetach; + vap->iv_deliver_data = hostap_deliver_data; +} + +static void +sta_disassoc(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = arg; + + if (ni->ni_vap == vap && ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_node_leave(ni); + } +} + +/* + * IEEE80211_M_HOSTAP vap state machine handler. + */ +static int +hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + enum ieee80211_state ostate; + + IEEE80211_LOCK_ASSERT(ic); + + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate], arg); + vap->iv_state = nstate; /* state transition */ + if (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(vap); /* background scan */ + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + case IEEE80211_S_CAC: + ieee80211_dfs_cac_stop(vap); + break; + case IEEE80211_S_RUN: + ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap); + break; + default: + break; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + } + if (vap->iv_auth->ia_detach != NULL) + vap->iv_auth->ia_detach(vap); + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_CSA: + case IEEE80211_S_RUN: + ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap); + /* + * Clear overlapping BSS state; the beacon frame + * will be reconstructed on transition to the RUN + * state and the timeout routines check if the flag + * is set before doing anything so this is sufficient. + */ + ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; + ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR; + /* fall thru... */ + case IEEE80211_S_CAC: + /* + * NB: We may get here because of a manual channel + * change in which case we need to stop CAC + * XXX no need to stop if ostate RUN but it's ok + */ + ieee80211_dfs_cac_stop(vap); + /* fall thru... */ + case IEEE80211_S_INIT: + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && + !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { + /* + * Already have a channel; bypass the + * scan and startup immediately. + * ieee80211_create_ibss will call back to + * move us to RUN state. + */ + ieee80211_create_ibss(vap, vap->iv_des_chan); + break; + } + /* + * Initiate a scan. We can come here as a result + * of an IEEE80211_IOC_SCAN_REQ too in which case + * the vap will be marked with IEEE80211_FEXT_SCANREQ + * and the scan request parameters will be present + * in iv_scanreq. Otherwise we do the default. + */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { + ieee80211_check_scan(vap, + vap->iv_scanreq_flags, + vap->iv_scanreq_duration, + vap->iv_scanreq_mindwell, + vap->iv_scanreq_maxdwell, + vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; + } else + ieee80211_check_scan_current(vap); + break; + case IEEE80211_S_SCAN: + /* + * A state change requires a reset; scan. + */ + ieee80211_check_scan_current(vap); + break; + default: + break; + } + break; + case IEEE80211_S_CAC: + /* + * Start CAC on a DFS channel. We come here when starting + * a bss on a DFS channel (see ieee80211_create_ibss). + */ + ieee80211_dfs_cac_start(vap); + break; + case IEEE80211_S_RUN: + if (vap->iv_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } + switch (ostate) { + case IEEE80211_S_INIT: + /* + * Already have a channel; bypass the + * scan and startup immediately. + * Note that ieee80211_create_ibss will call + * back to do a RUN->RUN state change. + */ + ieee80211_create_ibss(vap, + ieee80211_ht_adjust_channel(ic, + ic->ic_curchan, vap->iv_flags_ext)); + /* NB: iv_bss is changed on return */ + break; + case IEEE80211_S_CAC: + /* + * NB: This is the normal state change when CAC + * expires and no radar was detected; no need to + * clear the CAC timer as it's already expired. + */ + /* fall thru... */ + case IEEE80211_S_CSA: + /* + * Update bss node channel to reflect where + * we landed after CSA. + */ + ieee80211_node_set_chan(vap->iv_bss, + ieee80211_ht_adjust_channel(ic, ic->ic_curchan, + ieee80211_htchanflags(vap->iv_bss->ni_chan))); + /* XXX bypass debug msgs */ + break; + case IEEE80211_S_SCAN: + case IEEE80211_S_RUN: +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap)) { + struct ieee80211_node *ni = vap->iv_bss; + ieee80211_note(vap, + "synchronized with %s ssid ", + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(ni->ni_essid, + ni->ni_esslen); + /* XXX MCS/HT */ + printf(" channel %d start %uMb\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), + IEEE80211_RATE2MBS(ni->ni_txrate)); + } +#endif + break; + default: + break; + } + /* + * Start/stop the authenticator. We delay until here + * to allow configuration to happen out of order. + */ + if (vap->iv_auth->ia_attach != NULL) { + /* XXX check failure */ + vap->iv_auth->ia_attach(vap); + } else if (vap->iv_auth->ia_detach != NULL) { + vap->iv_auth->ia_detach(vap); + } + ieee80211_node_authorize(vap->iv_bss); + break; + default: + break; + } + return 0; +} + +static void +hostap_deliver_data(struct ieee80211vap *vap, + struct ieee80211_node *ni, struct mbuf *m) +{ + struct ether_header *eh = mtod(m, struct ether_header *); + struct ifnet *ifp = vap->iv_ifp; + + KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, + ("gack, opmode %d", vap->iv_opmode)); + /* + * Do accounting. + */ + ifp->if_ipackets++; + IEEE80211_NODE_STAT(ni, rx_data); + IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + m->m_flags |= M_MCAST; /* XXX M_BCAST? */ + IEEE80211_NODE_STAT(ni, rx_mcast); + } else + IEEE80211_NODE_STAT(ni, rx_ucast); + + /* clear driver/net80211 flags before passing up */ + m->m_flags &= ~M_80211_RX; + + /* perform as a bridge within the AP */ + if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) { + struct mbuf *mcopy = NULL; + + if (m->m_flags & M_MCAST) { + mcopy = m_copypacket(m, M_DONTWAIT); + if (mcopy == NULL) + ifp->if_oerrors++; + else + mcopy->m_flags |= M_MCAST; + } else { + /* + * Check if the destination is associated with the + * same vap and authorized to receive traffic. + * Beware of traffic destined for the vap itself; + * sending it will not work; just let it be delivered + * normally. + */ + struct ieee80211_node *sta = ieee80211_find_vap_node( + &vap->iv_ic->ic_sta, vap, 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 != vap->iv_bss) { + mcopy = m; + m = NULL; + } + } else { + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(sta, rx_unauth); + } + ieee80211_free_node(sta); + } + } + if (mcopy != NULL) { + int len, err; + len = mcopy->m_pkthdr.len; + IFQ_HANDOFF(ifp, mcopy, err); + if (err) { + /* NB: IFQ_HANDOFF reclaims mcopy */ + } else { + ifp->if_opackets++; + } + } + } + if (m != NULL) { + /* + * Mark frame as coming from vap's interface. + */ + m->m_pkthdr.rcvif = ifp; + if (m->m_flags & M_MCAST) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + if (ni->ni_vlan != 0) { + /* attach vlan tag */ + m->m_pkthdr.ether_vtag = ni->ni_vlan; + m->m_flags |= M_VLANTAG; + } + ifp->if_input(ifp, m); + } +} + +/* + * 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 ieee80211vap *vap, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return 0; + } + return 1; +} + +/* + * Process a received frame. The node associated with the sender + * should be supplied. If nothing was found in the node table then + * the caller is assumed to supply a reference to iv_bss instead. + * The RSSI and a timestamp are also supplied. The RSSI data is used + * during AP scanning to select a AP to associate with; it can have + * any units so long as values have consistent units and higher values + * mean ``better signal''. The receive timestamp is currently not used + * by the 802.11 layer. + */ +static int +hostap_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp) +{ +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct ether_header *eh; + int hdrspace, need_tap; + uint8_t dir, type, subtype, qos; + uint8_t *bssid; + uint16_t rxseq; + + if (m->m_flags & M_AMPDU) { + /* + * Fastpath for A-MPDU reorder q resubmission. Frames + * w/ M_AMPDU marked have already passed through here + * but were received out of order and been held on the + * reorder queue. When resubmitted they are marked + * with the M_AMPDU flag and we can bypass most of the + * normal processing. + */ + wh = mtod(m, struct ieee80211_frame *); + type = IEEE80211_FC0_TYPE_DATA; + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + subtype = IEEE80211_FC0_SUBTYPE_QOS; + hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ + goto resubmit_ampdu; + } + + KASSERT(ni != NULL, ("null node")); + ni->ni_inact = ni->ni_inact_reload; + + need_tap = 1; /* mbuf need to be tapped. */ + type = -1; /* undefined */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_pkthdr.len); + vap->iv_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) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); + vap->iv_stats.is_rx_badversion++; + goto err; + } + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + 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(vap, + IEEE80211_MSG_ANY, ni->ni_macaddr, + NULL, "too short (2): len %u", + m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } + bssid = wh->i_addr3; + } + /* + * Validate the bssid. + */ + if (!(type == IEEE80211_FC0_TYPE_MGT && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) && + !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && + !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { + /* not interested in */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); + vap->iv_stats.is_rx_wrongbss++; + goto out; + } + + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + if (HAS_SEQ(type)) { + uint8_t tid = ieee80211_gettid(wh); + if (IEEE80211_QOS_HAS_SEQ(wh) && + TID_TO_WME_AC(tid) >= WME_AC_VI) + ic->ic_wme.wme_hipri_traffic++; + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && + (wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { + /* duplicate, discard */ + IEEE80211_DISCARD_MAC(vap, 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); + vap->iv_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + goto out; + } + ni->ni_rxseqs[tid] = rxseq; + } + } + + 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(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "data too short: expecting %u", hdrspace); + vap->iv_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + if (!(dir == IEEE80211_FC1_DIR_TODS || + (dir == IEEE80211_FC1_DIR_DSTODS && + (vap->iv_flags & IEEE80211_F_DWDS)))) { + if (dir != IEEE80211_FC1_DIR_DSTODS) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT, wh, "data", + "incorrect dir 0x%x", dir); + } else { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT | + IEEE80211_MSG_WDS, wh, + "4-address data", + "%s", "DWDS not enabled"); + } + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + /* check if source STA is associated */ + if (ni == vap->iv_bss) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown src"); + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_AUTHED); + vap->iv_stats.is_rx_notassoc++; + goto err; + } + if (ni->ni_associd == 0) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unassoc src"); + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_NOT_ASSOCED); + vap->iv_stats.is_rx_notassoc++; + goto err; + } + + /* + * Check for power save state change. + * XXX out-of-order A-MPDU frames? + */ + 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); + /* + * For 4-address packets handle WDS discovery + * notifications. Once a WDS link is setup frames + * are just delivered to the WDS vap (see below). + */ + if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) { + if (!ieee80211_node_is_authorized(ni)) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT | + IEEE80211_MSG_WDS, wh, + "4-address data", + "%s", "unauthorized port"); + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + ieee80211_dwds_discover(ni, m); + return type; + } + + /* + * Handle A-MPDU re-ordering. The station must be + * associated and negotiated HT. The frame must be + * a QoS frame (not QoS null data) and not previously + * processed for A-MPDU re-ordering. If the frame is + * to be processed directly then ieee80211_ampdu_reorder + * will return 0; otherwise it has consumed the mbuf + * and we should do nothing more with it. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + subtype == IEEE80211_FC0_SUBTYPE_QOS && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + + /* + * 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 ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "WEP", "%s", "PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + IEEE80211_NODE_STAT(ni, rx_noprivacy); + goto out; + } + key = ieee80211_crypto_decap(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 { + /* XXX M_WEP and IEEE80211_F_PRIVACY */ + key = NULL; + } + + /* + * Save QoS bits for use below--before we strip the header. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { + qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? + ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : + ((struct ieee80211_qosframe *)wh)->i_qos[0]; + } else + qos = 0; + + /* + * Next up, any fragmentation. + */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + m = ieee80211_defrag(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(vap, key, m, 0)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "demic error"); + vap->iv_stats.is_rx_demicfail++; + IEEE80211_NODE_STAT(ni, rx_demicfail); + goto out; + } + /* copy to listener after decrypt */ + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + need_tap = 0; + /* + * Finally, strip the 802.11 header. + */ + m = ieee80211_decap(vap, m, hdrspace); + if (m == NULL) { + /* XXX mask bit to check for both */ + /* don't count Null data frames as errors */ + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || + subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) + goto out; + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "decap error"); + vap->iv_stats.is_rx_decap++; + IEEE80211_NODE_STAT(ni, rx_decap); + goto err; + } + 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(vap, IEEE80211_MSG_INPUT, + eh->ether_shost, "data", + "unauthorized port: ether type 0x%x len %u", + eh->ether_type, m->m_pkthdr.len); + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && + (key == NULL && (m->m_flags & M_WEP) == 0) && + eh->ether_type != htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + vap->iv_stats.is_rx_unencrypted++; + IEEE80211_NODE_STAT(ni, rx_unencrypted); + goto out; + } + } + /* XXX require HT? */ + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + m->m_pkthdr.len >= 3*FF_LLC_SIZE) { + struct llc *llc; + + /* + * Check for fast-frame tunnel encapsulation. + */ + if (m->m_len < FF_LLC_SIZE && + (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "m_pullup(llc) failed"); + vap->iv_stats.is_rx_tooshort++; + return IEEE80211_FC0_TYPE_DATA; + } + llc = (struct llc *)(mtod(m, uint8_t *) + + sizeof(struct ether_header)); + if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { + m_adj(m, FF_LLC_SIZE); + m = ieee80211_decap_fastframe(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + } +#undef FF_LLC_SIZE + if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL) + ieee80211_deliver_data(ni->ni_wdsvap, ni, m); + else + hostap_deliver_data(vap, ni, m); + return IEEE80211_FC0_TYPE_DATA; + + case IEEE80211_FC0_TYPE_MGT: + vap->iv_stats.is_rx_mgmt++; + IEEE80211_NODE_STAT(ni, rx_mgmt); + if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "mgt", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto err; + } + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "mgt", "too short: len %u", + m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } + if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { + /* ensure return frames are unicast */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "source is multicast: %s", + ether_sprintf(wh->i_addr2)); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ + goto out; + } +#ifdef IEEE80211_DEBUG + if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || + ieee80211_msg_dumppkts(vap)) { + if_printf(ifp, "received %s from %s rssi %d\n", + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(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(vap, IEEE80211_MSG_INPUT, + wh, NULL, + "%s", "WEP set but not permitted"); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "WEP set but PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + goto out; + } + hdrspace = ieee80211_hdrspace(ic, wh); + key = ieee80211_crypto_decap(ni, m, hdrspace); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); + m_freem(m); + return IEEE80211_FC0_TYPE_MGT; + + case IEEE80211_FC0_TYPE_CTL: + vap->iv_stats.is_rx_ctl++; + IEEE80211_NODE_STAT(ni, rx_ctrl); + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PS_POLL: + hostap_recv_pspoll(ni, m); + break; + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; + } + goto out; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "bad", "frame type 0x%x", type); + /* should not come here */ + break; + } +err: + ifp->if_ierrors++; +out: + if (m != NULL) { + if (bpf_peers_present(vap->iv_rawbpf) && need_tap) + bpf_mtap(vap->iv_rawbpf, m); + m_freem(m); + } + return type; +#undef SEQ_LEQ +} + +static void +hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, + int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); + + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "open auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_stats.is_rx_bad_auth++; /* XXX */ + /* + * Clear any challenge text that may be there if + * a previous shared key auth failed and then an + * open auth is attempted. + */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + /* XXX hack to workaround calling convention */ + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (IEEE80211_STATUS_ALG<<16)); + return; + } + if (seq != IEEE80211_AUTH_OPEN_REQUEST) { + vap->iv_stats.is_rx_bad_auth++; + return; + } + /* always accept open authentication requests */ + if (ni == vap->iv_bss) { + ni = ieee80211_dup_bss(vap, 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; + + if (vap->iv_acl != NULL && + vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { + /* + * When the ACL policy is set to RADIUS we defer the + * authorization to a user agent. Dispatch an event, + * a subsequent MLME call will decide the fate of the + * station. If the user agent is not present then the + * node will be reclaimed due to inactivity. + */ + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr, + "%s", "station authentication defered (radius acl)"); + ieee80211_notify_node_auth(ni); + } else { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr, + "%s", "station authenticated (open)"); + /* + * 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); + } +} + +static void +hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, + uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp, + uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + uint8_t *challenge; + int allocbs, estatus; + + KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); + + /* + * 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 ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DISCARD_MAC(vap, 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 Mac OS X). + */ + if (ni->ni_authmode != IEEE80211_AUTH_AUTO && + ni->ni_authmode != IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_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(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "ie %d/%d too long", + frm[0], (frm[1] + 2) - (efrm - frm)); + vap->iv_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(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", "no challenge"); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad challenge len %d", challenge[1]); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_REQUEST: + if (ni == vap->iv_bss) { + ni = ieee80211_dup_bss(vap, 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; + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + if (!ieee80211_alloc_challenge(ni)) { + /* NB: don't return error so they rexmit */ + return; + } + get_random_bytes(ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ni, "shared key %sauth request", allocbs ? "" : "re"); + /* + * When the ACL policy is set to RADIUS we defer the + * authorization to a user agent. Dispatch an event, + * a subsequent MLME call will decide the fate of the + * station. If the user agent is not present then the + * node will be reclaimed due to inactivity. + */ + if (vap->iv_acl != NULL && + vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, + ni->ni_macaddr, + "%s", "station authentication defered (radius acl)"); + ieee80211_notify_node_auth(ni); + return; + } + break; + case IEEE80211_AUTH_SHARED_RESPONSE: + if (ni == vap->iv_bss) { + IEEE80211_DISCARD_MAC(vap, 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(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "no challenge recorded"); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (memcmp(ni->ni_challenge, &challenge[2], + challenge[1]) != 0) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "challenge mismatch"); + vap->iv_stats.is_rx_auth_fail++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ni, "%s", "station authenticated (shared key)"); + ieee80211_node_authorize(ni); + break; + default: + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad seq %d", seq); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_SEQUENCE; + goto bad; + } + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + return; +bad: + /* + * Send an error response; but only when operating as an AP. + */ + /* XXX hack to workaround calling convention */ + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (estatus<<16)); +} + +/* + * Convert a WPA cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +wpa_cipher(const 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(const 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. + * Note that we do not validate security parameters; that + * is handled by the authenticator; the parsing done here + * is just for internal use in making operational decisions. + */ +static int +ieee80211_parse_wpa(struct ieee80211vap *vap, const 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 ((vap->iv_flags & IEEE80211_F_WPA1) == 0) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags); + return IEEE80211_REASON_IE_INVALID; + } + if (len < 14) { + IEEE80211_DISCARD_IE(vap, + 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(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + memset(rsn, 0, sizeof(*rsn)); + + /* multicast/group cipher */ + rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen); + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(vap, + 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<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); + frm += 4, len -= 4; + } + if (w & (1<<IEEE80211_CIPHER_TKIP)) + rsn->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(vap, + 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; + } + 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(const 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(const 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 ieee80211vap *vap, const 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 ((vap->iv_flags & IEEE80211_F_WPA2) == 0) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags); + return IEEE80211_REASON_IE_INVALID; + } + if (len < 10) { + IEEE80211_DISCARD_IE(vap, + 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(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + memset(rsn, 0, sizeof(*rsn)); + + /* multicast/group cipher */ + rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen); + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(vap, + 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_cipher(frm, &rsn->rsn_ucastkeylen); + frm += 4, len -= 4; + } + if (w & (1<<IEEE80211_CIPHER_TKIP)) + rsn->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(vap, + 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; + } + 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; +} + +/* + * WPA/802.11i assocation request processing. + */ +static int +wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms, + const struct ieee80211_frame *wh, const uint8_t *wpa, + const uint8_t *rsn, uint16_t capinfo) +{ + struct ieee80211vap *vap = ni->ni_vap; + uint8_t reason; + int badwparsn; + + ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN); + if (wpa == NULL && rsn == NULL) { + if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) { + /* + * W-Fi Protected Setup (WPS) permits + * clients to associate and pass EAPOL frames + * to establish initial credentials. + */ + ni->ni_flags |= IEEE80211_NODE_WPS; + return 1; + } + if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) && + (capinfo & IEEE80211_CAPINFO_PRIVACY)) { + /* + * Transitional Security Network. Permits clients + * to associate and use WEP while WPA is configured. + */ + ni->ni_flags |= IEEE80211_NODE_TSN; + return 1; + } + IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + wh, NULL, "%s", "no WPA/RSN IE in association request"); + vap->iv_stats.is_rx_assoc_badwpaie++; + reason = IEEE80211_REASON_IE_INVALID; + goto bad; + } + /* assert right association security credentials */ + badwparsn = 0; /* NB: to silence compiler */ + switch (vap->iv_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + badwparsn = (wpa == NULL); + break; + case IEEE80211_F_WPA2: + badwparsn = (rsn == NULL); + break; + case IEEE80211_F_WPA1|IEEE80211_F_WPA2: + badwparsn = (wpa == NULL && rsn == NULL); + break; + } + if (badwparsn) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + wh, NULL, + "%s", "missing WPA/RSN IE in association request"); + vap->iv_stats.is_rx_assoc_badwpaie++; + reason = IEEE80211_REASON_IE_INVALID; + goto bad; + } + /* + * Parse WPA/RSN information element. + */ + if (wpa != NULL) + reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh); + else + reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh); + if (reason != 0) { + /* XXX distinguish WPA/RSN? */ + vap->iv_stats.is_rx_assoc_badwpaie++; + goto bad; + } + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni, + "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x", + wpa != NULL ? "WPA" : "RSN", + rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen, + rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen, + rsnparms->rsn_keymgmt, rsnparms->rsn_caps); + + return 1; +bad: + ieee80211_node_deauth(ni, reason); + return 0; +} + +/* 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 ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_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(vap, IEEE80211_MSG_ASSOC, ni, + "%s", "no mbuf for l2uf frame"); + vap->iv_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); + hostap_deliver_data(vap, ni, m); +} + +static void +ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int reassoc, int resp, const char *tag, int rate) +{ + IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, %s rate set mismatch, rate/MCS %d", + reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL); + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE); + ieee80211_node_leave(ni); +} + +static void +capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int reassoc, int resp, const char *tag, int capinfo) +{ + struct ieee80211vap *vap = ni->ni_vap; + + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, %s mismatch 0x%x", + reassoc ? "reassoc" : "assoc", tag, capinfo); + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO); + ieee80211_node_leave(ni); + vap->iv_stats.is_rx_assoc_capmismatch++; +} + +static void +htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int reassoc, int resp) +{ + IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc"); + /* XXX no better code */ + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_OTHER); + ieee80211_node_leave(ni); +} + +static void +authalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int algo, int seq, int status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "unsupported alg %d", algo); + vap->iv_stats.is_rx_auth_unsupported++; + ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH, + seq | (status << 16)); +} + +static __inline int +ishtmixed(const uint8_t *ie) +{ + const struct ieee80211_ie_htinfo *ht = + (const struct ieee80211_ie_htinfo *) ie; + return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) == + IEEE80211_HTINFO_OPMODE_MIXED; +} + +static int +is11bclient(const uint8_t *rates, const uint8_t *xrates) +{ + static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); + int i; + + /* NB: the 11b clients we care about will not have xrates */ + if (xrates != NULL || rates == NULL) + return 0; + for (i = 0; i < rates[1]; i++) { + int r = rates[2+i] & IEEE80211_RATE_VAL; + if (r > 2*11 || ((1<<r) & brates) == 0) + return 0; + } + return 1; +} + +static void +hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, + int subtype, int rssi, int noise, uint32_t rstamp) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm, *sfrm; + uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap; + int reassoc, resp; + 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 when scanning; + * otherwise we check beacon frames for overlapping non-ERP + * BSS in 11g and/or overlapping legacy BSS when in HT. + */ + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + /* NB: accept off-channel frames */ + if (ieee80211_parse_beacon(ni, m0, &scan) &~ IEEE80211_BPARSE_OFFCHAN) + return; + /* + * Count frame now that we know it's to be processed. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + vap->iv_stats.is_rx_beacon++; /* XXX remove */ + IEEE80211_NODE_STAT(ni, rx_beacons); + } else + IEEE80211_NODE_STAT(ni, rx_proberesp); + /* + * If scanning, just pass information to the scan module. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { + if (scan.status == 0 && /* NB: on channel */ + (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(vap, 1); + ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; + } + ieee80211_add_scan(vap, &scan, wh, + subtype, rssi, noise, rstamp); + return; + } + /* + * Check beacon for overlapping bss w/ non ERP stations. + * If we detect one and protection is configured but not + * enabled, enable it and start a timer that'll bring us + * out if we stop seeing the bss. + */ + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + scan.status == 0 && /* NB: on-channel */ + ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/ + (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) { + ic->ic_lastnonerp = ticks; + ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR; + if (ic->ic_protmode != IEEE80211_PROT_NONE && + (ic->ic_flags & IEEE80211_F_USEPROT) == 0) { + IEEE80211_NOTE_FRAME(vap, + IEEE80211_MSG_ASSOC, wh, + "non-ERP present on channel %d " + "(saw erp 0x%x from channel %d), " + "enable use of protection", + ic->ic_curchan->ic_ieee, + scan.erp, scan.chan); + ic->ic_flags |= IEEE80211_F_USEPROT; + ieee80211_notify_erp(ic); + } + } + /* + * Check beacon for non-HT station on HT channel + * and update HT BSS occupancy as appropriate. + */ + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { + if (scan.status & IEEE80211_BPARSE_OFFCHAN) { + /* + * Off control channel; only check frames + * that come in the extension channel when + * operating w/ HT40. + */ + if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) + break; + if (scan.chan != ic->ic_curchan->ic_extieee) + break; + } + if (scan.htinfo == NULL) { + ieee80211_htprot_update(ic, + IEEE80211_HTINFO_OPMODE_PROTOPT | + IEEE80211_HTINFO_NONHT_PRESENT); + } else if (ishtmixed(scan.htinfo)) { + /* XXX? take NONHT_PRESENT from beacon? */ + ieee80211_htprot_update(ic, + IEEE80211_HTINFO_OPMODE_MIXED | + IEEE80211_HTINFO_NONHT_PRESENT); + } + } + break; + } + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + ssid = rates = xrates = NULL; + sfrm = frm; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1], return); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); + IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); + if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, + "%s", "no ssid with ssid suppression enabled"); + vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ + return; + } + + /* XXX find a better class or define it's own */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, + "%s", "recv probe req"); + /* + * Some legacy 11b clients cannot hack a complete + * probe response frame. When the request includes + * only a bare-bones rate set, communicate this to + * the transmit side. + */ + ieee80211_send_proberesp(vap, wh->i_addr2, + is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); + break; + + case IEEE80211_FC0_SUBTYPE_AUTH: { + uint16_t algo, seq, status; + + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "%s", "wrong bssid"); + vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/ + return; + } + /* + * auth frame format + * [2] algorithm + * [2] sequence + * [2] status + * [tlv*] challenge + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + algo = le16toh(*(uint16_t *)frm); + seq = le16toh(*(uint16_t *)(frm + 2)); + status = le16toh(*(uint16_t *)(frm + 4)); + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, + "recv auth frame with algorithm %d seq %d", algo, seq); + /* + * Consult the ACL policy module if setup. + */ + if (vap->iv_acl != NULL && + !vap->iv_acl->iac_check(vap, wh->i_addr2)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, + wh, NULL, "%s", "disallowed by ACL"); + vap->iv_stats.is_rx_acl++; + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); + return; + } + if (vap->iv_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, + wh, NULL, "%s", "TKIP countermeasures enabled"); + vap->iv_stats.is_rx_auth_countermeasures++; + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_REASON_MIC_FAILURE); + return; + } + if (algo == IEEE80211_AUTH_ALG_SHARED) + hostap_auth_shared(ni, wh, frm + 6, efrm, rssi, + noise, rstamp, seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + hostap_auth_open(ni, wh, rssi, noise, rstamp, + seq, status); + else if (algo == IEEE80211_AUTH_ALG_LEAP) { + authalgreject(ni, wh, algo, + seq+1, IEEE80211_STATUS_ALG); + return; + } else { + /* + * We assume that an unknown algorithm is the result + * of a decryption failure on a shared key auth frame; + * return a status code appropriate for that instead + * of IEEE80211_STATUS_ALG. + * + * NB: a seq# of 4 is intentional; the decrypted + * frame likely has a bogus seq value. + */ + authalgreject(ni, wh, algo, + 4, IEEE80211_STATUS_CHALLENGE); + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { + uint16_t capinfo, lintval; + struct ieee80211_rsnparms rsnparms; + + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "%s", "wrong bssid"); + vap->iv_stats.is_rx_assoc_bss++; + return; + } + if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + reassoc = 1; + resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; + } else { + reassoc = 0; + resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; + } + if (ni == vap->iv_bss) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, sta not authenticated", + reassoc ? "reassoc" : "assoc"); + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_NOT_AUTHED); + vap->iv_stats.is_rx_assoc_notauth++; + return; + } + + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] Atheros capabilities + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); + capinfo = le16toh(*(uint16_t *)frm); frm += 2; + lintval = le16toh(*(uint16_t *)frm); frm += 2; + if (reassoc) + frm += 6; /* ignore current AP info */ + ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; + sfrm = frm; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + case IEEE80211_ELEMID_RSN: + rsn = frm; + break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + wpa = frm; + else if (iswmeinfo(frm)) + wme = frm; + else if (isatherosoui(frm)) + ath = frm; + else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + if (ishtcapoui(frm) && htcap == NULL) + htcap = frm; + } + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1], return); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); + IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); + if (htcap != NULL) { + IEEE80211_VERIFY_LENGTH(htcap[1], + htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + return); /* XXX just NULL out? */ + } + + if ((vap->iv_flags & IEEE80211_F_WPA) && + !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo)) + return; + /* discard challenge after association */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + /* NB: 802.11 spec says to ignore station's privacy bit */ + if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { + capinfomismatch(ni, wh, reassoc, resp, + "capability", capinfo); + return; + } + /* + * Disallow re-associate w/ invalid slot time setting. + */ + if (ni->ni_associd != 0 && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && + ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { + capinfomismatch(ni, wh, reassoc, resp, + "slot time", capinfo); + return; + } + rate = ieee80211_setup_rates(ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (rate & IEEE80211_RATE_BASIC) { + ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate); + vap->iv_stats.is_rx_assoc_norate++; + return; + } + /* + * 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 ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) { + ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); + vap->iv_stats.is_rx_assoc_norate++; + return; + } + /* + * Do HT rate set handling and setup HT node state. + */ + ni->ni_chan = vap->iv_bss->ni_chan; + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { + rate = ieee80211_setup_htrates(ni, htcap, + IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO | + IEEE80211_F_DOBRS); + if (rate & IEEE80211_RATE_BASIC) { + ratesetmismatch(ni, wh, reassoc, resp, + "HT", rate); + vap->iv_stats.is_ht_assoc_norate++; + return; + } + ieee80211_ht_node_init(ni, htcap); + } else if (ni->ni_flags & IEEE80211_NODE_HT) + ieee80211_ht_node_cleanup(ni); + /* + * Allow AMPDU operation only with unencrypted traffic + * or AES-CCM; the 11n spec only specifies these ciphers + * so permitting any others is undefined and can lead + * to interoperability problems. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + (((vap->iv_flags & IEEE80211_F_WPA) && + rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) || + (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, + "disallow HT use because WEP or TKIP requested, " + "capinfo 0x%x ucastcipher %d", capinfo, + rsnparms.rsn_ucastcipher); + ieee80211_ht_node_cleanup(ni); + vap->iv_stats.is_ht_assoc_downgrade++; + } + /* + * If constrained to 11n-only stations reject legacy stations. + */ + if ((vap->iv_flags_ext & IEEE80211_FEXT_PUREN) && + (ni->ni_flags & IEEE80211_NODE_HT) == 0) { + htcapmismatch(ni, wh, reassoc, resp); + vap->iv_stats.is_ht_assoc_nohtcap++; + return; + } + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + ni->ni_intval = lintval; + ni->ni_capinfo = capinfo; + ni->ni_fhdwell = vap->iv_bss->ni_fhdwell; + ni->ni_fhindex = vap->iv_bss->ni_fhindex; + /* + * Store the IEs. + * XXX maybe better to just expand + */ + if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) { +#define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off) + if (wpa != NULL) + setie(wpa_ie, wpa - sfrm); + if (rsn != NULL) + setie(rsn_ie, rsn - sfrm); + if (htcap != NULL) + setie(htcap_ie, htcap - sfrm); + if (wme != NULL) { + setie(wme_ie, wme - sfrm); + /* + * Mark node as capable of QoS. + */ + ni->ni_flags |= IEEE80211_NODE_QOS; + } else + ni->ni_flags &= ~IEEE80211_NODE_QOS; + if (ath != NULL) { + setie(ath_ie, ath - sfrm); + /* + * Parse ATH station parameters. + */ + ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); + } else + ni->ni_ath_flags = 0; +#undef setie + } else { + ni->ni_flags &= ~IEEE80211_NODE_QOS; + ni->ni_ath_flags = 0; + } + ieee80211_node_join(ni, resp); + ieee80211_deliver_l2uf(ni); + break; + } + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_DISASSOC: { + uint16_t reason; + + if (vap->iv_state != IEEE80211_S_RUN || + /* NB: can happen when in promiscuous mode */ + !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + /* + * deauth/disassoc frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); + if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) { + vap->iv_stats.is_rx_deauth++; + IEEE80211_NODE_STAT(ni, rx_deauth); + } else { + vap->iv_stats.is_rx_disassoc++; + IEEE80211_NODE_STAT(ni, rx_disassoc); + } + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "recv %s (reason %d)", ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], reason); + if (ni != vap->iv_bss) + ieee80211_node_leave(ni); + break; + } + + case IEEE80211_FC0_SUBTYPE_ACTION: + if (vap->iv_state == IEEE80211_S_RUN) { + if (ieee80211_parse_action(ni, m0) == 0) + ic->ic_recv_action(ni, frm, efrm); + } else + vap->iv_stats.is_rx_mgtdiscard++; + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); + vap->iv_stats.is_rx_badsubtype++; + break; + } +} + +/* + * Process a received ps-poll frame. + */ +static void +hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame_min *wh; + struct ifnet *ifp = vap->iv_ifp; + struct mbuf *m; + uint16_t aid; + int qlen; + + wh = mtod(m0, struct ieee80211_frame_min *); + if (ni->ni_associd == 0) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + (struct ieee80211_frame *) wh, NULL, + "%s", "unassociated station"); + vap->iv_stats.is_ps_unassoc++; + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_ASSOCED); + return; + } + + aid = le16toh(*(uint16_t *)wh->i_dur); + if (aid != ni->ni_associd) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + (struct ieee80211_frame *) wh, NULL, + "aid mismatch: sta aid 0x%x poll aid 0x%x", + ni->ni_associd, aid); + vap->iv_stats.is_ps_badaid++; + IEEE80211_SEND_MGMT(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_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2, + "%s", "recv ps-poll, but queue empty"); + ieee80211_send_nulldata(ieee80211_ref_node(ni)); + vap->iv_stats.is_ps_qempty++; /* XXX node stat */ + if (vap->iv_set_tim != NULL) + vap->iv_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_NOTE(vap, IEEE80211_MSG_POWER, ni, + "recv ps-poll, send packet, %u still queued", qlen); + m->m_flags |= M_MORE_DATA; + } else { + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "%s", "recv ps-poll, send packet, queue empty"); + if (vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); + } + m->m_flags |= M_PWR_SAV; /* bypass PS handling */ + IF_ENQUEUE(&ifp->if_snd, m); + if_start(ifp); +} diff --git a/sys/net80211/ieee80211_hostap.h b/sys/net80211/ieee80211_hostap.h new file mode 100644 index 0000000..87f858d --- /dev/null +++ b/sys/net80211/ieee80211_hostap.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_HOSTAP_H_ +#define _NET80211_IEEE80211_HOSTAP_H_ + +/* + * Hostap implementation definitions. + */ +void ieee80211_hostap_attach(struct ieee80211com *); +void ieee80211_hostap_detach(struct ieee80211com *); +#endif /* !_NET80211_IEEE80211_HOSTAP_H_ */ diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 63fb39a..d8b2d3d 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); */ #include "opt_inet.h" +#include "opt_wlan.h" #include <sys/param.h> #include <sys/kernel.h> @@ -46,37 +47,34 @@ __FBSDID("$FreeBSD$"); #include <net/ethernet.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_input.h> /* define here, used throughout file */ #define MS(_v, _f) (((_v) & _f) >> _f##_S) #define SM(_v, _f) (((_v) << _f##_S) & _f) -/* XXX need max array size */ -/* NB: these are for HT20 w/ long GI */ -const int ieee80211_htrates[16] = { - 13, /* IFM_IEEE80211_MCS0 */ - 26, /* IFM_IEEE80211_MCS1 */ - 39, /* IFM_IEEE80211_MCS2 */ - 52, /* IFM_IEEE80211_MCS3 */ - 78, /* IFM_IEEE80211_MCS4 */ - 104, /* IFM_IEEE80211_MCS5 */ - 117, /* IFM_IEEE80211_MCS6 */ - 130, /* IFM_IEEE80211_MCS7 */ - 26, /* IFM_IEEE80211_MCS8 */ - 52, /* IFM_IEEE80211_MCS9 */ - 78, /* IFM_IEEE80211_MCS10 */ - 104, /* IFM_IEEE80211_MCS11 */ - 156, /* IFM_IEEE80211_MCS12 */ - 208, /* IFM_IEEE80211_MCS13 */ - 234, /* IFM_IEEE80211_MCS14 */ - 260, /* IFM_IEEE80211_MCS15 */ +const struct ieee80211_mcs_rates ieee80211_htrates[16] = { + { 13, 14, 27, 30 }, /* MCS 0 */ + { 26, 29, 54, 60 }, /* MCS 1 */ + { 39, 43, 81, 90 }, /* MCS 2 */ + { 52, 58, 108, 120 }, /* MCS 3 */ + { 78, 87, 162, 180 }, /* MCS 4 */ + { 104, 116, 216, 240 }, /* MCS 5 */ + { 117, 130, 243, 270 }, /* MCS 6 */ + { 130, 144, 270, 300 }, /* MCS 7 */ + { 26, 29, 54, 60 }, /* MCS 8 */ + { 52, 58, 108, 120 }, /* MCS 9 */ + { 78, 87, 162, 180 }, /* MCS 10 */ + { 104, 116, 216, 240 }, /* MCS 11 */ + { 156, 173, 324, 360 }, /* MCS 12 */ + { 208, 231, 432, 480 }, /* MCS 13 */ + { 234, 260, 486, 540 }, /* MCS 14 */ + { 260, 289, 540, 600 } /* MCS 15 */ }; static const struct ieee80211_htrateset ieee80211_rateset_11n = { 16, { - /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - /* 39 52 78 104 117, 130 */ 10, 11, 12, 13, 14, 15 } }; @@ -85,11 +83,26 @@ static const struct ieee80211_htrateset ieee80211_rateset_11n = int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ #endif int ieee80211_recv_bar_ena = 1; +int ieee80211_addba_timeout = -1; /* timeout waiting for ADDBA response */ +int ieee80211_addba_backoff = -1; /* backoff after max ADDBA requests */ +int ieee80211_addba_maxtries = 3; /* max ADDBA requests before backoff */ -#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250) -#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000) -#define IEEE80211_AGGR_MAXTRIES 3 +/* + * Setup HT parameters that depends on the clock frequency. + */ +static void +ieee80211_ht_setup(void) +{ +#ifdef IEEE80211_AMPDU_AGE + ieee80211_ampdu_age = msecs_to_ticks(500); +#endif + ieee80211_addba_timeout = msecs_to_ticks(250); + ieee80211_addba_backoff = msecs_to_ticks(10*1000); +} +SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL); +static int ieee80211_ampdu_enable(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); static int ieee80211_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout); @@ -104,56 +117,70 @@ static void ieee80211_aggr_recv_action(struct ieee80211_node *ni, void ieee80211_ht_attach(struct ieee80211com *ic) { -#ifdef IEEE80211_AMPDU_AGE - if (ieee80211_ampdu_age == -1) - ieee80211_ampdu_age = msecs_to_ticks(500); -#endif - /* setup default aggregation policy */ ic->ic_recv_action = ieee80211_aggr_recv_action; ic->ic_send_action = ieee80211_send_action; + ic->ic_ampdu_enable = ieee80211_ampdu_enable; ic->ic_addba_request = ieee80211_addba_request; ic->ic_addba_response = ieee80211_addba_response; ic->ic_addba_stop = ieee80211_addba_stop; ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; +} + +void +ieee80211_ht_detach(struct ieee80211com *ic) +{ +} - /* XXX get from driver */ - ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; - ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; - ic->ic_ampdu_limit = ic->ic_ampdu_rxmax; - ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839; +void +ieee80211_ht_vattach(struct ieee80211vap *vap) +{ - if (ic->ic_htcaps & IEEE80211_HTC_HT) { + /* driver can override defaults */ + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; + vap->iv_ampdu_limit = vap->iv_ampdu_rxmax; + vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU; + /* tx aggregation traffic thresholds */ + vap->iv_ampdu_mintraffic[WME_AC_BK] = 128; + vap->iv_ampdu_mintraffic[WME_AC_BE] = 64; + vap->iv_ampdu_mintraffic[WME_AC_VO] = 32; + vap->iv_ampdu_mintraffic[WME_AC_VI] = 32; + + if (vap->iv_htcaps & IEEE80211_HTC_HT) { /* * Device is HT capable; enable all HT-related * facilities by default. * XXX these choices may be too aggressive. */ - ic->ic_flags_ext |= IEEE80211_FEXT_HT - | IEEE80211_FEXT_HTCOMPAT - ; - if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) - ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; + vap->iv_flags_ext |= IEEE80211_FEXT_HT + | IEEE80211_FEXT_HTCOMPAT + ; + if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20) + vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20; /* XXX infer from channel list? */ - if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { - ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; - if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) - ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; + if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) { + vap->iv_flags_ext |= IEEE80211_FEXT_USEHT40; + if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40) + vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40; } /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ - ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; - if (ic->ic_htcaps & IEEE80211_HTC_AMPDU) - ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; - ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; - if (ic->ic_htcaps & IEEE80211_HTC_AMSDU) - ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; + vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX; + if (vap->iv_htcaps & IEEE80211_HTC_AMPDU) + vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX; + vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX; + if (vap->iv_htcaps & IEEE80211_HTC_AMSDU) + vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX; } + /* NB: disable default legacy WDS, too many issues right now */ + if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) + vap->iv_flags_ext &= ~IEEE80211_FEXT_HT; } void -ieee80211_ht_detach(struct ieee80211com *ic) +ieee80211_ht_vdetach(struct ieee80211vap *vap) { } @@ -170,7 +197,7 @@ ht_announce(struct ieee80211com *ic, int mode, rs->rs_rates[i] | IEEE80211_RATE_MCS, mode); if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) continue; - rate = ieee80211_htrates[rs->rs_rates[i]]; + rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns; printf("%s%d%sMbps", (i != 0 ? " " : ""), rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); } @@ -205,14 +232,14 @@ ieee80211_get_suphtrates(struct ieee80211com *ic, struct mbuf * ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; int framelen; struct mbuf *n; /* discard 802.3 header inserted by ieee80211_decap */ m_adj(m, sizeof(struct ether_header)); - ic->ic_stats.is_amsdu_decap++; + vap->iv_stats.is_amsdu_decap++; for (;;) { /* @@ -223,23 +250,23 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) */ m = ieee80211_decap1(m, &framelen); if (m == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "a-msdu", "%s", "decap failed"); - ic->ic_stats.is_amsdu_tooshort++; + vap->iv_stats.is_amsdu_tooshort++; return NULL; } if (m->m_pkthdr.len == framelen) break; n = m_split(m, framelen, M_NOWAIT); if (n == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "a-msdu", "%s", "unable to split encapsulated frames"); - ic->ic_stats.is_amsdu_split++; + vap->iv_stats.is_amsdu_split++; m_freem(m); /* NB: must reclaim */ return NULL; } - ieee80211_deliver_data(ic, ni, m); + vap->iv_deliver_data(vap, ni, m); /* * Remove frame contents; each intermediate frame @@ -252,19 +279,6 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) } /* - * Start A-MPDU rx/re-order processing for the specified TID. - */ -static void -ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) -{ - memset(rap, 0, sizeof(*rap)); - rap->rxa_wnd = (bufsiz == 0) ? - IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); - rap->rxa_start = start; - rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND; -} - -/* * Purge all frames in the A-MPDU re-order queue. */ static void @@ -289,13 +303,33 @@ ampdu_rx_purge(struct ieee80211_rx_ampdu *rap) } /* + * Start A-MPDU rx/re-order processing for the specified TID. + */ +static void +ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start) +{ + if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) { + /* + * AMPDU previously setup and not terminated with a DELBA, + * flush the reorder q's in case anything remains. + */ + ampdu_rx_purge(rap); + } + memset(rap, 0, sizeof(*rap)); + rap->rxa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + rap->rxa_start = start; + rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND; +} + +/* * Stop A-MPDU rx processing for the specified TID. */ static void ampdu_rx_stop(struct ieee80211_rx_ampdu *rap) { - rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND; ampdu_rx_purge(rap); + rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND); } /* @@ -309,7 +343,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) { m->m_flags |= M_AMPDU; /* bypass normal processing */ /* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */ - (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0); + (void) ieee80211_input(ni, m, 0, 0, 0); } /* @@ -323,7 +357,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) static void ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct mbuf *m; int i; @@ -353,14 +387,14 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) } } KASSERT(n == 0, ("lost %d frames", n)); - ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; + vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; } /* * Adjust the start of the BA window to * reflect the frames just dispatched. */ rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); - ic->ic_stats.is_ampdu_rx_oor += i; + vap->iv_stats.is_ampdu_rx_oor += i; } #ifdef IEEE80211_AMPDU_AGE @@ -370,7 +404,7 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) static void ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct mbuf *m; int i; @@ -381,7 +415,7 @@ ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; - ic->ic_stats.is_ampdu_rx_oor++; + vap->iv_stats.is_ampdu_rx_oor++; ampdu_dispatch(ni, m); if (rap->rxa_qframes == 0) @@ -399,7 +433,7 @@ static void ampdu_rx_flush_upto(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct mbuf *m; ieee80211_seq seqno; int i; @@ -418,7 +452,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni, rap->rxa_m[i] = NULL; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; - ic->ic_stats.is_ampdu_rx_oor++; + vap->iv_stats.is_ampdu_rx_oor++; ampdu_dispatch(ni, m); } else { @@ -433,6 +467,10 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni, */ if (rap->rxa_qframes != 0) { int n = rap->rxa_qframes, j; + + /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */ + KASSERT(rap->rxa_m[0] == NULL, + ("%s: BA window slot 0 occupied", __func__)); for (j = i+1; j < rap->rxa_wnd; j++) { if (rap->rxa_m[j] != NULL) { rap->rxa_m[j-i] = rap->rxa_m[j]; @@ -446,7 +484,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni, __func__, n, rap->rxa_qframes, i, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), winstart)); - ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes; + vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; } /* * Move the start of the BA window; we use the @@ -472,7 +510,7 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) #define PROCESS 0 /* caller should process frame */ #define CONSUMED 1 /* frame consumed, caller does nothing */ - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_qosframe *wh; struct ieee80211_rx_ampdu *rap; ieee80211_seq rxseq; @@ -557,7 +595,7 @@ again: * frame; flush the reorder buffer. */ if (rap->rxa_qframes != 0) { - ic->ic_stats.is_ampdu_rx_age += + vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; ampdu_rx_flush(ni, rap); } @@ -576,15 +614,15 @@ again: rap->rxa_m[off] = m; rap->rxa_qframes++; rap->rxa_qbytes += m->m_pkthdr.len; - ic->ic_stats.is_ampdu_rx_reorder++; + vap->iv_stats.is_ampdu_rx_reorder++; } else { - IEEE80211_DISCARD_MAC(ic, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "a-mpdu duplicate", "seqno %u tid %u BA win <%u:%u>", rxseq, tid, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1)); - ic->ic_stats.is_rx_dup++; + vap->iv_stats.is_rx_dup++; IEEE80211_NODE_STAT(ni, rx_dup); m_freem(m); } @@ -596,12 +634,12 @@ again: * flush the reorder q and move the window. * Sec 9.10.7.6 b) (D2.04 p.118 line 60) */ - IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "move BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); - ic->ic_stats.is_ampdu_rx_move++; + vap->iv_stats.is_ampdu_rx_move++; /* * The spec says to flush frames up to but not including: @@ -620,14 +658,14 @@ again: * Outside the BA window and out of range; toss. * Sec 9.10.7.6 c) (D2.04 p.119 line 16) */ - IEEE80211_DISCARD_MAC(ic, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, - "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", + "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); - ic->ic_stats.is_ampdu_rx_drop++; + vap->iv_stats.is_ampdu_rx_drop++; IEEE80211_NODE_STAT(ni, rx_drop); m_freem(m); return CONSUMED; @@ -645,7 +683,7 @@ again: void ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame_bar *wh; struct ieee80211_rx_ampdu *rap; ieee80211_seq rxseq; @@ -653,10 +691,10 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) if (!ieee80211_recv_bar_ena) { #if 0 - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "%s", "processing disabled"); #endif - ic->ic_stats.is_ampdu_bar_bad++; + vap->iv_stats.is_ampdu_bar_bad++; return; } wh = mtod(m0, struct ieee80211_frame_bar *); @@ -667,13 +705,13 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) /* * No ADDBA request yet, don't touch. */ - IEEE80211_DISCARD_MAC(ic, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); - ic->ic_stats.is_ampdu_bar_bad++; + vap->iv_stats.is_ampdu_bar_bad++; return; } - ic->ic_stats.is_ampdu_bar_rx++; + vap->iv_stats.is_ampdu_bar_rx++; rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; if (rxseq == rap->rxa_start) return; @@ -684,12 +722,12 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) * Flush the reorder q up to rxseq and move the window. * Sec 9.10.7.6 a) (D2.04 p.119 line 22) */ - IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); - ic->ic_stats.is_ampdu_bar_move++; + vap->iv_stats.is_ampdu_bar_move++; ampdu_rx_flush_upto(ni, rap, rxseq); if (off >= rap->rxa_wnd) { @@ -705,14 +743,14 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) * Out of range; toss. * Sec 9.10.7.6 b) (D2.04 p.119 line 41) */ - IEEE80211_DISCARD_MAC(ic, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); - ic->ic_stats.is_ampdu_bar_oow++; + vap->iv_stats.is_ampdu_bar_oow++; IEEE80211_NODE_STAT(ni, rx_drop); } } @@ -767,6 +805,8 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni) */ ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]); IEEE80211_TAPQ_DESTROY(tap); + tap->txa_lastsample = 0; + tap->txa_avgpps = 0; /* NB: clearing NAK means we may re-send ADDBA */ tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); @@ -780,6 +820,45 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni) IEEE80211_NODE_AMPDU); } +/* + * Age out HT resources for a station. + */ +void +ieee80211_ht_node_age(struct ieee80211_node *ni) +{ +#ifdef IEEE80211_AMPDU_AGE + struct ieee80211vap *vap = ni->ni_vap; + uint8_t tid; +#endif + + KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); + +#ifdef IEEE80211_AMPDU_AGE + for (tid = 0; tid < WME_NUM_TID; tid++) { + struct ieee80211_rx_ampdu *rap; + + rap = &ni->ni_rx_ampdu[tid]; + if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) + continue; + if (rap->rxa_qframes == 0) + continue; + /* + * Check for frames sitting too long in the reorder queue. + * See above for more details on what's happening here. + */ + /* XXX honor batimeout? */ + if (ticks - rap->rxa_age > ieee80211_ampdu_age) { + /* + * Too long since we received the first + * frame; flush the reorder buffer. + */ + vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; + ampdu_rx_flush(ni, rap); + } + } +#endif /* IEEE80211_AMPDU_AGE */ +} + static struct ieee80211_channel * findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) { @@ -832,11 +911,11 @@ ieee80211_ht_adjust_channel(struct ieee80211com *ic, void ieee80211_ht_wds_init(struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_tx_ampdu *tap; int ac; - KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested")); + KASSERT(vap->iv_flags_ext & IEEE80211_FEXT_HT, ("no HT requested")); /* XXX check scan cache in case peer has an ap and we have info */ /* @@ -845,11 +924,11 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni) * AP) is suitable use it so we use the same location * for the extension channel). */ - ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan, - ic->ic_flags_ext); + ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic, + ni->ni_chan, ieee80211_htchanflags(ni->ni_chan)); ni->ni_htcap = 0; - if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) + if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20; if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40; @@ -858,7 +937,7 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni) ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; - if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) + if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40; } else { ni->ni_chw = 20; @@ -883,20 +962,30 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni) static void htinfo_notify(struct ieee80211com *ic) { - if (ic->ic_opmode != IEEE80211_M_HOSTAP) - return; - IEEE80211_NOTE(ic, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, - ic->ic_bss, - "HT bss occupancy change: %d sta, %d ht, " - "%d ht40%s, HT protmode now 0x%x" - , ic->ic_sta_assoc - , ic->ic_ht_sta_assoc - , ic->ic_ht40_sta_assoc - , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ? - ", non-HT sta present" : "" - , ic->ic_curhtprotmode); - ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO); + struct ieee80211vap *vap; + int first = 1; + + IEEE80211_LOCK_ASSERT(ic); + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_opmode != IEEE80211_M_HOSTAP) + continue; + if (first) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, + vap->iv_bss, + "HT bss occupancy change: %d sta, %d ht, " + "%d ht40%s, HT protmode now 0x%x" + , ic->ic_sta_assoc + , ic->ic_ht_sta_assoc + , ic->ic_ht40_sta_assoc + , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ? + ", non-HT sta present" : "" + , ic->ic_curhtprotmode); + first = 0; + } + ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO); + } } /* @@ -908,13 +997,14 @@ htinfo_update(struct ieee80211com *ic) { uint8_t protmode; - if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) { - protmode = IEEE80211_HTINFO_OPMODE_PROTOPT - | IEEE80211_HTINFO_NONHT_PRESENT; - } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { + if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { protmode = IEEE80211_HTINFO_OPMODE_MIXED - | IEEE80211_HTINFO_NONHT_PRESENT; - } else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && + | IEEE80211_HTINFO_NONHT_PRESENT; + } else if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) { + protmode = IEEE80211_HTINFO_OPMODE_PROTOPT + | IEEE80211_HTINFO_NONHT_PRESENT; + } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) { protmode = IEEE80211_HTINFO_OPMODE_HT20PR; } else { @@ -964,15 +1054,36 @@ ieee80211_ht_node_leave(struct ieee80211_node *ni) /* * Public version of htinfo_update; used for processing - * beacon frames from overlapping bss in hostap_recv_mgmt. + * beacon frames from overlapping bss. + * + * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED + * (on receipt of a beacon that advertises MIXED) or + * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon + * from an overlapping legacy bss). We treat MIXED with + * a higher precedence than PROTOPT (i.e. we will not change + * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This + * corresponds to how we handle things in htinfo_update. */ void -ieee80211_htinfo_update(struct ieee80211com *ic, int protmode) +ieee80211_htprot_update(struct ieee80211com *ic, int protmode) { - if (protmode != ic->ic_curhtprotmode) { - ic->ic_curhtprotmode = protmode; - htinfo_notify(ic); - } +#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE) + if (protmode == ic->ic_curhtprotmode) + return; + if (OPMODE(ic->ic_curhtprotmode) == IEEE80211_HTINFO_OPMODE_MIXED && + OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT) + return; + + /* track non-HT station presence */ + KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT, + ("missing NONHT_PRESENT")); + ic->ic_flags_ext |= IEEE80211_FEXT_NONHT_PR; + ic->ic_lastnonht = ticks; + + /* push beacon update */ + ic->ic_curhtprotmode = protmode; + htinfo_notify(ic); +#undef OPMODE } /* @@ -991,7 +1102,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic) if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) && time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { #if 0 - IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "%s", "time out non-HT STA present on channel"); #endif ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR; @@ -1011,7 +1122,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic) void ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; if (ie[0] == IEEE80211_ELEMID_VENDOR) { /* @@ -1029,7 +1140,7 @@ ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; /* XXX needed or will ieee80211_parse_htinfo always be called? */ ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && - (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20; + (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20; } /* @@ -1044,6 +1155,7 @@ void ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) { struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htinfo *htinfo; struct ieee80211_channel *c; uint16_t w; @@ -1063,11 +1175,11 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) * identify the right channel to use. If we cannot locate it * in the channel table then fallback to legacy operation. */ - htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ? - IEEE80211_CHAN_HT20 : 0; /* NB: honor operating mode constraint */ + htflags = (vap->iv_flags_ext & IEEE80211_FEXT_HT) ? + IEEE80211_CHAN_HT20 : 0; if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && - (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) { + (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)) { if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) htflags = IEEE80211_CHAN_HT40U; else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) @@ -1076,20 +1188,20 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags; if (chanflags != ni->ni_chan->ic_flags) { c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags); - if (c == NULL && htflags != IEEE80211_CHAN_HT20) { + if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) { /* * No HT40 channel entry in our table; fall back * to HT20 operation. This should not happen. */ c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20); - IEEE80211_NOTE(ni->ni_ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "no HT40 channel (freq %u), falling back to HT20", ni->ni_chan->ic_freq); /* XXX stat */ } if (c != NULL && c != ni->ni_chan) { - IEEE80211_NOTE(ni->ni_ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "switch station to HT%d channel %u/0x%x", IEEE80211_IS_CHAN_HT40(c) ? 40 : 20, @@ -1108,7 +1220,7 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) int ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htcap *htcap; struct ieee80211_htrateset *rs; int i; @@ -1123,11 +1235,11 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) if (isclr(htcap->hc_mcsset, i)) continue; if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "WARNING, HT rate set too large; only " "using %u rates", IEEE80211_HTRATE_MAXSIZE); - ic->ic_stats.is_rx_rstoobig++; + vap->iv_stats.is_rx_rstoobig++; break; } rs->rs_rates[rs->rs_nrates++] = i; @@ -1152,7 +1264,7 @@ ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) htinfo = (const struct ieee80211_ie_htinfo *) ie; rs = &ni->ni_htrates; if (rs->rs_nrates == 0) { - IEEE80211_NOTE(ni->ni_ic, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "%s", "WARNING, empty HT rate set"); return; @@ -1180,10 +1292,10 @@ static void addba_start_timeout(struct ieee80211_tx_ampdu *tap) { /* XXX use CALLOUT_PENDING instead? */ - callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT, + callout_reset(&tap->txa_timer, ieee80211_addba_timeout, addba_timeout, tap); tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; - tap->txa_lastrequest = ticks; + tap->txa_nextrequest = ticks + ieee80211_addba_timeout; } static void @@ -1274,6 +1386,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm) { struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_action *ia; struct ieee80211_rx_ampdu *rap; struct ieee80211_tx_ampdu *tap; @@ -1295,7 +1408,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, tid = MS(baparamset, IEEE80211_BAPS_TID); bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA request: dialogtoken %u " "baparamset 0x%x (tid %d bufsiz %d) batimeout %d " @@ -1314,19 +1427,19 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, * violates the 11n spec and is mostly for testing). */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) && - (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) { + (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)) { ampdu_rx_start(rap, bufsiz, MS(baseqctl, IEEE80211_BASEQ_START)); args[1] = IEEE80211_STATUS_SUCCESS; } else { - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "reject ADDBA request: %s", ni->ni_flags & IEEE80211_NODE_AMPDU_RX ? "administratively disabled" : "not negotiated for station"); - ic->ic_stats.is_addba_reject++; + vap->iv_stats.is_addba_reject++; args[1] = IEEE80211_STATUS_UNSPECIFIED; } /* XXX honor rap flags? */ @@ -1350,26 +1463,26 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, ac = TID_TO_WME_AC(tid); tap = &ni->ni_tx_ampdu[ac]; if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { - IEEE80211_DISCARD_MAC(ic, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "no pending ADDBA, tid %d dialogtoken %u " "code %d", tid, dialogtoken, code); - ic->ic_stats.is_addba_norequest++; + vap->iv_stats.is_addba_norequest++; return; } if (dialogtoken != tap->txa_token) { - IEEE80211_DISCARD_MAC(ic, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "dialogtoken mismatch: waiting for %d, " "received %d, tid %d code %d", tap->txa_token, dialogtoken, tid, code); - ic->ic_stats.is_addba_badtoken++; + vap->iv_stats.is_addba_badtoken++; return; } - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA response: dialogtoken %u code %d " "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", @@ -1385,7 +1498,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni, tid = MS(baparamset, IEEE80211_DELBAPS_TID); - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv DELBA: baparamset 0x%x (tid %d initiator %d) " "code %d", baparamset, tid, @@ -1416,18 +1529,18 @@ void ieee80211_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_action *ia; int chw; ia = (const struct ieee80211_action *) frm; switch (ia->ia_category) { case IEEE80211_ACTION_CAT_BA: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: BA action %d not implemented", __func__, ia->ia_action); - ic->ic_stats.is_rx_mgtdiscard++; + vap->iv_stats.is_rx_mgtdiscard++; break; case IEEE80211_ACTION_CAT_HT: switch (ia->ia_action) { @@ -1437,7 +1550,7 @@ ieee80211_recv_action(struct ieee80211_node *ni, ni->ni_chw = chw; ni->ni_flags |= IEEE80211_NODE_CHWUPDATE; } - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT txchwidth, width %d (%s)", __func__, chw, @@ -1445,25 +1558,25 @@ ieee80211_recv_action(struct ieee80211_node *ni, "new" : "no change"); break; case IEEE80211_ACTION_HT_MIMOPWRSAVE: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT MIMO PS", __func__); break; default: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT action %d not implemented", __func__, ia->ia_action); - ic->ic_stats.is_rx_mgtdiscard++; + vap->iv_stats.is_rx_mgtdiscard++; break; } break; default: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: category %d not implemented", __func__, ia->ia_category); - ic->ic_stats.is_rx_mgtdiscard++; + vap->iv_stats.is_rx_mgtdiscard++; break; } } @@ -1473,6 +1586,39 @@ ieee80211_recv_action(struct ieee80211_node *ni, */ /* + * Check if A-MPDU should be requested/enabled for a stream. + * We require a traffic rate above a per-AC threshold and we + * also handle backoff from previous failed attempts. + * + * Drivers may override this method to bring in information + * such as link state conditions in making the decision. + */ +static int +ieee80211_ampdu_enable(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ + struct ieee80211vap *vap = ni->ni_vap; + + if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac]) + return 0; + /* XXX check rssi? */ + if (tap->txa_attempts >= ieee80211_addba_maxtries && + ticks < tap->txa_nextrequest) { + /* + * Don't retry too often; txa_nextrequest is set + * to the minimum interval we'll retry after + * ieee80211_addba_maxtries failed attempts are made. + */ + return 0; + } + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, + "%s: enable AMPDU on %s, avgpps %d pkts %d", + __func__, ieee80211_wme_acnames[tap->txa_ac], + tap->txa_avgpps, tap->txa_pkts); + return 1; +} + +/* * Request A-MPDU tx aggregation. Setup local state and * issue an ADDBA request. BA use will only happen after * the other end replies with ADDBA response. @@ -1493,16 +1639,6 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, callout_init(&tap->txa_timer, CALLOUT_MPSAFE); tap->txa_flags |= IEEE80211_AGGR_SETUP; } - if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES && - (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) { - /* - * Don't retry too often; IEEE80211_AGGR_MINRETRY - * defines the minimum interval we'll retry after - * IEEE80211_AGGR_MAXTRIES failed attempts to - * negotiate use. - */ - return 0; - } /* XXX hack for not doing proper locking */ tap->txa_flags &= ~IEEE80211_AGGR_NAK; @@ -1521,12 +1657,14 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, /* NB: do first so there's no race against reply */ if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { /* unable to setup state, don't make request */ - IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: could not setup BA stream for AC %d", __func__, tap->txa_ac); /* defer next try so we don't slam the driver with requests */ - tap->txa_attempts = IEEE80211_AGGR_MAXTRIES; - tap->txa_lastrequest = ticks; + tap->txa_attempts = ieee80211_addba_maxtries; + /* NB: check in case driver wants to override */ + if (tap->txa_nextrequest <= ticks) + tap->txa_nextrequest = ticks + ieee80211_addba_backoff; return 0; } tokens = dialogtoken; /* allocate token */ @@ -1542,13 +1680,14 @@ void ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; uint16_t args[4]; /* XXX locking */ if (IEEE80211_AMPDU_RUNNING(tap)) { - IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac); - ic->ic_stats.is_ampdu_stop++; + vap->iv_stats.is_ampdu_stop++; ic->ic_addba_stop(ni, tap); args[0] = WME_AC_TO_TID(tap->txa_ac); @@ -1557,10 +1696,10 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_DELBA, args); } else { - IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: BA stream for AC %d not running", __func__, tap->txa_ac); - ic->ic_stats.is_ampdu_stop_failed++; + vap->iv_stats.is_ampdu_stop_failed++; } } @@ -1573,14 +1712,14 @@ int ieee80211_send_bar(struct ieee80211_node *ni, const struct ieee80211_tx_ampdu *tap) { -#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) +#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; struct ieee80211_frame_min *wh; struct mbuf *m; uint8_t *frm; @@ -1601,7 +1740,7 @@ ieee80211_send_bar(struct ieee80211_node *ni, IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; wh->i_fc[1] = 0; IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); tid = WME_AC_TO_TID(tap->txa_ac); barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? @@ -1617,17 +1756,15 @@ ieee80211_send_bar(struct ieee80211_node *ni, ADDSHORT(frm, barseqctl); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + M_WME_SETAC(m, WME_AC_VO); + IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ - IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, "send bar frame (tid %u start %u) on channel %u", tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan)); - m->m_pkthdr.rcvif = (void *)ni; - IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ - if_start(ifp); - - return 0; + return ic->ic_raw_xmit(ni, m, NULL); bad: ieee80211_free_node(ni); return ret; @@ -1644,12 +1781,13 @@ int ieee80211_send_action(struct ieee80211_node *ni, int category, int action, uint16_t args[4]) { -#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) +#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct mbuf *m; uint8_t *frm; @@ -1663,7 +1801,7 @@ ieee80211_send_action(struct ieee80211_node *ni, * the xmit is complete all the way in the driver. On error we * will remove our reference. */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), @@ -1685,7 +1823,7 @@ ieee80211_send_action(struct ieee80211_node *ni, case IEEE80211_ACTION_CAT_BA: switch (action) { case IEEE80211_ACTION_BA_ADDBA_REQUEST: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send ADDBA request: dialogtoken %d " "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", @@ -1698,7 +1836,7 @@ ieee80211_send_action(struct ieee80211_node *ni, ADDSHORT(frm, args[3]); /* baseqctl */ break; case IEEE80211_ACTION_BA_ADDBA_RESPONSE: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send ADDBA response: dialogtoken %d status %d " "baparamset 0x%x (tid %d) batimeout %d", @@ -1718,7 +1856,7 @@ ieee80211_send_action(struct ieee80211_node *ni, ADDSHORT(frm, baparamset); ADDSHORT(frm, args[2]); /* reason code */ - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send DELBA action: tid %d, initiator %d reason %d", args[0], args[1], args[2]); @@ -1730,12 +1868,12 @@ ieee80211_send_action(struct ieee80211_node *ni, case IEEE80211_ACTION_CAT_HT: switch (action) { case IEEE80211_ACTION_HT_TXCHWIDTH: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send HT txchwidth: width %d", - IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20 + IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20 ); - *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? + *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? IEEE80211_A_HT_TXCHWIDTH_2040 : IEEE80211_A_HT_TXCHWIDTH_20; break; @@ -1745,7 +1883,7 @@ ieee80211_send_action(struct ieee80211_node *ni, break; default: badaction: - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: unsupported category %d action %d", __func__, category, action); @@ -1754,12 +1892,11 @@ ieee80211_send_action(struct ieee80211_node *ni, } m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); - ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION); - if (ret != 0) - goto bad; - return 0; + return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION); bad: ieee80211_free_node(ni); + if (m != NULL) + m_freem(m); return ret; #undef ADDSHORT #undef senderr @@ -1794,12 +1931,12 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) frm[1] = (v) >> 8; \ frm += 2; \ } while (0) - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; uint16_t caps; int rxmax, density; /* HT capabilities */ - caps = ic->ic_htcaps & 0xffff; + caps = vap->iv_htcaps & 0xffff; /* * Note channel width depends on whether we are operating as * a sta or not. When operating as a sta we are generating @@ -1808,9 +1945,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) * how we've been setup (which might be different if a fixed * channel is specified). */ - if (ic->ic_opmode == IEEE80211_M_STA) { + if (vap->iv_opmode == IEEE80211_M_STA) { /* override 20/40 use based on config */ - if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) + if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; @@ -1819,17 +1956,17 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); } else { /* override 20/40 use based on current channel */ - if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; - rxmax = ic->ic_ampdu_rxmax; - density = ic->ic_ampdu_density; + rxmax = vap->iv_ampdu_rxmax; + density = vap->iv_ampdu_density; } /* adjust short GI based on channel and config */ - if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) + if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI20; - if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || + if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 || (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI40; ADDSHORT(frm, caps); @@ -1909,23 +2046,25 @@ ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) * Update the HTINFO ie for a beacon frame. */ void -ieee80211_ht_update_beacon(struct ieee80211com *ic, +ieee80211_ht_update_beacon(struct ieee80211vap *vap, struct ieee80211_beacon_offsets *bo) { #define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) + const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan; + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_ie_htinfo *ht = (struct ieee80211_ie_htinfo *) bo->bo_htinfo; /* XXX only update on channel change */ - ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan); + ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan); ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; - if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan)) + if (IEEE80211_IS_CHAN_HT40U(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; - else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan)) + else if (IEEE80211_IS_CHAN_HT40D(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; else ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; - if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) + if (IEEE80211_IS_CHAN_HT40(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; /* protection mode */ @@ -1951,16 +2090,16 @@ ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); /* primary/control channel center */ - *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; - if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan)) + if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; - else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan)) + else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; else frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; - if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan)) + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; frm[1] = ic->ic_curhtprotmode; diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h index d1d6699..80052cf 100644 --- a/sys/net80211/ieee80211_ht.h +++ b/sys/net80211/ieee80211_ht.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2007 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,13 +46,16 @@ struct ieee80211_tx_ampdu { #define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */ uint8_t txa_ac; uint8_t txa_token; /* dialog token */ + int txa_lastsample; /* ticks @ last traffic sample */ + int txa_pkts; /* packets over last sample interval */ + int txa_avgpps; /* filtered traffic over window */ int txa_qbytes; /* data queued (bytes) */ short txa_qframes; /* data queued (frames) */ ieee80211_seq txa_seqstart; ieee80211_seq txa_start; uint16_t txa_wnd; /* BA window size */ - uint8_t txa_attempts; /* # setup attempts */ - int txa_lastrequest;/* time of last ADDBA request */ + uint8_t txa_attempts; /* # ADDBA requests w/o a response */ + int txa_nextrequest;/* soonest to make next ADDBA request */ struct ifqueue txa_q; /* packet queue */ struct callout txa_timer; void *txa_private; /* driver-private storage */ @@ -67,6 +70,65 @@ struct ieee80211_tx_ampdu { (((tap)->txa_flags & \ (IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0) +/* + * Traffic estimator support. We estimate packets/sec for + * each AC that is setup for AMPDU or will potentially be + * setup for AMPDU. The traffic rate can be used to decide + * when AMPDU should be setup (according to a threshold) + * and is available for drivers to do things like cache + * eviction when only a limited number of BA streams are + * available and more streams are requested than available. + */ + +static void __inline +ieee80211_txampdu_update_pps(struct ieee80211_tx_ampdu *tap) +{ + /* NB: scale factor of 2 was picked heuristically */ + tap->txa_avgpps = ((tap->txa_avgpps << 2) - + tap->txa_avgpps + tap->txa_pkts) >> 2; +} + +/* + * Count a packet towards the pps estimate. + */ +static void __inline +ieee80211_txampdu_count_packet(struct ieee80211_tx_ampdu *tap) +{ + /* XXX bound loop/do more crude estimate? */ + while (ticks - tap->txa_lastsample >= hz) { + ieee80211_txampdu_update_pps(tap); + /* reset to start new sample interval */ + tap->txa_pkts = 0; + if (tap->txa_avgpps == 0) { + tap->txa_lastsample = ticks; + break; + } + tap->txa_lastsample += hz; + } + tap->txa_pkts++; +} + +/* + * Get the current pps estimate. If the average is out of + * date due to lack of traffic then we decay the estimate + * to account for the idle time. + */ +static int __inline +ieee80211_txampdu_getpps(struct ieee80211_tx_ampdu *tap) +{ + /* XXX bound loop/do more crude estimate? */ + while (ticks - tap->txa_lastsample >= hz) { + ieee80211_txampdu_update_pps(tap); + tap->txa_pkts = 0; + if (tap->txa_avgpps == 0) { + tap->txa_lastsample = ticks; + break; + } + tap->txa_lastsample += hz; + } + return tap->txa_avgpps; +} + struct ieee80211_rx_ampdu { int rxa_flags; int rxa_qbytes; /* data queued (bytes) */ @@ -81,10 +143,18 @@ struct ieee80211_rx_ampdu { void ieee80211_ht_attach(struct ieee80211com *); void ieee80211_ht_detach(struct ieee80211com *); +void ieee80211_ht_vattach(struct ieee80211vap *); +void ieee80211_ht_vdetach(struct ieee80211vap *); void ieee80211_ht_announce(struct ieee80211com *); -extern const int ieee80211_htrates[16]; +struct ieee80211_mcs_rates { + uint16_t ht20_rate_800ns; + uint16_t ht20_rate_400ns; + uint16_t ht40_rate_800ns; + uint16_t ht40_rate_400ns; +}; +extern const struct ieee80211_mcs_rates ieee80211_htrates[16]; const struct ieee80211_htrateset *ieee80211_get_suphtrates( struct ieee80211com *, const struct ieee80211_channel *); @@ -98,12 +168,14 @@ int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *); void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *); void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *); void ieee80211_ht_node_cleanup(struct ieee80211_node *); +void ieee80211_ht_node_age(struct ieee80211_node *); + struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *, struct ieee80211_channel *, int); void ieee80211_ht_wds_init(struct ieee80211_node *); void ieee80211_ht_node_join(struct ieee80211_node *); void ieee80211_ht_node_leave(struct ieee80211_node *); -void ieee80211_htinfo_update(struct ieee80211com *, int protmode); +void ieee80211_htprot_update(struct ieee80211com *, int protmode); void ieee80211_ht_timeout(struct ieee80211com *); void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *); void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *); @@ -122,6 +194,6 @@ uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *); uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *); uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *); struct ieee80211_beacon_offsets; -void ieee80211_ht_update_beacon(struct ieee80211com *, +void ieee80211_ht_update_beacon(struct ieee80211vap *, struct ieee80211_beacon_offsets *); #endif /* _NET80211_IEEE80211_HT_H_ */ diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 9238bdf..9ae653f 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,6 +27,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -35,591 +37,75 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/socket.h> - -#include <net/if.h> -#include <net/if_media.h> + #include <net/ethernet.h> +#include <net/if.h> #include <net/if_llc.h> +#include <net/if_media.h> #include <net/if_vlan_var.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_input.h> #include <net/bpf.h> -#ifdef IEEE80211_DEBUG -#include <machine/stdarg.h> - -/* - * 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; -} - -static const uint8_t *ieee80211_getbssid(struct ieee80211com *, - const struct ieee80211_frame *); -#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 struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *, - struct ieee80211_node *, struct mbuf *); -static void ieee80211_recv_pspoll(struct ieee80211com *, - struct ieee80211_node *, struct mbuf *); +#ifdef INET +#include <netinet/in.h> +#include <net/ethernet.h> +#endif -/* - * Process a received frame. The node associated with the sender - * should be supplied. If nothing was found in the node table then - * the caller is assumed to supply a reference to ic_bss instead. - * The RSSI and a timestamp are also supplied. The RSSI data is used - * during AP scanning to select a AP to associate with; it can have - * any units so long as values have consistent units and higher values - * mean ``better signal''. The receive timestamp is currently not used - * by the 802.11 layer. - */ int -ieee80211_input(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp) +ieee80211_input_all(struct ieee80211com *ic, + struct mbuf *m, int rssi, int noise, u_int32_t rstamp) { -#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; - int hdrspace, need_tap; - uint8_t dir, type, subtype, qos; - uint8_t *bssid; - uint16_t rxseq; - - if (m->m_flags & M_AMPDU) { - /* - * Fastpath for A-MPDU reorder q resubmission. Frames - * w/ M_AMPDU marked have already passed through here - * but were received out of order and been held on the - * reorder queue. When resubmitted they are marked - * with the M_AMPDU flag and we can bypass most of the - * normal processing. - */ - wh = mtod(m, struct ieee80211_frame *); - type = IEEE80211_FC0_TYPE_DATA; - dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; - subtype = IEEE80211_FC0_SUBTYPE_QOS; - hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ - need_tap = 0; - goto resubmit_ampdu; - } - - KASSERT(ni != NULL, ("null node")); - ni->ni_inact = ni->ni_inact_reload; - - need_tap = 1; /* mbuf need to be tapped. */ - type = -1; /* undefined */ - /* - * In monitor mode, send everything directly to bpf. - * XXX may want to include the CRC - */ - 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) { - 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; - 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: - bssid = wh->i_addr2; - if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { - /* not interested in */ - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, - bssid, NULL, "%s", "not to bss"); - ic->ic_stats.is_rx_wrongbss++; - goto out; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - 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_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; - default: - goto out; - } - ni->ni_rssi = rssi; - ni->ni_noise = noise; - ni->ni_rstamp = rstamp; - 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 = IEEE80211_NONQOS_TID; - rxseq = le16toh(*(uint16_t *)wh->i_seq); - if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && - (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; - } - } - - 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", "unknown dir 0x%x", dir); - ic->ic_stats.is_rx_wrongdir++; - goto out; - } - if ((ifp->if_flags & IFF_SIMPLEX) && - IEEE80211_IS_MULTICAST(wh->i_addr1) && - IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { - /* - * In IEEE802.11 network, multicast packet - * sent from me is broadcasted from AP. - * 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; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - if (dir != IEEE80211_FC1_DIR_NODS) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "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", "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_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_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "%s", "unassoc src"); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_NOT_ASSOCED); - ic->ic_stats.is_rx_notassoc++; - goto err; - } - - /* - * Check for power save state change. - * XXX out-of-order A-MPDU frames? - */ - 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; - default: - /* XXX here to keep compiler happy */ - goto out; - } - - /* - * Handle A-MPDU re-ordering. The station must be - * associated and negotiated HT. The frame must be - * a QoS frame (not QoS null data) and not previously - * processed for A-MPDU re-ordering. If the frame is - * to be processed directly then ieee80211_ampdu_reorder - * will return 0; otherwise it has consumed the mbuf - * and we should do nothing more with it. - */ - if ((ni->ni_flags & IEEE80211_NODE_HT) && - subtype == IEEE80211_FC0_SUBTYPE_QOS && - ieee80211_ampdu_reorder(ni, m) != 0) { - m = NULL; - goto out; - } - resubmit_ampdu: - - /* - * 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_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 { - /* XXX M_WEP and IEEE80211_F_PRIVACY */ - key = NULL; - } - - /* - * Save QoS bits for use below--before we strip the header. - */ - if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { - qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? - ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : - ((struct ieee80211_qosframe *)wh)->i_qos[0]; - } else - qos = 0; + struct ieee80211vap *vap; + int type = -1; - /* - * 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 */ + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ieee80211_node *ni; + struct mbuf *mcopy; /* - * Next strip any MSDU crypto bits. + * WDS vap's only receive directed traffic from the + * station at the ``far end''. That traffic should + * be passed through the AP vap the station is associated + * to--so don't spam them with mcast frames. */ - if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, - ni->ni_macaddr, "data", "%s", "demic error"); - ic->ic_stats.is_rx_demicfail++; - IEEE80211_NODE_STAT(ni, rx_demicfail); - goto out; - } - - /* copy to listener after decrypt */ - if (bpf_peers_present(ic->ic_rawbpf)) - bpf_mtap(ic->ic_rawbpf, m); - need_tap = 0; - - /* - * 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 || - subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) - 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; - } - eh = mtod(m, struct ether_header *); - if (!ieee80211_node_is_authorized(ni)) { + if (vap->iv_opmode == IEEE80211_M_WDS) + continue; + if (TAILQ_NEXT(vap, iv_next) != NULL) { /* - * 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. + * Packet contents are changed by ieee80211_decap + * so do a deep copy of the packet. */ - 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; + mcopy = m_dup(m, M_DONTWAIT); + if (mcopy == NULL) { + /* XXX stat+msg */ + continue; } } else { - /* - * When denying unencrypted frames, discard - * any non-PAE frames received without encryption. - */ - if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && - (key == NULL && (m->m_flags & M_WEP) == 0) && - eh->ether_type != htons(ETHERTYPE_PAE)) { - /* - * Drop unencrypted frames. - */ - ic->ic_stats.is_rx_unencrypted++; - IEEE80211_NODE_STAT(ni, rx_unencrypted); - goto out; - } - } - /* XXX require HT? */ - if (qos & IEEE80211_QOS_AMSDU) { - m = ieee80211_decap_amsdu(ni, m); - if (m == NULL) - return IEEE80211_FC0_TYPE_DATA; - } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && -#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) - m->m_pkthdr.len >= 3*FF_LLC_SIZE) { - struct llc *llc; - - /* - * Check for fast-frame tunnel encapsulation. - */ - if (m->m_len < FF_LLC_SIZE && - (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, - ni->ni_macaddr, "fast-frame", - "%s", "m_pullup(llc) failed"); - ic->ic_stats.is_rx_tooshort++; - return IEEE80211_FC0_TYPE_DATA; - } - llc = (struct llc *)(mtod(m, uint8_t *) + - sizeof(struct ether_header)); - if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { - m_adj(m, FF_LLC_SIZE); - m = ieee80211_decap_fastframe(ic, ni, m); - if (m == NULL) - return IEEE80211_FC0_TYPE_DATA; - } - } -#undef FF_LLC_SIZE - ieee80211_deliver_data(ic, ni, m); - return IEEE80211_FC0_TYPE_DATA; - - case IEEE80211_FC0_TYPE_MGT: - ic->ic_stats.is_rx_mgmt++; - IEEE80211_NODE_STAT(ni, rx_mgmt); - if (dir != IEEE80211_FC1_DIR_NODS) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, - wh, "data", "unknown dir 0x%x", dir); - ic->ic_stats.is_rx_wrongdir++; - goto err; - } - 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; - } -#ifdef IEEE80211_DEBUG - if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || - ieee80211_msg_dumppkts(ic)) { - if_printf(ic->ic_ifp, "received %s from %s rssi %d\n", - ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - ether_sprintf(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; - } - 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; - } - 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; - } - wh = mtod(m, struct ieee80211_frame *); - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - } - if (bpf_peers_present(ic->ic_rawbpf)) - bpf_mtap(ic->ic_rawbpf, m); - (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp); - m_freem(m); - return IEEE80211_FC0_TYPE_MGT; - - case IEEE80211_FC0_TYPE_CTL: - ic->ic_stats.is_rx_ctl++; - IEEE80211_NODE_STAT(ni, rx_ctrl); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_PS_POLL: - ieee80211_recv_pspoll(ic, ni, m); - break; - case IEEE80211_FC0_SUBTYPE_BAR: - ieee80211_recv_bar(ni, m); - break; - } + mcopy = m; + m = NULL; } - goto out; - default: - IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, - wh, NULL, "bad frame type 0x%x", type); - /* should not come here */ - break; + ni = ieee80211_ref_node(vap->iv_bss); + type = ieee80211_input(ni, mcopy, rssi, noise, rstamp); + ieee80211_free_node(ni); } -err: - ifp->if_ierrors++; -out: - if (m != NULL) { - if (bpf_peers_present(ic->ic_rawbpf) && need_tap) - bpf_mtap(ic->ic_rawbpf, m); + if (m != NULL) /* no vaps, reclaim mbuf */ m_freem(m); - } return type; -#undef SEQ_LEQ } /* * This function reassemble fragments. + * + * XXX should handle 3 concurrent reassemblies per-spec. */ -static struct mbuf * -ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m, int hdrspace) +struct mbuf * +ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); struct ieee80211_frame *lwh; uint16_t rxseq; @@ -681,7 +167,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, if (mfrag == NULL) { if (fragno != 0) { /* !first fragment, discard */ - ic->ic_stats.is_rx_defrag++; + vap->iv_stats.is_rx_defrag++; IEEE80211_NODE_STAT(ni, rx_defrag); m_freem(m); return NULL; @@ -705,12 +191,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, } void -ieee80211_deliver_data(struct ieee80211com *ic, +ieee80211_deliver_data(struct ieee80211vap *vap, struct ieee80211_node *ni, struct mbuf *m) { struct ether_header *eh = mtod(m, struct ether_header *); - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; + /* NB: see hostap_deliver_data, this path doesn't handle hostap */ + KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap")); /* * Do accounting. */ @@ -722,66 +210,21 @@ ieee80211_deliver_data(struct ieee80211com *ic, IEEE80211_NODE_STAT(ni, rx_mcast); } else IEEE80211_NODE_STAT(ni, rx_ucast); + m->m_pkthdr.rcvif = ifp; /* clear driver/net80211 flags before passing up */ m->m_flags &= ~M_80211_RX; - /* 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 (m->m_flags & M_MCAST) { - m1 = m_dup(m, M_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) { - int error; - - /* XXX does not work well with WME */ - IFQ_HANDOFF(ifp, m1, error); - } - } - if (m != NULL) { - m->m_pkthdr.rcvif = ifp; - if (ni->ni_vlan != 0) { - /* attach vlan tag */ - m->m_pkthdr.ether_vtag = ni->ni_vlan; - m->m_flags |= M_VLANTAG; - } - (*ifp->if_input)(ifp, m); + if (ni->ni_vlan != 0) { + /* attach vlan tag */ + m->m_pkthdr.ether_vtag = ni->ni_vlan; + m->m_flags |= M_VLANTAG; } + ifp->if_input(ifp, m); } -static struct mbuf * -ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen) +struct mbuf * +ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) { struct ieee80211_qosframe_addr4 wh; /* Max size address frames */ struct ether_header *eh; @@ -916,28 +359,28 @@ ieee80211_decap1(struct mbuf *m, int *framelen) * for delivery. The second frame is returned for delivery * via the normal path. */ -static struct mbuf * -ieee80211_decap_fastframe(struct ieee80211com *ic, - struct ieee80211_node *ni, struct mbuf *m) +struct mbuf * +ieee80211_decap_fastframe(struct ieee80211_node *ni, struct mbuf *m) { #define MS(x,f) (((x) & f) >> f##_S) + struct ieee80211vap *vap = ni->ni_vap; uint32_t ath; struct mbuf *n; int framelen; m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath); if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "unsupport tunnel protocol, header 0x%x", ath); - ic->ic_stats.is_ff_badhdr++; + vap->iv_stats.is_ff_badhdr++; m_freem(m); return NULL; } /* NB: skip header and alignment padding */ m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2); - ic->ic_stats.is_ff_decap++; + vap->iv_stats.is_ff_decap++; /* * Decap the first frame, bust it apart from the @@ -946,21 +389,22 @@ ieee80211_decap_fastframe(struct ieee80211com *ic, */ m = ieee80211_decap1(m, &framelen); if (m == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "%s", "first decap failed"); - ic->ic_stats.is_ff_tooshort++; + vap->iv_stats.is_ff_tooshort++; return NULL; } n = m_split(m, framelen, M_NOWAIT); if (n == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "%s", "unable to split encapsulated frames"); - ic->ic_stats.is_ff_split++; + vap->iv_stats.is_ff_split++; m_freem(m); /* NB: must reclaim */ return NULL; } - ieee80211_deliver_data(ic, ni, m); /* 1st of pair */ + /* XXX not right for WDS */ + vap->iv_deliver_data(vap, ni, m); /* 1st of pair */ /* * Decap second frame. @@ -968,9 +412,9 @@ ieee80211_decap_fastframe(struct ieee80211com *ic, m_adj(n, roundup2(framelen, 4) - framelen); /* padding */ n = ieee80211_decap1(n, &framelen); if (n == NULL) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "fast-frame", "%s", "second decap failed"); - ic->ic_stats.is_ff_tooshort++; + vap->iv_stats.is_ff_tooshort++; } /* XXX verify framelen against mbuf contents */ return n; /* 2nd delivered by caller */ @@ -984,7 +428,7 @@ 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 ieee80211vap *vap = ni->ni_vap; struct ieee80211_rateset *rs = &ni->ni_rates; memset(rs, 0, sizeof(*rs)); @@ -998,11 +442,10 @@ ieee80211_setup_rates(struct ieee80211_node *ni, nxrates = xrates[1]; if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, - "[%s] extended rate set too large;" - " only using %u of %u rates\n", - ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]); - ic->ic_stats.is_rx_rstoobig++; + IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni, + "extended rate set too large; only using " + "%u of %u rates", nxrates, xrates[1]); + vap->iv_stats.is_rx_rstoobig++; } memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); rs->rs_nrates += nxrates; @@ -1010,101 +453,6 @@ ieee80211_setup_rates(struct ieee80211_node *ni, return ieee80211_fix_rate(ni, rs, flags); } -static void -ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, - struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp, - uint16_t seq, uint16_t status) -{ - - 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) { - /* - * Clear any challenge text that may be there if - * a previous shared key auth failed and then an - * open auth is attempted. - */ - if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_80211_NODE); - ni->ni_challenge = NULL; - } - /* 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: - case IEEE80211_M_WDS: - /* 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; - } - /* 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, - "[%s] station authenticated (open)\n", - ether_sprintf(ni->ni_macaddr)); - /* - * 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, - "[%s] open auth failed (reason %d)\n", - ether_sprintf(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, - IEEE80211_SCAN_FAIL_STATUS); - } else - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); - break; - } -} - /* * Send a management frame error response to the specified * station. If ni is associated with the station then use @@ -1112,14 +460,26 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, * 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) +void +ieee80211_send_error(struct ieee80211_node *ni, + const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg) { + struct ieee80211vap *vap = ni->ni_vap; int istmp; - if (ni == ic->ic_bss) { - ni = ieee80211_tmp_node(ic, mac); + if (ni == vap->iv_bss) { + if (vap->iv_state != IEEE80211_S_RUN) { + /* + * XXX hack until we get rid of this routine. + * We can be called prior to the vap reaching + * run state under certain conditions in which + * case iv_bss->ni_chan will not be setup. + * Check for this explicitly and and just ignore + * the request. + */ + return; + } + ni = ieee80211_tmp_node(vap, mac); if (ni == NULL) { /* XXX msg */ return; @@ -1127,2204 +487,342 @@ ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, istmp = 1; } else istmp = 0; - IEEE80211_SEND_MGMT(ic, ni, subtype, arg); + IEEE80211_SEND_MGMT(ni, subtype, arg); if (istmp) ieee80211_free_node(ni); } -static int -alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) +int +ieee80211_alloc_challenge(struct ieee80211_node *ni) { if (ni->ni_challenge == NULL) MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN, M_80211_NODE, M_NOWAIT); if (ni->ni_challenge == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] shared key challenge alloc failed\n", - ether_sprintf(ni->ni_macaddr)); + IEEE80211_NOTE(ni->ni_vap, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni, + "%s", "shared key challenge alloc failed"); /* 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, int noise, 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: - case IEEE80211_M_WDS: - 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_noise = noise; - 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, - "[%s] shared key %sauth request\n", - ether_sprintf(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, - "[%s] station authenticated (shared key)\n", - ether_sprintf(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_80211_NODE); - ni->ni_challenge = NULL; - } - if (status != 0) { - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, - "[%s] shared key auth failed (reason %d)\n", - ether_sprintf(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, 0); - 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, - IEEE80211_SCAN_FAIL_STATUS); - } -} - -/* 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, _action) do { \ - if ((_len) < (_minlen)) { \ - IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ - wh, ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - "ie too short, got %d, expected %d", \ - (_len), (_minlen)); \ - ic->ic_stats.is_rx_elem_toosmall++; \ - _action; \ - } \ -} 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("[%s] discard %s frame, ssid mismatch: ", - ether_sprintf(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 __inline int -iswpaoui(const uint8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); -} - -static __inline int -iswmeoui(const uint8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); -} - -static __inline int -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 __inline int -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 __inline int -isatherosoui(const uint8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); -} - -static __inline int -ishtcapoui(const uint8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI); -} - -static __inline int -ishtinfooui(const uint8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_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<<wpa_cipher(frm, &rsn->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<<IEEE80211_CIPHER_TKIP)) - rsn->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_cipher(frm, &rsn->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<<IEEE80211_CIPHER_TKIP)) - rsn->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 -} - -static int -ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm, - const struct ieee80211_frame *wh) -{ - struct ieee80211com *ic = ni->ni_ic; - const struct ieee80211_ath_ie *ath; - u_int len = frm[1]; - int capschanged; - uint16_t defkeyix; - - if (len < sizeof(struct ieee80211_ath_ie)-2) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG, - wh, "Atheros", "too short, len %u", len); - return -1; - } - ath = (const struct ieee80211_ath_ie *)frm; - capschanged = (ni->ni_ath_flags != ath->ath_capability); - defkeyix = LE_READ_2(ath->ath_defkeyix); - if (capschanged || defkeyix != ni->ni_ath_defkeyix) { - ni->ni_ath_flags = ath->ath_capability; - ni->ni_ath_defkeyix = defkeyix; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, - "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n", - ether_sprintf(ni->ni_macaddr), - ni->ni_ath_flags, ni->ni_ath_defkeyix); - } - if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) { - uint16_t curflags, newflags; - - /* - * Check for turbo mode switch. Calculate flags - * for the new mode and effect the switch. - */ - newflags = curflags = ic->ic_bsschan->ic_flags; - /* NB: BOOST is not in ic_flags, so get it from the ie */ - if (ath->ath_capability & ATHEROS_CAP_BOOST) - newflags |= IEEE80211_CHAN_TURBO; - else - newflags &= ~IEEE80211_CHAN_TURBO; - if (newflags != curflags) - ieee80211_dturbo_switch(ic, newflags); - } - return capschanged; -} - void -ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie) +ieee80211_parse_ath(struct ieee80211_node *ni, uint8_t *ie) { const struct ieee80211_ath_ie *ath = (const struct ieee80211_ath_ie *) ie; ni->ni_ath_flags = ath->ath_capability; ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix); - ieee80211_saveie(&ni->ni_ath_ie, ie); -} - -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_80211_NODE); - MALLOC(*iep, void*, ielen, M_80211_NODE, 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. + * Parse a Beacon or ProbeResponse frame and return the + * useful information in an ieee80211_scanparams structure. + * Status is set to 0 if no problems were found; otherwise + * a bitmask of IEEE80211_BPARSE_* items is returned that + * describes the problems detected. */ -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); - ieee80211_deliver_data(ic, ni, m); -} - -static __inline int -contbgscan(struct ieee80211com *ic) -{ - return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && - time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)); -} - -static __inline int -startbgscan(struct ieee80211com *ic) -{ - return ((ic->ic_flags & IEEE80211_F_BGSCAN) && - !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && - time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) && - time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)); -} - -static void -ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, - int reassoc, int resp, const char *tag, int rate) -{ - struct ieee80211com *ic = ni->ni_ic; - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n", - ether_sprintf(wh->i_addr2), - reassoc ? "reassoc" : "assoc", tag, rate); - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); - ieee80211_node_leave(ic, ni); - ic->ic_stats.is_rx_assoc_norate++; -} - -static void -capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, - int reassoc, int resp, const char *tag, int capinfo) -{ - struct ieee80211com *ic = ni->ni_ic; - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] deny %s request, %s mismatch 0x%x\n", - ether_sprintf(wh->i_addr2), - reassoc ? "reassoc" : "assoc", tag, capinfo); - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO); - ieee80211_node_leave(ic, ni); - ic->ic_stats.is_rx_assoc_capmismatch++; -} - -static void -htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, - int reassoc, int resp) +int +ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, + struct ieee80211_scanparams *scan) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - - IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2, - "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc"); - /* XXX no better code */ - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER); - ieee80211_node_leave(ic, ni); -} - -void -ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_node *ni, - int subtype, int rssi, int noise, 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, *rsn, *wme, *ath, *htcap, *htinfo; - int reassoc, resp, allocbs; - uint8_t rate; - wh = mtod(m0, struct ieee80211_frame *); + wh = mtod(m, 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 - * [tlv] supported rates - * [tlv] country information - * [tlv] parameter set (FH/DS) - * [tlv] erp information - * [tlv] extended supported rates - * [tlv] WME - * [tlv] WPA or RSN - * [tlv] HT capabilities - * [tlv] HT information - * [tlv] Atheros capabilities - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return); - 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_curchan); - scan.curchan = ic->ic_curchan; - - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_SSID: - scan.ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - scan.rates = frm; - break; - case IEEE80211_ELEMID_COUNTRY: - scan.country = frm; - break; - case IEEE80211_ELEMID_FHPARMS: - if (ic->ic_phytype == IEEE80211_T_FH) { - scan.fhdwell = LE_READ_2(&frm[2]); - scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]); - scan.fhindex = frm[6]; - } - break; - case IEEE80211_ELEMID_DSPARMS: - /* - * XXX hack this since depending on phytype - * is problematic for multi-mode devices. - */ - if (ic->ic_phytype != IEEE80211_T_FH) - scan.bchan = 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: - scan.xrates = frm; - break; - case IEEE80211_ELEMID_ERP: - if (frm[1] != 1) { - IEEE80211_DISCARD_IE(ic, - IEEE80211_MSG_ELEMID, wh, "ERP", - "bad len %u", frm[1]); - ic->ic_stats.is_rx_elem_toobig++; - break; - } - scan.erp = frm[2]; - break; - case IEEE80211_ELEMID_HTCAP: - scan.htcap = frm; - break; - case IEEE80211_ELEMID_RSN: - scan.rsn = frm; - break; - case IEEE80211_ELEMID_HTINFO: - scan.htinfo = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (iswpaoui(frm)) - scan.wpa = frm; - else if (iswmeparam(frm) || iswmeinfo(frm)) - scan.wme = frm; - else if (isatherosoui(frm)) - scan.ath = frm; - else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { - /* - * Accept pre-draft HT ie's if the - * standard ones have not been seen. - */ - if (ishtcapoui(frm)) { - if (scan.htcap == NULL) - scan.htcap = frm; - } else if (ishtinfooui(frm)) { - if (scan.htinfo == NULL) - scan.htcap = frm; - } - } - break; - default: - 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(scan.rates, IEEE80211_RATE_MAXSIZE); - if (scan.xrates != NULL) - IEEE80211_VERIFY_ELEMENT(scan.xrates, - IEEE80211_RATE_MAXSIZE - scan.rates[1]); - IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN); -#if IEEE80211_CHAN_MAX < 255 - if (scan.chan > IEEE80211_CHAN_MAX) { - IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "invalid channel %u", scan.chan); - ic->ic_stats.is_rx_badchan++; - return; - } -#endif - if (IEEE80211_CHAN2IEEE(scan.curchan) != 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; - * silently discard it. - * - * NB: this can happen due to signal leakage. - * But we should take it for FH phy because - * the rssi value should be correct even for - * different hop pattern in FH. - */ - IEEE80211_DISCARD(ic, - IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, - wh, ieee80211_mgt_subtype_name[subtype >> - IEEE80211_FC0_SUBTYPE_SHIFT], - "for off-channel %u", - IEEE80211_CHAN2IEEE(scan.curchan)); - 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; - } - /* - * Process HT ie's. This is complicated by our - * accepting both the standard ie's and the pre-draft - * vendor OUI ie's that some vendors still use/require. - */ - if (scan.htcap != NULL) { - IEEE80211_VERIFY_LENGTH(scan.htcap[1], - scan.htcap[0] == IEEE80211_ELEMID_VENDOR ? - 4 + sizeof(struct ieee80211_ie_htcap)-2 : - sizeof(struct ieee80211_ie_htcap)-2, - scan.htcap = NULL); - } - if (scan.htinfo != NULL) { - IEEE80211_VERIFY_LENGTH(scan.htinfo[1], - scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ? - 4 + sizeof(struct ieee80211_ie_htinfo)-2 : - sizeof(struct ieee80211_ie_htinfo)-2, - scan.htinfo = NULL); - } - - /* - * Count frame now that we know it's to be processed. - */ - if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { - ic->ic_stats.is_rx_beacon++; /* XXX remove */ - IEEE80211_NODE_STAT(ni, rx_beacons); - } else - IEEE80211_NODE_STAT(ni, rx_proberesp); - - /* - * 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_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, - "[%s] erp change: was 0x%x, now 0x%x\n", - ether_sprintf(wh->i_addr2), - ni->ni_erp, scan.erp); - if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && - (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, - "[%s] capabilities change: before 0x%x," - " now 0x%x\n", - ether_sprintf(wh->i_addr2), - ni->ni_capinfo, scan.capinfo); - /* - * NB: we assume short preamble doesn't - * change dynamically - */ - ieee80211_set_shortslottime(ic, - IEEE80211_IS_CHAN_A(ic->ic_bsschan) || - (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); - ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) - | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); - /* 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.ath != NULL) - ieee80211_parse_athparams(ni, scan.ath, wh); - if (scan.htcap != NULL) - ieee80211_parse_htcap(ni, scan.htcap); - if (scan.htinfo != NULL) { - ieee80211_parse_htinfo(ni, scan.htinfo); - if (ni->ni_chan != ic->ic_bsschan) { - /* - * Channel has been adjusted based on - * negotiated HT parameters; force the - * channel state to follow. - */ - ieee80211_setbsschan(ic, ni->ni_chan); - } - } - if (scan.tim != NULL) { - struct ieee80211_tim_ie *tim = - (struct ieee80211_tim_ie *) scan.tim; -#if 0 - int aid = IEEE80211_AID(ni->ni_associd); - int ix = aid / NBBY; - int min = tim->tim_bitctl &~ 1; - int max = tim->tim_len + min - 4; - if ((tim->tim_bitctl&1) || - (min <= ix && ix <= max && - isset(tim->tim_bitmap - min, aid))) { - /* - * XXX Do not let bg scan kick off - * we are expecting data. - */ - ic->ic_lastdata = ticks; - ieee80211_sta_pwrsave(ic, 0); - } -#endif - ni->ni_dtim_count = tim->tim_count; - ni->ni_dtim_period = tim->tim_period; + efrm = mtod(m, uint8_t *) + m->m_len; + scan->status = 0; + /* + * beacon/probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] capability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] country information + * [tlv] parameter set (FH/DS) + * [tlv] erp information + * [tlv] extended supported rates + * [tlv] WME + * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Atheros capabilities + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 12, + return (scan->status = IEEE80211_BPARSE_BADIELEN)); + 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; + scan->ies = frm; + scan->ies_len = efrm - frm; + + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, + return (scan->status = IEEE80211_BPARSE_BADIELEN)); + switch (*frm) { + case IEEE80211_ELEMID_SSID: + scan->ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + scan->rates = frm; + break; + case IEEE80211_ELEMID_COUNTRY: + scan->country = frm; + break; + case IEEE80211_ELEMID_FHPARMS: + if (ic->ic_phytype == IEEE80211_T_FH) { + 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: /* - * If scanning, pass the info to the scan module. - * Otherwise, check if it's the right time to do - * a background scan. Background scanning must - * be enabled and we must not be operating in the - * turbo phase of dynamic turbo mode. Then, - * it's been a while since the last background - * scan and if no data frames have come through - * recently, kick off a scan. Note that this - * is the mechanism by which a background scan - * is started _and_ continued each time we - * return on-channel to receive a beacon from - * our ap. + * XXX hack this since depending on phytype + * is problematic for multi-mode devices. */ - if (ic->ic_flags & IEEE80211_F_SCAN) { - ieee80211_add_scan(ic, &scan, wh, - subtype, rssi, noise, rstamp); - } else if (contbgscan(ic)) { - ieee80211_bg_scan(ic); - } else if (startbgscan(ic)) { -#if 0 - /* wakeup if we are sleeing */ - ieee80211_set_pwrsave(ic, 0); -#endif - ieee80211_bg_scan(ic); - } - 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, noise, 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_noise = noise; - ni->ni_rstamp = rstamp; - } - } - break; - } - - 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 (IEEE80211_IS_MULTICAST(wh->i_addr2)) { - /* frame must be directed */ - ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ - return; - } - - /* - * prreq frame format - * [tlv] ssid - * [tlv] supported rates - * [tlv] extended supported rates - * [tlv] Atheros capabilities - */ - ssid = rates = xrates = ath = NULL; - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_XRATES: - xrates = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (isatherosoui(frm)) - ath = frm; + if (ic->ic_phytype != IEEE80211_T_FH) + scan->chan = frm[2]; + break; + case IEEE80211_ELEMID_TIM: + /* XXX ATIM? */ + scan->tim = frm; + scan->timoff = frm - mtod(m, uint8_t *); + break; + case IEEE80211_ELEMID_IBSSPARMS: + case IEEE80211_ELEMID_CFPARMS: + /* NB: avoid debugging complaints */ + break; + case IEEE80211_ELEMID_XRATES: + scan->xrates = frm; + break; + case IEEE80211_ELEMID_ERP: + if (frm[1] != 1) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID, wh, "ERP", + "bad len %u", frm[1]); + vap->iv_stats.is_rx_elem_toobig++; break; } - frm += frm[1] + 2; - } - IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); - if (xrates != NULL) - IEEE80211_VERIFY_ELEMENT(xrates, - IEEE80211_RATE_MAXSIZE - rates[1]); - IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - 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) { - 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)) { + scan->erp = frm[2] | 0x100; + break; + case IEEE80211_ELEMID_HTCAP: + scan->htcap = frm; + break; + case IEEE80211_ELEMID_RSN: + scan->rsn = frm; + break; + case IEEE80211_ELEMID_HTINFO: + scan->htinfo = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + scan->wpa = frm; + else if (iswmeparam(frm) || iswmeinfo(frm)) + scan->wme = frm; + else if (isatherosoui(frm)) + scan->ath = frm; + else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) { /* - * 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. + * Accept pre-draft HT ie's if the + * standard ones have not been seen. */ - ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta, - wh->i_addr2); - } - if (ni == NULL) - return; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] recv probe req\n", ether_sprintf(wh->i_addr2)); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - 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_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) { - /* - * Temporary node created just to send a - * response, reclaim immediately. - */ - ieee80211_free_node(ni); - } else if (ath != NULL) - ieee80211_saveath(ni, ath); - break; - - case IEEE80211_FC0_SUBTYPE_AUTH: { - uint16_t algo, seq, status; - /* - * auth frame format - * [2] algorithm - * [2] sequence - * [2] status - * [tlv*] challenge - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); - algo = le16toh(*(uint16_t *)frm); - seq = le16toh(*(uint16_t *)(frm + 2)); - status = le16toh(*(uint16_t *)(frm + 4)); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] recv auth frame with algorithm %d seq %d\n", - ether_sprintf(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; - } - 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); - } - return; - } - if (algo == IEEE80211_AUTH_ALG_SHARED) - ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, - noise, rstamp, seq, status); - else if (algo == IEEE80211_AUTH_ALG_OPEN) - ieee80211_auth_open(ic, wh, ni, rssi, noise, 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)); - } - return; - } - break; - } - - case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: - case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { - uint16_t capinfo, lintval; - struct ieee80211_rsnparms rsnparms; - uint8_t reason; - int badwparsn; - - if (ic->ic_opmode != IEEE80211_M_HOSTAP || - ic->ic_state != IEEE80211_S_RUN) { - ic->ic_stats.is_rx_mgtdiscard++; - return; - } - - if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { - reassoc = 1; - resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; - } else { - reassoc = 0; - resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; - } - /* - * asreq frame format - * [2] capability information - * [2] listen interval - * [6*] current AP address (reassoc only) - * [tlv] ssid - * [tlv] supported rates - * [tlv] extended supported rates - * [tlv] WPA or RSN - * [tlv] HT capabilities - * [tlv] Atheros capabilities - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); - if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { - 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; - lintval = le16toh(*(uint16_t *)frm); frm += 2; - if (reassoc) - frm += 6; /* ignore current AP info */ - ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_XRATES: - xrates = frm; - break; - /* XXX verify only one of RSN and WPA ie's? */ - case IEEE80211_ELEMID_RSN: - rsn = frm; - break; - case IEEE80211_ELEMID_HTCAP: - htcap = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (iswpaoui(frm)) - wpa = frm; - else if (iswmeinfo(frm)) - wme = frm; - else if (isatherosoui(frm)) - ath = frm; - else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { - if (ishtcapoui(frm) && htcap == NULL) - htcap = frm; + if (ishtcapoui(frm)) { + if (scan->htcap == NULL) + scan->htcap = frm; + } else if (ishtinfooui(frm)) { + if (scan->htinfo == NULL) + scan->htcap = frm; } - break; } - frm += frm[1] + 2; - } - IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); - if (xrates != NULL) - IEEE80211_VERIFY_ELEMENT(xrates, - IEEE80211_RATE_MAXSIZE - rates[1]); - IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); - if (htcap != NULL) { - IEEE80211_VERIFY_LENGTH(htcap[1], - htcap[0] == IEEE80211_ELEMID_VENDOR ? - 4 + sizeof(struct ieee80211_ie_htcap)-2 : - sizeof(struct ieee80211_ie_htcap)-2, - return); /* XXX just NULL out? */ - } - - if (ni == ic->ic_bss) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] deny %s request, sta not authenticated\n", - ether_sprintf(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; - } - /* assert right association security credentials */ - badwparsn = 0; - switch (ic->ic_flags & IEEE80211_F_WPA) { - case IEEE80211_F_WPA1: - if (wpa == NULL) - badwparsn = 1; - break; - case IEEE80211_F_WPA2: - if (rsn == NULL) - badwparsn = 1; break; - case IEEE80211_F_WPA1|IEEE80211_F_WPA2: - if (wpa == NULL && rsn == NULL) - badwparsn = 1; + default: + IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID, + wh, "unhandled", + "id %u, len %u", *frm, frm[1]); + vap->iv_stats.is_rx_elem_unknown++; break; } - if (badwparsn) { - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, - "[%s] no WPA/RSN IE in association request\n", - ether_sprintf(wh->i_addr2)); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_IE_INVALID); - ieee80211_node_leave(ic, ni); - ic->ic_stats.is_rx_assoc_badwpaie++; - return; - } - if (wpa != NULL || rsn != NULL) { - /* - * Parse WPA/RSN 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. - */ - rsnparms = ni->ni_rsn; - if (wpa != NULL) - reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh); - else - reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh); - if (reason != 0) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, reason); - ieee80211_node_leave(ic, ni); - /* XXX distinguish WPA/RSN? */ - ic->ic_stats.is_rx_assoc_badwpaie++; - return; - } - IEEE80211_DPRINTF(ic, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, - "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", - ether_sprintf(wh->i_addr2), - wpa != NULL ? "WPA" : "RSN", - rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen, - rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen, - rsnparms.rsn_keymgmt, rsnparms.rsn_caps); - } - /* discard challenge after association */ - if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_80211_NODE); - ni->ni_challenge = NULL; - } - /* NB: 802.11 spec says to ignore station's privacy bit */ - if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { - capinfomismatch(ni, wh, reassoc, resp, - "capability", capinfo); - return; - } - /* - * Disallow re-associate w/ invalid slot time setting. - */ - if (ni->ni_associd != 0 && - IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && - ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { - capinfomismatch(ni, wh, reassoc, resp, - "slot time", capinfo); - return; - } - rate = ieee80211_setup_rates(ni, rates, xrates, - IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (rate & IEEE80211_RATE_BASIC) { - ratesetmismatch(ni, wh, reassoc, resp, "basic", rate); - return; - } - /* - * 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 ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) { - ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); - return; - } - /* XXX enforce PUREN */ - /* 802.11n-specific rateset handling */ - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) { - rate = ieee80211_setup_htrates(ni, htcap, - IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | - IEEE80211_F_DOBRS); - if (rate & IEEE80211_RATE_BASIC) { - /* XXX 11n-specific stat */ - ratesetmismatch(ni, wh, reassoc, resp, - "HT", rate); - return; - } - ieee80211_ht_node_init(ni, htcap); - } else if (ni->ni_flags & IEEE80211_NODE_HT) - ieee80211_ht_node_cleanup(ni); - /* - * Allow AMPDU operation only with unencrypted traffic - * or AES-CCM; the 11n spec only specifies these ciphers - * so permitting any others is undefined and can lead - * to interoperability problems. - * - * NB: We check for AES by looking at the GTK cipher - * since the WPA/11i specs say the PTK cipher has - * to be "as good or better". - */ - if ((ni->ni_flags & IEEE80211_NODE_HT) && - (((ic->ic_flags & IEEE80211_F_WPA) && - rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) || - (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { - IEEE80211_NOTE(ic, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, - "disallow HT use because WEP or TKIP requested, " - "capinfo 0x%x mcastcipher %d", capinfo, - rsnparms.rsn_mcastcipher); - ieee80211_ht_node_cleanup(ni); - ic->ic_stats.is_ht_assoc_downgrade++; - } - /* - * If constrained to 11n-only stations reject legacy stations. - */ - if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) && - (ni->ni_flags & IEEE80211_NODE_HT) == 0) { - htcapmismatch(ni, wh, reassoc, resp); - ic->ic_stats.is_ht_assoc_nohtcap++; - return; - } - ni->ni_rssi = rssi; - ni->ni_noise = noise; - ni->ni_rstamp = rstamp; - ni->ni_intval = lintval; - ni->ni_capinfo = capinfo; - ni->ni_chan = ic->ic_bsschan; - ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; - ni->ni_fhindex = ic->ic_bss->ni_fhindex; - if (wpa != NULL) { - /* - * Record WPA parameters for station, mark - * node as using WPA and record information element - * for applications that require it. - */ - ni->ni_rsn = rsnparms; - 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_80211_NODE); - ni->ni_wpa_ie = NULL; - } - if (rsn != NULL) { - /* - * Record RSN parameters for station, mark - * node as using WPA and record information element - * for applications that require it. - */ - ni->ni_rsn = rsnparms; - ieee80211_saveie(&ni->ni_rsn_ie, rsn); - } else if (ni->ni_rsn_ie != NULL) { - /* - * Flush any state from a previous association. - */ - FREE(ni->ni_rsn_ie, M_80211_NODE); - ni->ni_rsn_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_80211_NODE); - ni->ni_wme_ie = NULL; - ni->ni_flags &= ~IEEE80211_NODE_QOS; - } - if (ath != NULL) { - /* - * Record ATH parameters for station, mark - * node with appropriate capabilities, and - * record the information element for - * applications that require it. - */ - ieee80211_saveath(ni, ath); - } else if (ni->ni_ath_ie != NULL) { - /* - * Flush any state from a previous association. - */ - FREE(ni->ni_ath_ie, M_80211_NODE); - ni->ni_ath_ie = NULL; - ni->ni_ath_flags = 0; - } - ieee80211_node_join(ic, ni, resp); - ieee80211_deliver_l2uf(ni); - break; + frm += frm[1] + 2; } - - 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_stats.is_rx_mgtdiscard++; - return; - } - - /* - * asresp frame format - * [2] capability information - * [2] status - * [2] association ID - * [tlv] supported rates - * [tlv] extended supported rates - * [tlv] WME - * [tlv] HT capabilities - * [tlv] HT info - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); - ni = ic->ic_bss; - capinfo = le16toh(*(uint16_t *)frm); - frm += 2; - status = le16toh(*(uint16_t *)frm); - frm += 2; - if (status != 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] %sassoc failed (reason %d)\n", - ether_sprintf(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++; /* XXX */ - return; - } - associd = le16toh(*(uint16_t *)frm); - frm += 2; - - rates = xrates = wme = htcap = htinfo = NULL; - while (efrm - frm > 1) { - IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); - switch (*frm) { - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_XRATES: - xrates = frm; - break; - case IEEE80211_ELEMID_HTCAP: - htcap = frm; - break; - case IEEE80211_ELEMID_HTINFO: - htinfo = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (iswmeoui(frm)) - wme = frm; - else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { - /* - * Accept pre-draft HT ie's if the - * standard ones have not been seen. - */ - if (ishtcapoui(frm)) { - if (htcap == NULL) - htcap = frm; - } else if (ishtinfooui(frm)) { - if (htinfo == NULL) - htcap = frm; - } - } - /* XXX Atheros OUI support */ - break; - } - frm += frm[1] + 2; - } - - IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); - if (xrates != NULL) - IEEE80211_VERIFY_ELEMENT(xrates, - IEEE80211_RATE_MAXSIZE - rates[1]); - rate = ieee80211_setup_rates(ni, rates, xrates, - IEEE80211_F_JOIN | - IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (rate & IEEE80211_RATE_BASIC) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] %sassoc failed (rate set mismatch)\n", - ether_sprintf(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, - IEEE80211_SCAN_FAIL_STATUS); - 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; + IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE, + scan->status |= IEEE80211_BPARSE_RATES_INVALID); + if (scan->rates != NULL && scan->xrates != NULL) { /* - * Setup HT state according to the negotiation. + * NB: don't process XRATES if RATES is missing. This + * avoids a potential null ptr deref and should be ok + * as the return code will already note RATES is missing + * (so callers shouldn't otherwise process the frame). */ - if ((ic->ic_htcaps & IEEE80211_HTC_HT) && - htcap != NULL && htinfo != NULL) { - ieee80211_ht_node_init(ni, htcap); - ieee80211_parse_htinfo(ni, htinfo); - ieee80211_setup_htrates(ni, - htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS); - ieee80211_setup_basic_htrates(ni, htinfo); - if (ni->ni_chan != ic->ic_bsschan) { - /* - * Channel has been adjusted based on - * negotiated HT parameters; force the - * channel state to follow. - */ - ieee80211_setbsschan(ic, ni->ni_chan); - } - } - /* - * Configure state now that we are associated. - * - * XXX may need different/additional driver callbacks? - */ - if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || - (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, - IEEE80211_IS_CHAN_A(ic->ic_curchan) || - (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + IEEE80211_VERIFY_ELEMENT(scan->xrates, + IEEE80211_RATE_MAXSIZE - scan->rates[1], + scan->status |= IEEE80211_BPARSE_XRATES_INVALID); + } + IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN, + scan->status |= IEEE80211_BPARSE_SSID_INVALID); +#if IEEE80211_CHAN_MAX < 255 + if (scan->chan > IEEE80211_CHAN_MAX) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, + wh, NULL, "invalid channel %u", scan->chan); + vap->iv_stats.is_rx_badchan++; + scan->status |= IEEE80211_BPARSE_CHAN_INVALID; + } +#endif + if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) { /* - * Honor ERP protection. + * Frame was received on a channel different from the + * one indicated in the DS params element id; + * silently discard it. * - * NB: ni_erp should zero for non-11g operation. - */ - if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && - (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, - "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n", - ether_sprintf(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" : "", - ni->ni_flags & IEEE80211_NODE_HT ? - (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "", - ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", - IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ? - ", fast-frames" : "", - IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ? - ", turbo" : "" - ); - ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); - break; + * NB: this can happen due to signal leakage. + * But we should take it for FH phy because + * the rssi value should be correct even for + * different hop pattern in FH. + */ + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, NULL, "for off-channel %u", scan->chan); + vap->iv_stats.is_rx_chanmismatch++; + scan->status |= IEEE80211_BPARSE_OFFCHAN; + } + if (!(IEEE80211_BINTVAL_MIN <= scan->bintval && + scan->bintval <= IEEE80211_BINTVAL_MAX)) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, + wh, NULL, "bogus beacon interval", scan->bintval); + vap->iv_stats.is_rx_badbintval++; + scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID; + } + if (scan->country != NULL) { + /* + * Validate we have at least enough data to extract + * the country code. Not sure if we should return an + * error instead of discarding the IE; consider this + * being lenient as we don't depend on the data for + * correct operation. + */ + IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t), + scan->country = NULL); } - - 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 - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); - reason = le16toh(*(uint16_t *)frm); - ic->ic_stats.is_rx_deauth++; - IEEE80211_NODE_STAT(ni, rx_deauth); - - if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) { - /* NB: can happen when in promiscuous mode */ - ic->ic_stats.is_rx_mgtdiscard++; - break; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] recv deauthenticate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), reason); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ieee80211_new_state(ic, IEEE80211_S_AUTH, - (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); - break; - case IEEE80211_M_HOSTAP: - if (ni != ic->ic_bss) - ieee80211_node_leave(ic, ni); - break; - default: - ic->ic_stats.is_rx_mgtdiscard++; - break; - } - break; + /* + * Process HT ie's. This is complicated by our + * accepting both the standard ie's and the pre-draft + * vendor OUI ie's that some vendors still use/require. + */ + if (scan->htcap != NULL) { + IEEE80211_VERIFY_LENGTH(scan->htcap[1], + scan->htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + scan->htcap = NULL); } + if (scan->htinfo != NULL) { + IEEE80211_VERIFY_LENGTH(scan->htinfo[1], + scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htinfo)-2 : + sizeof(struct ieee80211_ie_htinfo)-2, + scan->htinfo = NULL); + } + return scan->status; +} - 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 - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); - reason = le16toh(*(uint16_t *)frm); - ic->ic_stats.is_rx_disassoc++; - IEEE80211_NODE_STAT(ni, rx_disassoc); +/* + * Parse an Action frame. Return 0 on success, non-zero on failure. + */ +int +ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_action *ia; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm; - if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) { - /* NB: can happen when in promiscuous mode */ - ic->ic_stats.is_rx_mgtdiscard++; - break; - } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] recv disassociate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), reason); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0); + /* + * action frame format: + * [1] category + * [1] action + * [tlv] parameters + */ + wh = mtod(m, struct ieee80211_frame *); + frm = (u_int8_t *)&wh[1]; + efrm = mtod(m, u_int8_t *) + m->m_len; + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action), return EINVAL); + ia = (const struct ieee80211_action *) frm; + + vap->iv_stats.is_rx_action++; + IEEE80211_NODE_STAT(ni, rx_action); + + /* verify frame payloads but defer processing */ + /* XXX maybe push this to method */ + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_BA: + switch (ia->ia_action) { + case IEEE80211_ACTION_BA_ADDBA_REQUEST: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbarequest), + return EINVAL); break; - case IEEE80211_M_HOSTAP: - if (ni != ic->ic_bss) - ieee80211_node_leave(ic, ni); + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_addbaresponse), + return EINVAL); break; - default: - ic->ic_stats.is_rx_mgtdiscard++; + case IEEE80211_ACTION_BA_DELBA: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ba_delba), + return EINVAL); break; } break; - } - - case IEEE80211_FC0_SUBTYPE_ACTION: { - const struct ieee80211_action *ia; - - 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; - } - /* - * action frame format: - * [1] category - * [1] action - * [tlv] parameters - */ - IEEE80211_VERIFY_LENGTH(efrm - frm, - sizeof(struct ieee80211_action), return); - ia = (const struct ieee80211_action *) frm; - - ic->ic_stats.is_rx_action++; - IEEE80211_NODE_STAT(ni, rx_action); - - /* verify frame payloads but defer processing */ - /* XXX maybe push this to method */ - switch (ia->ia_category) { - case IEEE80211_ACTION_CAT_BA: - switch (ia->ia_action) { - case IEEE80211_ACTION_BA_ADDBA_REQUEST: - IEEE80211_VERIFY_LENGTH(efrm - frm, - sizeof(struct ieee80211_action_ba_addbarequest), - return); - break; - case IEEE80211_ACTION_BA_ADDBA_RESPONSE: - IEEE80211_VERIFY_LENGTH(efrm - frm, - sizeof(struct ieee80211_action_ba_addbaresponse), - return); - break; - case IEEE80211_ACTION_BA_DELBA: - IEEE80211_VERIFY_LENGTH(efrm - frm, - sizeof(struct ieee80211_action_ba_delba), - return); - break; - } + case IEEE80211_ACTION_CAT_HT: + switch (ia->ia_action) { + case IEEE80211_ACTION_HT_TXCHWIDTH: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ht_txchwidth), + return EINVAL); break; - case IEEE80211_ACTION_CAT_HT: - switch (ia->ia_action) { - case IEEE80211_ACTION_HT_TXCHWIDTH: - IEEE80211_VERIFY_LENGTH(efrm - frm, - sizeof(struct ieee80211_action_ht_txchwidth), - return); - break; - } + case IEEE80211_ACTION_HT_MIMOPWRSAVE: + IEEE80211_VERIFY_LENGTH(efrm - frm, + sizeof(struct ieee80211_action_ht_mimopowersave), + return EINVAL); break; } - ic->ic_recv_action(ni, frm, efrm); break; } - - default: - 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 - -/* - * 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; - - 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, - "[%s] recv ps-poll, but queue empty\n", - ether_sprintf(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, - "[%s] recv ps-poll, send packet, %u still queued\n", - ether_sprintf(ni->ni_macaddr), qlen); - m->m_flags |= M_MORE_DATA; - } else { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] recv ps-poll, send packet, queue empty\n", - ether_sprintf(ni->ni_macaddr)); - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); - } - m->m_flags |= M_PWR_SAV; /* bypass PS handling */ - IF_ENQUEUE(&ic->ic_ifp->if_snd, m); + return 0; } #ifdef IEEE80211_DEBUG /* * Debugging support. */ +void +ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag, + uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) +{ + printf("[%s] discard %s frame, ssid mismatch: ", + ether_sprintf(mac), tag); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf("\n"); +} /* * Return the bssid of a frame. */ static const uint8_t * -ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) +ieee80211_getbssid(struct ieee80211vap *vap, const struct ieee80211_frame *wh) { - if (ic->ic_opmode == IEEE80211_M_STA) + if (vap->iv_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; @@ -3333,8 +831,10 @@ ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) return wh->i_addr3; } +#include <machine/stdarg.h> + void -ieee80211_note(struct ieee80211com *ic, const char *fmt, ...) +ieee80211_note(struct ieee80211vap *vap, const char *fmt, ...) { char buf[128]; /* XXX */ va_list ap; @@ -3343,11 +843,11 @@ ieee80211_note(struct ieee80211com *ic, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */ + if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */ } void -ieee80211_note_frame(struct ieee80211com *ic, +ieee80211_note_frame(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const char *fmt, ...) { @@ -3357,12 +857,12 @@ ieee80211_note_frame(struct ieee80211com *ic, va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - if_printf(ic->ic_ifp, "[%s] %s\n", - ether_sprintf(ieee80211_getbssid(ic, wh)), buf); + if_printf(vap->iv_ifp, "[%s] %s\n", + ether_sprintf(ieee80211_getbssid(vap, wh)), buf); } void -ieee80211_note_mac(struct ieee80211com *ic, +ieee80211_note_mac(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...) { @@ -3372,22 +872,24 @@ ieee80211_note_mac(struct ieee80211com *ic, va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf); + if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf); } void -ieee80211_discard_frame(struct ieee80211com *ic, +ieee80211_discard_frame(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const char *type, const char *fmt, ...) { va_list ap; - printf("[%s:%s] discard ", ic->ic_ifp->if_xname, - ether_sprintf(ieee80211_getbssid(ic, wh))); - if (type != NULL) + if_printf(vap->iv_ifp, "[%s] discard ", + ether_sprintf(ieee80211_getbssid(vap, wh))); + if (type == NULL) { + printf("%s frame, ", ieee80211_mgt_subtype_name[ + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT]); + } else printf("%s frame, ", type); - else - printf("frame, "); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); @@ -3395,14 +897,14 @@ ieee80211_discard_frame(struct ieee80211com *ic, } void -ieee80211_discard_ie(struct ieee80211com *ic, +ieee80211_discard_ie(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const char *type, const char *fmt, ...) { va_list ap; - printf("[%s:%s] discard ", ic->ic_ifp->if_xname, - ether_sprintf(ieee80211_getbssid(ic, wh))); + if_printf(vap->iv_ifp, "[%s] discard ", + ether_sprintf(ieee80211_getbssid(vap, wh))); if (type != NULL) printf("%s information element, ", type); else @@ -3414,13 +916,13 @@ ieee80211_discard_ie(struct ieee80211com *ic, } void -ieee80211_discard_mac(struct ieee80211com *ic, +ieee80211_discard_mac(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...) { va_list ap; - printf("[%s:%s] discard ", ic->ic_ifp->if_xname, ether_sprintf(mac)); + if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac)); if (type != NULL) printf("%s frame, ", type); else diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h new file mode 100644 index 0000000..dd41d7c --- /dev/null +++ b/sys/net80211/ieee80211_input.h @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_INPUT_H_ +#define _NET80211_IEEE80211_INPUT_H_ + +/* Verify the existence and length of __elem or get out. */ +#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen, _action) do { \ + if ((__elem) == NULL) { \ + IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \ + wh, NULL, "%s", "no " #__elem ); \ + vap->iv_stats.is_rx_elem_missing++; \ + _action; \ + } else if ((__elem)[1] > (__maxlen)) { \ + IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \ + wh, NULL, "bad " #__elem " len %d", (__elem)[1]); \ + vap->iv_stats.is_rx_elem_toobig++; \ + _action; \ + } \ +} while (0) + +#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \ + if ((_len) < (_minlen)) { \ + IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \ + wh, NULL, "ie too short, got %d, expected %d", \ + (_len), (_minlen)); \ + vap->iv_stats.is_rx_elem_toosmall++; \ + _action; \ + } \ +} while (0) + +#ifdef IEEE80211_DEBUG +void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag, + uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid); + +#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \ + if ((_ssid)[1] != 0 && \ + ((_ssid)[1] != (_ni)->ni_esslen || \ + memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ + if (ieee80211_msg_input(vap)) \ + ieee80211_ssid_mismatch(vap, \ + ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + wh->i_addr2, _ssid); \ + vap->iv_stats.is_rx_ssidmismatch++; \ + _action; \ + } \ +} while (0) +#else /* !IEEE80211_DEBUG */ +#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \ + if ((_ssid)[1] != 0 && \ + ((_ssid)[1] != (_ni)->ni_esslen || \ + memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ + vap->iv_stats.is_rx_ssidmismatch++; \ + _action; \ + } \ +} 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 __inline int +iswpaoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static __inline int +iswmeoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); +} + +static __inline int +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 __inline int +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 __inline int +isatherosoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); +} + +static __inline int +ishtcapoui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI); +} + +static __inline int +ishtinfooui(const uint8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI); +} + +void ieee80211_deliver_data(struct ieee80211vap *, + struct ieee80211_node *, struct mbuf *); +struct mbuf *ieee80211_defrag(struct ieee80211_node *, + struct mbuf *, int); +struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int); +struct mbuf *ieee80211_decap1(struct mbuf *, int *); +struct mbuf *ieee80211_decap_fastframe(struct ieee80211_node *, + struct mbuf *); +int ieee80211_setup_rates(struct ieee80211_node *ni, + const uint8_t *rates, const uint8_t *xrates, int flags); +void ieee80211_send_error(struct ieee80211_node *, + const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg); +int ieee80211_alloc_challenge(struct ieee80211_node *); +void ieee80211_parse_ath(struct ieee80211_node *, uint8_t *); +int ieee80211_parse_beacon(struct ieee80211_node *, struct mbuf *, + struct ieee80211_scanparams *); +int ieee80211_parse_action(struct ieee80211_node *, struct mbuf *); +#endif /* _NET80211_IEEE80211_INPUT_H_ */ diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 83627e8..3b088b1 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,14 +27,13 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include "opt_compat.h" - /* * IEEE 802.11 ioctl support (FreeBSD-specific) */ #include "opt_inet.h" #include "opt_ipx.h" +#include "opt_wlan.h" #include <sys/endian.h> #include <sys/param.h> @@ -61,33 +60,21 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ioctl.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_input.h> -#define IS_UP(_ic) \ - (((_ic)->ic_ifp->if_flags & IFF_UP) && \ - ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING)) -#define IS_UP_AUTO(_ic) \ - (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) -#define RESCAN 1 +#define IS_UP_AUTO(_vap) \ + (IFNET_IS_UP_RUNNING(vap->iv_ifp) && \ + (_vap)->iv_roaming == IEEE80211_ROAMING_AUTO) +static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; static struct ieee80211_channel *findchannel(struct ieee80211com *, int ieee, int mode); -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) +static __noinline int +ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; struct ieee80211req_key ik; struct ieee80211_key *wk; @@ -102,26 +89,26 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) return error; kid = ik.ik_keyix; if (kid == IEEE80211_KEYIX_NONE) { - ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ik.ik_macaddr); if (ni == NULL) - return EINVAL; /* XXX */ + return ENOENT; 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); + wk = &vap->iv_nw_keys[kid]; + IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_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) + if (wk->wk_keyix == vap->iv_def_txkey) ik.ik_flags |= IEEE80211_KEY_DEFAULT; if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { /* NB: only root can read key data */ - ik.ik_keyrsc = wk->wk_keyrsc; + ik.ik_keyrsc = wk->wk_keyrsc[IEEE80211_NONQOS_TID]; ik.ik_keytsc = wk->wk_keytsc; memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { @@ -140,18 +127,20 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) return copyout(&ik, ireq->i_data, sizeof(ik)); } -static int -ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; 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) +static __noinline int +ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; int space; space = __offsetof(struct ieee80211req_chaninfo, @@ -162,8 +151,9 @@ ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) return copyout(&ic->ic_nchans, ireq->i_data, space); } -static int -ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req) +static __noinline int +ieee80211_ioctl_getwpaie(struct ieee80211vap *vap, + struct ieee80211req *ireq, int req) { struct ieee80211_node *ni; struct ieee80211req_wpaie2 wpaie; @@ -174,34 +164,34 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int 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); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie.wpa_macaddr); if (ni == NULL) - return ENOENT; /* XXX */ + return ENOENT; memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); - if (ni->ni_wpa_ie != NULL) { - int ielen = ni->ni_wpa_ie[1] + 2; + if (ni->ni_ies.wpa_ie != NULL) { + int ielen = ni->ni_ies.wpa_ie[1] + 2; if (ielen > sizeof(wpaie.wpa_ie)) ielen = sizeof(wpaie.wpa_ie); - memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); + memcpy(wpaie.wpa_ie, ni->ni_ies.wpa_ie, ielen); } if (req == IEEE80211_IOC_WPAIE2) { memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie)); - if (ni->ni_rsn_ie != NULL) { - int ielen = ni->ni_rsn_ie[1] + 2; + if (ni->ni_ies.rsn_ie != NULL) { + int ielen = ni->ni_ies.rsn_ie[1] + 2; if (ielen > sizeof(wpaie.rsn_ie)) ielen = sizeof(wpaie.rsn_ie); - memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen); + memcpy(wpaie.rsn_ie, ni->ni_ies.rsn_ie, ielen); } if (ireq->i_len > sizeof(struct ieee80211req_wpaie2)) ireq->i_len = sizeof(struct ieee80211req_wpaie2); } else { /* compatibility op, may overwrite wpa ie */ /* XXX check ic_flags? */ - if (ni->ni_rsn_ie != NULL) { - int ielen = ni->ni_rsn_ie[1] + 2; + if (ni->ni_ies.rsn_ie != NULL) { + int ielen = ni->ni_ies.rsn_ie[1] + 2; if (ielen > sizeof(wpaie.wpa_ie)) ielen = sizeof(wpaie.wpa_ie); - memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen); + memcpy(wpaie.wpa_ie, ni->ni_ies.rsn_ie, ielen); } if (ireq->i_len > sizeof(struct ieee80211req_wpaie)) ireq->i_len = sizeof(struct ieee80211req_wpaie); @@ -210,8 +200,8 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int return copyout(&wpaie, ireq->i_data, ireq->i_len); } -static int -ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211_node *ni; uint8_t macaddr[IEEE80211_ADDR_LEN]; @@ -223,9 +213,9 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); if (error != 0) return error; - ni = ieee80211_find_node(&ic->ic_sta, macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr); if (ni == NULL) - return EINVAL; + return ENOENT; if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) ireq->i_len = sizeof(struct ieee80211req_sta_stats); /* NB: copy out only the statistics */ @@ -235,149 +225,6 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) return error; } -static __inline uint8_t * -copyie(uint8_t *cp, const uint8_t *ie) -{ - if (ie != NULL) { - memcpy(cp, ie, 2+ie[1]); - cp += 2+ie[1]; - } - return cp; -} - -#ifdef COMPAT_FREEBSD6 -#define IEEE80211_IOC_SCAN_RESULTS_OLD 24 - -struct scan_result_old { - 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 */ -}; - -struct oscanreq { - struct scan_result_old *sr; - size_t space; -}; - -static size_t -old_scan_space(const struct ieee80211_scan_entry *se, int *ielen) -{ - size_t len; - - *ielen = 0; - if (se->se_wpa_ie != NULL) - *ielen += 2+se->se_wpa_ie[1]; - if (se->se_wme_ie != NULL) - *ielen += 2+se->se_wme_ie[1]; - /* - * NB: ie's can be no more than 255 bytes and the max 802.11 - * packet is <3Kbytes so we are sure this doesn't overflow - * 16-bits; if this is a concern we can drop the ie's. - */ - len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen; - return roundup(len, sizeof(uint32_t)); -} - -static void -old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se) -{ - struct oscanreq *req = arg; - int ielen; - - req->space += old_scan_space(se, &ielen); -} - -static void -old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se) -{ - struct oscanreq *req = arg; - struct scan_result_old *sr; - int ielen, len, nr, nxr; - uint8_t *cp; - - len = old_scan_space(se, &ielen); - if (len > req->space) - return; - - sr = req->sr; - memset(sr, 0, sizeof(*sr)); - sr->isr_ssid_len = se->se_ssid[1]; - /* NB: beware of overflow, isr_ie_len is 8 bits */ - sr->isr_ie_len = (ielen > 255 ? 0 : ielen); - sr->isr_len = len; - sr->isr_freq = se->se_chan->ic_freq; - sr->isr_flags = se->se_chan->ic_flags; - sr->isr_rssi = se->se_rssi; - sr->isr_noise = se->se_noise; - sr->isr_intval = se->se_intval; - sr->isr_capinfo = se->se_capinfo; - sr->isr_erp = se->se_erp; - IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); - nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); - memcpy(sr->isr_rates, se->se_rates+2, nr); - nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); - memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); - sr->isr_nrates = nr + nxr; - - cp = (uint8_t *)(sr+1); - memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); - cp += sr->isr_ssid_len; - if (sr->isr_ie_len) { - cp = copyie(cp, se->se_wpa_ie); - cp = copyie(cp, se->se_wme_ie); - } - - req->space -= len; - req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len); -} - -static int -old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) -{ - struct oscanreq req; - int error; - - if (ireq->i_len < sizeof(struct scan_result_old)) - return EFAULT; - - error = 0; - req.space = 0; - ieee80211_scan_iterate(ic, old_get_scan_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 */ - MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); - if (p == NULL) - return ENOMEM; - req.sr = p; - ieee80211_scan_iterate(ic, old_get_scan_result, &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; -} -#endif /* COMPAT_FREEBSD6 */ - struct scanreq { struct ieee80211req_scan_result *sr; size_t space; @@ -388,15 +235,7 @@ scan_space(const struct ieee80211_scan_entry *se, int *ielen) { size_t len; - *ielen = 0; - if (se->se_wpa_ie != NULL) - *ielen += 2+se->se_wpa_ie[1]; - if (se->se_rsn_ie != NULL) - *ielen += 2+se->se_rsn_ie[1]; - if (se->se_wme_ie != NULL) - *ielen += 2+se->se_wme_ie[1]; - if (se->se_ath_ie != NULL) - *ielen += 2+se->se_ath_ie[1]; + *ielen = se->se_ies.len; /* * NB: ie's can be no more than 255 bytes and the max 802.11 * packet is <3Kbytes so we are sure this doesn't overflow @@ -415,7 +254,7 @@ get_scan_space(void *arg, const struct ieee80211_scan_entry *se) req->space += scan_space(se, &ielen); } -static void +static __noinline void get_scan_result(void *arg, const struct ieee80211_scan_entry *se) { struct scanreq *req = arg; @@ -430,9 +269,9 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se) sr = req->sr; KASSERT(len <= 65535 && ielen <= 65535, ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen)); + sr->isr_len = len; sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); sr->isr_ie_len = ielen; - sr->isr_len = len; sr->isr_freq = se->se_chan->ic_freq; sr->isr_flags = se->se_chan->ic_flags; sr->isr_rssi = se->se_rssi; @@ -453,29 +292,26 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se) if (ielen) { cp += sr->isr_ssid_len; - cp = copyie(cp, se->se_wpa_ie); - cp = copyie(cp, se->se_rsn_ie); - cp = copyie(cp, se->se_wme_ie); - cp = copyie(cp, se->se_ath_ie); - cp = copyie(cp, se->se_htcap_ie); + memcpy(cp, se->se_ies.data, ielen); } req->space -= len; req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len); } -static int -ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getscanresults(struct ieee80211vap *vap, + struct ieee80211req *ireq) { struct scanreq req; int error; - if (ireq->i_len < sizeof(struct ieee80211req_scan_result)) + if (ireq->i_len < sizeof(struct scanreq)) return EFAULT; error = 0; req.space = 0; - ieee80211_scan_iterate(ic, get_scan_space, &req); + ieee80211_scan_iterate(vap, get_scan_space, &req); if (req.space > ireq->i_len) req.space = ireq->i_len; if (req.space > 0) { @@ -488,7 +324,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire if (p == NULL) return ENOMEM; req.sr = p; - ieee80211_scan_iterate(ic, get_scan_result, &req); + ieee80211_scan_iterate(vap, get_scan_result, &req); ireq->i_len = space - req.space; error = copyout(p, ireq->i_data, ireq->i_len); FREE(p, M_TEMP); @@ -499,7 +335,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire } struct stainforeq { - struct ieee80211com *ic; + struct ieee80211vap *vap; struct ieee80211req_sta_info *si; size_t space; }; @@ -507,15 +343,7 @@ struct stainforeq { 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_rsn_ie != NULL) - *ielen += 2+ni->ni_rsn_ie[1]; - if (ni->ni_wme_ie != NULL) - *ielen += 2+ni->ni_wme_ie[1]; - if (ni->ni_ath_ie != NULL) - *ielen += 2+ni->ni_ath_ie[1]; + *ielen = ni->ni_ies.len; return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, sizeof(uint32_t)); } @@ -524,25 +352,28 @@ 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 && + if (req->vap != ni->ni_vap) + return; + if (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP && ni->ni_associd == 0) /* only associated stations */ return; req->space += sta_space(ni, &ielen); } -static void +static __noinline void get_sta_info(void *arg, struct ieee80211_node *ni) { struct stainforeq *req = arg; - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211req_sta_info *si; size_t ielen, len; uint8_t *cp; - if (ic->ic_opmode == IEEE80211_M_HOSTAP && + if (req->vap != ni->ni_vap) + return; + if (vap->iv_opmode == IEEE80211_M_HOSTAP && ni->ni_associd == 0) /* only associated stations */ return; if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ @@ -558,8 +389,8 @@ get_sta_info(void *arg, struct ieee80211_node *ni) si->isi_flags = ni->ni_chan->ic_flags; si->isi_state = ni->ni_flags; si->isi_authmode = ni->ni_authmode; - ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); - si->isi_noise = 0; /* XXX */ + vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); + vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo); si->isi_capinfo = ni->ni_capinfo; si->isi_erp = ni->ni_erp; IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); @@ -568,7 +399,22 @@ get_sta_info(void *arg, struct ieee80211_node *ni) si->isi_nrates = 15; memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); si->isi_txrate = ni->ni_txrate; - si->isi_ie_len = ielen; + if (si->isi_txrate & IEEE80211_RATE_MCS) { + const struct ieee80211_mcs_rates *mcs = + &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS]; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { + if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) + si->isi_txmbps = mcs->ht40_rate_800ns; + else + si->isi_txmbps = mcs->ht40_rate_400ns; + } else { + if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) + si->isi_txmbps = mcs->ht20_rate_800ns; + else + si->isi_txmbps = mcs->ht20_rate_400ns; + } + } else + si->isi_txmbps = si->isi_txrate; si->isi_associd = ni->ni_associd; si->isi_txpower = ni->ni_txpower; si->isi_vlan = ni->ni_vlan; @@ -581,29 +427,29 @@ get_sta_info(void *arg, struct ieee80211_node *ni) } /* 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; + si->isi_inact = vap->iv_inact_run; + else if (ni->ni_associd != 0 || + (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) + si->isi_inact = vap->iv_inact_auth; else - si->isi_inact = ic->ic_inact_init; + si->isi_inact = vap->iv_inact_init; si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; if (ielen) { cp = ((uint8_t *)si) + si->isi_ie_off; - cp = copyie(cp, ni->ni_wpa_ie); - cp = copyie(cp, ni->ni_rsn_ie); - cp = copyie(cp, ni->ni_wme_ie); - cp = copyie(cp, ni->ni_ath_ie); + memcpy(cp, ni->ni_ies.data, ielen); } req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len); req->space -= len; } -static int -getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, +static __noinline int +getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq, struct ieee80211_node *ni, int off) { + struct ieee80211com *ic = vap->iv_ic; struct stainforeq req; size_t space; void *p; @@ -611,6 +457,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, error = 0; req.space = 0; + req.vap = vap; if (ni == NULL) ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req); else @@ -620,7 +467,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq, if (req.space > 0) { space = req.space; /* XXX M_WAITOK after driver lock released */ - MALLOC(p, void *, space, M_TEMP, M_NOWAIT); + MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO); if (p == NULL) { error = ENOMEM; goto bad; @@ -641,8 +488,8 @@ bad: return error; } -static int -ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq) { uint8_t macaddr[IEEE80211_ADDR_LEN]; const int off = __offsetof(struct ieee80211req_sta_req, info); @@ -654,30 +501,18 @@ ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); if (error != 0) return error; - if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) { + if (IEEE80211_ADDR_EQ(macaddr, vap->iv_ifp->if_broadcastaddr)) { ni = NULL; } else { - ni = ieee80211_find_node(&ic->ic_sta, macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr); if (ni == NULL) - return EINVAL; + return ENOENT; } - return getstainfo_common(ic, ireq, ni, off); + return getstainfo_common(vap, ireq, ni, off); } -#ifdef COMPAT_FREEBSD6 -#define IEEE80211_IOC_STA_INFO_OLD 45 - -static int -old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) -{ - if (ireq->i_len < sizeof(struct ieee80211req_sta_info)) - return EFAULT; - return getstainfo_common(ic, ireq, NULL, 0); -} -#endif /* COMPAT_FREEBSD6 */ - -static int -ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211_node *ni; struct ieee80211req_sta_txpow txpow; @@ -688,18 +523,19 @@ ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) error = copyin(ireq->i_data, &txpow, sizeof(txpow)); if (error != 0) return error; - ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr); if (ni == NULL) - return EINVAL; /* XXX */ + return ENOENT; 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) +static __noinline int +ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_wme_state *wme = &ic->ic_wme; struct wmeParams *wmep; int ac; @@ -739,12 +575,12 @@ ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) return 0; } -static int -ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq) { - const struct ieee80211_aclator *acl = ic->ic_acl; + const struct ieee80211_aclator *acl = vap->iv_acl; - return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq)); + return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq)); } /* @@ -753,20 +589,151 @@ ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) * setting. Otherwise report the current setting. */ static int -getathcap(struct ieee80211com *ic, int cap) +getathcap(struct ieee80211vap *vap, int cap) { - if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN) - return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0; + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) + return IEEE80211_ATH_CAP(vap, vap->iv_bss, cap) != 0; else - return (ic->ic_flags & cap) != 0; + return (vap->iv_flags & cap) != 0; } -static int -ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; + if (ireq->i_len != sizeof(struct ieee80211_channel)) return EINVAL; - return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan)); + /* + * vap's may have different operating channels when HT is + * in use. When in RUN state report the vap-specific channel. + * Otherwise return curchan. + */ + if (vap->iv_state == IEEE80211_S_RUN) + c = vap->iv_bss->ni_chan; + else + c = ic->ic_curchan; + return copyout(c, ireq->i_data, sizeof(*c)); +} + +static int +getappie(const struct ieee80211_appie *aie, struct ieee80211req *ireq) +{ + if (aie == NULL) + return EINVAL; + /* NB: truncate, caller can check length */ + if (ireq->i_len > aie->ie_len) + ireq->i_len = aie->ie_len; + return copyout(aie->ie_data, ireq->i_data, ireq->i_len); +} + +static int +ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + uint8_t fc0; + + fc0 = ireq->i_val & 0xff; + if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return EINVAL; + /* NB: could check iv_opmode and reject but hardly worth the effort */ + switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return getappie(vap->iv_appie_beacon, ireq); + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return getappie(vap->iv_appie_proberesp, ireq); + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + return getappie(vap->iv_appie_assocresp, ireq); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return getappie(vap->iv_appie_probereq, ireq); + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + return getappie(vap->iv_appie_assocreq, ireq); + case IEEE80211_FC0_SUBTYPE_BEACON|IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return getappie(vap->iv_appie_wpa, ireq); + } + return EINVAL; +} + +static __noinline int +ieee80211_ioctl_getregdomain(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + + if (ireq->i_len != sizeof(ic->ic_regdomain)) + return EINVAL; + return copyout(&ic->ic_regdomain, ireq->i_data, + sizeof(ic->ic_regdomain)); +} + +static __noinline int +ieee80211_ioctl_getroam(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + if (ireq->i_len != sizeof(vap->iv_roamparms)) + return EINVAL; + return copyout(vap->iv_roamparms, ireq->i_data, + sizeof(vap->iv_roamparms)); +} + +static __noinline int +ieee80211_ioctl_gettxparams(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + if (ireq->i_len != sizeof(vap->iv_txparms)) + return EINVAL; + return copyout(vap->iv_txparms, ireq->i_data, sizeof(vap->iv_txparms)); +} + +static __noinline int +ieee80211_ioctl_getdevcaps(struct ieee80211com *ic, + const struct ieee80211req *ireq) +{ + struct ieee80211_devcaps_req *dc; + struct ieee80211req_chaninfo *ci; + int error; + + if (ireq->i_len != sizeof(struct ieee80211_devcaps_req)) + return EINVAL; + MALLOC(dc, struct ieee80211_devcaps_req *, + sizeof(struct ieee80211_devcaps_req), M_TEMP, M_NOWAIT | M_ZERO); + if (dc == NULL) + return ENOMEM; + dc->dc_drivercaps = ic->ic_caps; + dc->dc_cryptocaps = ic->ic_cryptocaps; + dc->dc_htcaps = ic->ic_htcaps; + ci = &dc->dc_chaninfo; + ic->ic_getradiocaps(ic, &ci->ic_nchans, ci->ic_chans); + ieee80211_sort_channels(ci->ic_chans, ci->ic_nchans); + error = copyout(dc, ireq->i_data, sizeof(*dc)); + FREE(dc, M_TEMP); + return error; +} + +static __noinline int +ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_vlan vlan; + int error; + + if (ireq->i_len != sizeof(vlan)) + return EINVAL; + error = copyin(ireq->i_data, &vlan, sizeof(vlan)); + if (error != 0) + return error; + if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + vlan.sv_macaddr); + if (ni == NULL) + return ENOENT; + } else + ni = ieee80211_ref_node(vap->iv_bss); + vlan.sv_vlan = ni->ni_vlan; + error = copyout(&vlan, ireq->i_data, sizeof(vlan)); + ieee80211_free_node(ni); + return error; } /* @@ -785,30 +752,28 @@ ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq) * 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) +static __noinline int +ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, + struct ieee80211req *ireq) { - const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; - int error = 0; - u_int kid, len, m; +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct ieee80211com *ic = vap->iv_ic; + u_int kid, len; uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; char tmpssid[IEEE80211_NWID_LEN]; + int error = 0; switch (ireq->i_type) { case IEEE80211_IOC_SSID: - switch (ic->ic_state) { + switch (vap->iv_state) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: - ireq->i_len = ic->ic_des_ssid[0].len; - memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len); + ireq->i_len = vap->iv_des_ssid[0].len; + memcpy(tmpssid, vap->iv_des_ssid[0].ssid, ireq->i_len); break; default: - ireq->i_len = ic->ic_bss->ni_esslen; - memcpy(tmpssid, ic->ic_bss->ni_essid, - ireq->i_len); + ireq->i_len = vap->iv_bss->ni_esslen; + memcpy(tmpssid, vap->iv_bss->ni_essid, ireq->i_len); break; } error = copyout(tmpssid, ireq->i_data, ireq->i_len); @@ -817,9 +782,9 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ireq->i_val = 1; break; case IEEE80211_IOC_WEP: - if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) ireq->i_val = IEEE80211_WEP_OFF; - else if (ic->ic_flags & IEEE80211_F_DROPUNENC) + else if (vap->iv_flags & IEEE80211_F_DROPUNENC) ireq->i_val = IEEE80211_WEP_ON; else ireq->i_val = IEEE80211_WEP_MIXED; @@ -828,10 +793,10 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) return EINVAL; - len = (u_int) ic->ic_nw_keys[kid].wk_keylen; + len = (u_int) vap->iv_nw_keys[kid].wk_keylen; /* NB: only root can read WEP keys */ if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { - bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); + bcopy(vap->iv_nw_keys[kid].wk_key, tmpkey, len); } else { bzero(tmpkey, len); } @@ -842,19 +807,19 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ireq->i_val = IEEE80211_WEP_NKID; break; case IEEE80211_IOC_WEPTXKEY: - ireq->i_val = ic->ic_def_txkey; + ireq->i_val = vap->iv_def_txkey; break; case IEEE80211_IOC_AUTHMODE: - if (ic->ic_flags & IEEE80211_F_WPA) + if (vap->iv_flags & IEEE80211_F_WPA) ireq->i_val = IEEE80211_AUTH_WPA; else - ireq->i_val = ic->ic_bss->ni_authmode; + ireq->i_val = vap->iv_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) + if (vap->iv_flags & IEEE80211_F_PMGTON) ireq->i_val = IEEE80211_POWERSAVE_ON; else ireq->i_val = IEEE80211_POWERSAVE_OFF; @@ -863,42 +828,25 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ireq->i_val = ic->ic_lintval; break; case IEEE80211_IOC_RTSTHRESHOLD: - ireq->i_val = ic->ic_rtsthreshold; + ireq->i_val = vap->iv_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<<cap2cipher(m); - break; - case IEEE80211_IOC_UCASTCIPHER: - ireq->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; + /* + * Tx power limit is the min of max regulatory + * power, any user-set limit, and the max the + * radio can do. + */ + ireq->i_val = 2*ic->ic_curchan->ic_maxregpower; + if (ireq->i_val > ic->ic_txpowlimit) + ireq->i_val = ic->ic_txpowlimit; + if (ireq->i_val > ic->ic_curchan->ic_maxpower) + ireq->i_val = ic->ic_curchan->ic_maxpower; break; case IEEE80211_IOC_WPA: - switch (ic->ic_flags & IEEE80211_F_WPA) { + switch (vap->iv_flags & IEEE80211_F_WPA) { case IEEE80211_F_WPA1: ireq->i_val = 1; break; @@ -914,85 +862,63 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re } break; case IEEE80211_IOC_CHANLIST: - error = ieee80211_ioctl_getchanlist(ic, ireq); + error = ieee80211_ioctl_getchanlist(vap, ireq); break; case IEEE80211_IOC_ROAMING: - ireq->i_val = ic->ic_roaming; + ireq->i_val = vap->iv_roaming; break; case IEEE80211_IOC_PRIVACY: - ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; + ireq->i_val = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0; break; case IEEE80211_IOC_DROPUNENCRYPTED: - ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; + ireq->i_val = (vap->iv_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; + ireq->i_val = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0; break; case IEEE80211_IOC_WME: - ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; + ireq->i_val = (vap->iv_flags & IEEE80211_F_WME) != 0; break; case IEEE80211_IOC_HIDESSID: - ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; + ireq->i_val = (vap->iv_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); + ireq->i_val = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0; break; case IEEE80211_IOC_WPAKEY: - error = ieee80211_ioctl_getkey(ic, ireq); + error = ieee80211_ioctl_getkey(vap, ireq); break; case IEEE80211_IOC_CHANINFO: - error = ieee80211_ioctl_getchaninfo(ic, ireq); + error = ieee80211_ioctl_getchaninfo(vap, 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, + error = copyout(vap->iv_state == IEEE80211_S_RUN ? + vap->iv_bss->ni_bssid : + vap->iv_des_bssid, ireq->i_data, ireq->i_len); break; case IEEE80211_IOC_WPAIE: - error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); + error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type); break; case IEEE80211_IOC_WPAIE2: - error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type); - break; -#ifdef COMPAT_FREEBSD6 - case IEEE80211_IOC_SCAN_RESULTS_OLD: - error = old_getscanresults(ic, ireq); + error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type); break; -#endif case IEEE80211_IOC_SCAN_RESULTS: - error = ieee80211_ioctl_getscanresults(ic, ireq); + error = ieee80211_ioctl_getscanresults(vap, ireq); break; case IEEE80211_IOC_STA_STATS: - error = ieee80211_ioctl_getstastats(ic, ireq); + error = ieee80211_ioctl_getstastats(vap, ireq); break; case IEEE80211_IOC_TXPOWMAX: - ireq->i_val = ic->ic_bss->ni_txpower; + ireq->i_val = vap->iv_bss->ni_txpower; break; case IEEE80211_IOC_STA_TXPOW: - error = ieee80211_ioctl_getstatxpow(ic, ireq); - break; -#ifdef COMPAT_FREEBSD6 - case IEEE80211_IOC_STA_INFO_OLD: - error = old_getstainfo(ic, ireq); + error = ieee80211_ioctl_getstatxpow(vap, ireq); break; -#endif case IEEE80211_IOC_STA_INFO: - error = ieee80211_ioctl_getstainfo(ic, ireq); + error = ieee80211_ioctl_getstainfo(vap, ireq); break; case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ @@ -1000,180 +926,163 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re 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); + error = ieee80211_ioctl_getwmeparam(vap, ireq); break; case IEEE80211_IOC_DTIM_PERIOD: - ireq->i_val = ic->ic_dtim_period; + ireq->i_val = vap->iv_dtim_period; break; case IEEE80211_IOC_BEACON_INTERVAL: /* NB: get from ic_bss for station mode */ - ireq->i_val = ic->ic_bss->ni_intval; + ireq->i_val = vap->iv_bss->ni_intval; break; case IEEE80211_IOC_PUREG: - ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0; + ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0; break; case IEEE80211_IOC_FF: - ireq->i_val = getathcap(ic, IEEE80211_F_FF); + ireq->i_val = getathcap(vap, IEEE80211_F_FF); break; case IEEE80211_IOC_TURBOP: - ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP); + ireq->i_val = getathcap(vap, IEEE80211_F_TURBOP); break; case IEEE80211_IOC_BGSCAN: - ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0; + ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0; break; case IEEE80211_IOC_BGSCAN_IDLE: - ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */ + ireq->i_val = vap->iv_bgscanidle*hz/1000; /* ms */ break; case IEEE80211_IOC_BGSCAN_INTERVAL: - ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */ + ireq->i_val = vap->iv_bgscanintvl/hz; /* seconds */ break; case IEEE80211_IOC_SCANVALID: - ireq->i_val = ic->ic_scanvalid/hz; /* seconds */ - break; - case IEEE80211_IOC_ROAM_RSSI_11A: - ireq->i_val = ic->ic_roam.rssi11a; - break; - case IEEE80211_IOC_ROAM_RSSI_11B: - ireq->i_val = ic->ic_roam.rssi11bOnly; - break; - case IEEE80211_IOC_ROAM_RSSI_11G: - ireq->i_val = ic->ic_roam.rssi11b; - break; - case IEEE80211_IOC_ROAM_RATE_11A: - ireq->i_val = ic->ic_roam.rate11a; - break; - case IEEE80211_IOC_ROAM_RATE_11B: - ireq->i_val = ic->ic_roam.rate11bOnly; - break; - case IEEE80211_IOC_ROAM_RATE_11G: - ireq->i_val = ic->ic_roam.rate11b; - break; - case IEEE80211_IOC_MCAST_RATE: - ireq->i_val = ic->ic_mcast_rate; + ireq->i_val = vap->iv_scanvalid/hz; /* seconds */ break; case IEEE80211_IOC_FRAGTHRESHOLD: - ireq->i_val = ic->ic_fragthreshold; + ireq->i_val = vap->iv_fragthreshold; break; case IEEE80211_IOC_MACCMD: - error = ieee80211_ioctl_getmaccmd(ic, ireq); + error = ieee80211_ioctl_getmaccmd(vap, ireq); break; case IEEE80211_IOC_BURST: - ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0; + ireq->i_val = (vap->iv_flags & IEEE80211_F_BURST) != 0; break; case IEEE80211_IOC_BMISSTHRESHOLD: - ireq->i_val = ic->ic_bmissthreshold; + ireq->i_val = vap->iv_bmissthreshold; break; case IEEE80211_IOC_CURCHAN: - error = ieee80211_ioctl_getcurchan(ic, ireq); + error = ieee80211_ioctl_getcurchan(vap, ireq); break; case IEEE80211_IOC_SHORTGI: ireq->i_val = 0; - if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) + if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) ireq->i_val |= IEEE80211_HTCAP_SHORTGI20; - if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) + if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) ireq->i_val |= IEEE80211_HTCAP_SHORTGI40; break; case IEEE80211_IOC_AMPDU: ireq->i_val = 0; - if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX) + if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX) ireq->i_val |= 1; - if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX) + if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX) ireq->i_val |= 2; break; case IEEE80211_IOC_AMPDU_LIMIT: - ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ireq->i_val = vap->iv_ampdu_rxmax; + else if (vap->iv_state == IEEE80211_S_RUN) + ireq->i_val = MS(vap->iv_bss->ni_htparam, + IEEE80211_HTCAP_MAXRXAMPDU); + else + ireq->i_val = vap->iv_ampdu_limit; break; case IEEE80211_IOC_AMPDU_DENSITY: - ireq->i_val = ic->ic_ampdu_density; + if (vap->iv_state == IEEE80211_S_RUN) + ireq->i_val = MS(vap->iv_bss->ni_htparam, + IEEE80211_HTCAP_MPDUDENSITY); + else + ireq->i_val = vap->iv_ampdu_density; break; case IEEE80211_IOC_AMSDU: ireq->i_val = 0; - if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX) + if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_TX) ireq->i_val |= 1; - if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX) + if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_RX) ireq->i_val |= 2; break; case IEEE80211_IOC_AMSDU_LIMIT: - ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */ + ireq->i_val = vap->iv_amsdu_limit; /* XXX truncation? */ break; case IEEE80211_IOC_PUREN: - ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0; + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_PUREN) != 0; break; case IEEE80211_IOC_DOTH: - ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0; + ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0; + break; + case IEEE80211_IOC_REGDOMAIN: + error = ieee80211_ioctl_getregdomain(vap, ireq); + break; + case IEEE80211_IOC_ROAM: + error = ieee80211_ioctl_getroam(vap, ireq); + break; + case IEEE80211_IOC_TXPARAMS: + error = ieee80211_ioctl_gettxparams(vap, ireq); break; case IEEE80211_IOC_HTCOMPAT: - ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0; + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0; + break; + case IEEE80211_IOC_DWDS: + ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0; break; case IEEE80211_IOC_INACTIVITY: - ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0; + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0; + break; + case IEEE80211_IOC_APPIE: + error = ieee80211_ioctl_getappie(vap, ireq); + break; + case IEEE80211_IOC_WPS: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_WPS) != 0; + break; + case IEEE80211_IOC_TSN: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_TSN) != 0; + break; + case IEEE80211_IOC_DFS: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DFS) != 0; + break; + case IEEE80211_IOC_DOTD: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DOTD) != 0; + break; + case IEEE80211_IOC_DEVCAPS: + error = ieee80211_ioctl_getdevcaps(ic, ireq); break; case IEEE80211_IOC_HTPROTMODE: ireq->i_val = ic->ic_htprotmode; break; case IEEE80211_IOC_HTCONF: - if (ic->ic_flags_ext & IEEE80211_FEXT_HT) { + if (vap->iv_flags_ext & IEEE80211_FEXT_HT) { ireq->i_val = 1; - if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) + if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) ireq->i_val |= 2; } else ireq->i_val = 0; break; + case IEEE80211_IOC_STA_VLAN: + error = ieee80211_ioctl_getstavlan(vap, ireq); + break; default: error = EINVAL; break; } return error; +#undef MS } -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; - /* NB: data.length is validated by the wireless extensions code */ - /* XXX M_WAITOK after driver lock released */ - if (ireq->i_len > 0) { - MALLOC(ie, void *, 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) +static __noinline int +ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211req_key ik; struct ieee80211_node *ni; struct ieee80211_key *wk; uint16_t kid; - int error; + int error, i; if (ireq->i_len != sizeof(ik)) return EINVAL; @@ -1189,14 +1098,15 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) /* 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 (vap->iv_opmode == IEEE80211_M_STA) { + ni = ieee80211_ref_node(vap->iv_bss); if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) { ieee80211_free_node(ni); return EADDRNOTAVAIL; } } else { - ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + ik.ik_macaddr); if (ni == NULL) return ENOENT; } @@ -1204,7 +1114,7 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) } else { if (kid >= IEEE80211_WEP_NKID) return EINVAL; - wk = &ic->ic_nw_keys[kid]; + wk = &vap->iv_nw_keys[kid]; /* * Global slots start off w/o any assigned key index. * Force one here for consistency with IEEE80211_IOC_WEPKEY. @@ -1214,31 +1124,32 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) ni = NULL; } error = 0; - ieee80211_key_update_begin(ic); - if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) { + ieee80211_key_update_begin(vap); + if (ieee80211_crypto_newkey(vap, 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; + for (i = 0; i < IEEE80211_TID_SIZE; i++) + wk->wk_keyrsc[i] = 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, + if (!ieee80211_crypto_setkey(vap, wk, ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) error = EIO; else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) - ic->ic_def_txkey = kid; + vap->iv_def_txkey = kid; } else error = ENXIO; - ieee80211_key_update_end(ic); + ieee80211_key_update_end(vap); if (ni != NULL) ieee80211_free_node(ni); return error; } -static int -ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211req_del_key dk; int kid, error; @@ -1253,14 +1164,15 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) 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 (vap->iv_opmode == IEEE80211_M_STA) { + ni = ieee80211_ref_node(vap->iv_bss); if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) { ieee80211_free_node(ni); return EADDRNOTAVAIL; } } else { - ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + dk.idk_macaddr); if (ni == NULL) return ENOENT; } @@ -1271,25 +1183,216 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) if (kid >= IEEE80211_WEP_NKID) return EINVAL; /* XXX error return */ - ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); + ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]); } return 0; } +struct mlmeop { + struct ieee80211vap *vap; + int op; + int reason; +}; + +static void +mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], + int op, int reason) +{ +#ifdef IEEE80211_DEBUG + static const struct { + int mask; + const char *opstr; + } ops[] = { + { 0, "op#0" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_ASSOC, "assoc" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_ASSOC, "disassoc" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_AUTH, "deauth" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_AUTH, "authorize" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_AUTH, "unauthorize" }, + }; + + if (op == IEEE80211_MLME_AUTH) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_IOCTL | + IEEE80211_MSG_STATE | IEEE80211_MSG_AUTH, mac, + "station authenticate %s via MLME (reason %d)", + reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT", + reason); + } else if (!(IEEE80211_MLME_ASSOC <= op && op <= IEEE80211_MLME_AUTH)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, mac, + "unknown MLME request %d (reason %d)", op, reason); + } else if (reason == IEEE80211_STATUS_SUCCESS) { + IEEE80211_NOTE_MAC(vap, ops[op].mask, mac, + "station %s via MLME", ops[op].opstr); + } else { + IEEE80211_NOTE_MAC(vap, ops[op].mask, mac, + "station %s via MLME (reason %d)", ops[op].opstr, reason); + } +#endif /* IEEE80211_DEBUG */ +} + static void domlme(void *arg, struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; - struct ieee80211req_mlme *mlme = arg; + struct mlmeop *mop = arg; + struct ieee80211vap *vap = ni->ni_vap; + + if (vap != mop->vap) + return; + /* + * NB: if ni_associd is zero then the node is already cleaned + * up and we don't need to do this (we're safely holding a + * reference but should otherwise not modify it's state). + */ + if (ni->ni_associd == 0) + return; + mlmedebug(vap, ni->ni_macaddr, mop->op, mop->reason); + if (mop->op == IEEE80211_MLME_DEAUTH) { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + mop->reason); + } else { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, + mop->reason); + } + ieee80211_node_leave(ni); +} + +static int +setmlme_dropsta(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN], struct mlmeop *mlmeop) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *ni; + int error = 0; + + /* NB: the broadcast address means do 'em all */ + if (!IEEE80211_ADDR_EQ(mac, ic->ic_ifp->if_broadcastaddr)) { + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_node_locked(nt, mac); + if (ni != NULL) { + domlme(mlmeop, ni); + ieee80211_free_node(ni); + } else + error = ENOENT; + IEEE80211_NODE_UNLOCK(nt); + } else { + ieee80211_iterate_nodes(nt, domlme, mlmeop); + } + return error; +} + +static __noinline int +setmlme_common(struct ieee80211vap *vap, int op, + const uint8_t mac[IEEE80211_ADDR_LEN], int reason) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *ni; + struct mlmeop mlmeop; + int error; - 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); + error = 0; + switch (op) { + case IEEE80211_MLME_DISASSOC: + case IEEE80211_MLME_DEAUTH: + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason); + /* XXX not quite right */ + ieee80211_new_state(vap, IEEE80211_S_INIT, reason); + break; + case IEEE80211_M_HOSTAP: + mlmeop.vap = vap; + mlmeop.op = op; + mlmeop.reason = reason; + error = setmlme_dropsta(vap, mac, &mlmeop); + break; + case IEEE80211_M_WDS: + /* XXX user app should send raw frame? */ + if (op != IEEE80211_MLME_DEAUTH) { + error = EINVAL; + break; + } +#if 0 + /* XXX accept any address, simplifies user code */ + if (!IEEE80211_ADDR_EQ(mac, vap->iv_bss->ni_macaddr)) { + error = EINVAL; + break; + } +#endif + mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason); + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, reason); + ieee80211_free_node(ni); + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_MLME_AUTHORIZE: + case IEEE80211_MLME_UNAUTHORIZE: + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_WDS) { + error = EINVAL; + break; + } + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_vap_node_locked(nt, vap, mac); + if (ni != NULL) { + mlmedebug(vap, mac, op, reason); + if (op == IEEE80211_MLME_AUTHORIZE) + ieee80211_node_authorize(ni); + else + ieee80211_node_unauthorize(ni); + ieee80211_free_node(ni); + } else + error = ENOENT; + IEEE80211_NODE_UNLOCK(nt); + break; + case IEEE80211_MLME_AUTH: + if (vap->iv_opmode != IEEE80211_M_HOSTAP) { + error = EINVAL; + break; + } + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_vap_node_locked(nt, vap, mac); + if (ni != NULL) { + mlmedebug(vap, mac, op, reason); + if (reason == IEEE80211_STATUS_SUCCESS) { + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + /* + * For shared key auth, just continue the + * exchange. Otherwise 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 && + ni->ni_challenge == NULL) + ieee80211_node_authorize(ni); + } else { + vap->iv_stats.is_rx_acl++; + ieee80211_send_error(ni, ni->ni_macaddr, + IEEE80211_FC0_SUBTYPE_AUTH, 2|(reason<<16)); + ieee80211_node_leave(ni); + } + ieee80211_free_node(ni); + } else + error = ENOENT; + IEEE80211_NODE_UNLOCK(nt); + break; + default: + error = EINVAL; + break; } - ieee80211_node_leave(ic, ni); + return error; } struct scanlookup { @@ -1318,11 +1421,34 @@ mlmelookup(void *arg, const struct ieee80211_scan_entry *se) look->se = se; } -static int -ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +setmlme_assoc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], + int ssid_len, const uint8_t ssid[IEEE80211_NWID_LEN]) +{ + struct scanlookup lookup; + + /* XXX ibss/ahdemo */ + if (vap->iv_opmode != IEEE80211_M_STA) + return EINVAL; + + /* NB: this is racey if roaming is !manual */ + lookup.se = NULL; + lookup.mac = mac; + lookup.esslen = ssid_len; + lookup.essid = ssid; + ieee80211_scan_iterate(vap, mlmelookup, &lookup); + if (lookup.se == NULL) + return ENOENT; + mlmedebug(vap, mac, IEEE80211_MLME_ASSOC, 0); + if (!ieee80211_sta_join(vap, lookup.se)) + return EIO; /* XXX unique but could be better */ + return 0; +} + +static __noinline int +ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211req_mlme mlme; - struct ieee80211_node *ni; int error; if (ireq->i_len != sizeof(mlme)) @@ -1330,72 +1456,19 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) error = copyin(ireq->i_data, &mlme, sizeof(mlme)); if (error) return error; - switch (mlme.im_op) { - case IEEE80211_MLME_ASSOC: - /* XXX ibss/ahdemo */ - if (ic->ic_opmode == IEEE80211_M_STA) { - struct scanlookup lookup; - - lookup.se = NULL; - lookup.mac = mlme.im_macaddr; - /* XXX use revised api w/ explicit ssid */ - lookup.esslen = ic->ic_des_ssid[0].len; - lookup.essid = ic->ic_des_ssid[0].ssid; - ieee80211_scan_iterate(ic, mlmelookup, &lookup); - if (lookup.se != NULL && - ieee80211_sta_join(ic, lookup.se)) - return 0; - } - return EINVAL; - 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 { - ieee80211_iterate_nodes(&ic->ic_sta, - domlme, &mlme); - } - 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; + if (mlme.im_op == IEEE80211_MLME_ASSOC) + return setmlme_assoc(vap, mlme.im_macaddr, + vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid); + else + return setmlme_common(vap, mlme.im_op, + mlme.im_macaddr, mlme.im_reason); } -static int -ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq) { uint8_t mac[IEEE80211_ADDR_LEN]; - const struct ieee80211_aclator *acl = ic->ic_acl; + const struct ieee80211_aclator *acl = vap->iv_acl; int error; if (ireq->i_len != sizeof(mac)) @@ -1405,57 +1478,59 @@ ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) return error; if (acl == NULL) { acl = ieee80211_aclator_get("mac"); - if (acl == NULL || !acl->iac_attach(ic)) + if (acl == NULL || !acl->iac_attach(vap)) return EINVAL; - ic->ic_acl = acl; + vap->iv_acl = acl; } if (ireq->i_type == IEEE80211_IOC_ADDMAC) - acl->iac_add(ic, mac); + acl->iac_add(vap, mac); else - acl->iac_remove(ic, mac); + acl->iac_remove(vap, mac); return 0; } -static int -ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq) { - const struct ieee80211_aclator *acl = ic->ic_acl; + const struct ieee80211_aclator *acl = vap->iv_acl; switch (ireq->i_val) { case IEEE80211_MACCMD_POLICY_OPEN: case IEEE80211_MACCMD_POLICY_ALLOW: case IEEE80211_MACCMD_POLICY_DENY: + case IEEE80211_MACCMD_POLICY_RADIUS: if (acl == NULL) { acl = ieee80211_aclator_get("mac"); - if (acl == NULL || !acl->iac_attach(ic)) + if (acl == NULL || !acl->iac_attach(vap)) return EINVAL; - ic->ic_acl = acl; + vap->iv_acl = acl; } - acl->iac_setpolicy(ic, ireq->i_val); + acl->iac_setpolicy(vap, ireq->i_val); break; case IEEE80211_MACCMD_FLUSH: if (acl != NULL) - acl->iac_flush(ic); + acl->iac_flush(vap); /* NB: silently ignore when not in use */ break; case IEEE80211_MACCMD_DETACH: if (acl != NULL) { - ic->ic_acl = NULL; - acl->iac_detach(ic); + vap->iv_acl = NULL; + acl->iac_detach(vap); } break; default: if (acl == NULL) return EINVAL; else - return acl->iac_setioctl(ic, ireq); + return acl->iac_setioctl(vap, ireq); } return 0; } -static int -ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211req_chanlist list; u_char chanlist[IEEE80211_CHAN_BYTES]; int i, j, nchan, error; @@ -1491,11 +1566,12 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) isclr(chanlist, ic->ic_bsschan->ic_ieee)) ic->ic_bsschan = IEEE80211_CHAN_ANYC; memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); - return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0; + ieee80211_scan_flush(vap); + return ENETRESET; } -static int -ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211_node *ni; uint8_t macaddr[IEEE80211_ADDR_LEN]; @@ -1511,16 +1587,17 @@ ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq) error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); if (error != 0) return error; - ni = ieee80211_find_node(&ic->ic_sta, macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr); if (ni == NULL) - return EINVAL; /* XXX */ + return ENOENT; + /* XXX require ni_vap == vap? */ memset(&ni->ni_stats, 0, sizeof(ni->ni_stats)); ieee80211_free_node(ni); return 0; } -static int -ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211_node *ni; struct ieee80211req_sta_txpow txpow; @@ -1531,23 +1608,24 @@ ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) error = copyin(ireq->i_data, &txpow, sizeof(txpow)); if (error != 0) return error; - ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr); + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr); if (ni == NULL) - return EINVAL; /* XXX */ + return ENOENT; ni->ni_txpower = txpow.it_txpow; ieee80211_free_node(ni); return error; } -static int -ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) +static __noinline int +ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; 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; + return EOPNOTSUPP; isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); @@ -1610,20 +1688,7 @@ ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) (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; - } + ieee80211_wme_updateparams(vap); return 0; } @@ -1666,10 +1731,7 @@ findchannel(struct ieee80211com *ic, int ieee, int mode) u_int modeflags; int i; - KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); modeflags = chanflags[mode]; - KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO, - ("no chanflags for mode %u", mode)); for (i = 0; i < ic->ic_nchans; i++) { struct ieee80211_channel *c = &ic->ic_channels[i]; @@ -1735,44 +1797,56 @@ check_mode_consistency(const struct ieee80211_channel *c, int mode) * change or a kick of the state machine. */ static int -setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) +setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c) { + struct ieee80211com *ic = vap->iv_ic; int error; if (c != IEEE80211_CHAN_ANYC) { - if (ic->ic_opmode == IEEE80211_M_HOSTAP && - !check_mode_consistency(c, ic->ic_des_mode)) - return EINVAL; - if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan) + if (IEEE80211_IS_CHAN_RADAR(c)) + return EBUSY; /* XXX better code? */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + if (IEEE80211_IS_CHAN_NOHOSTAP(c)) + return EINVAL; + if (!check_mode_consistency(c, vap->iv_des_mode)) + return EINVAL; + } else if (vap->iv_opmode == IEEE80211_M_IBSS) { + if (IEEE80211_IS_CHAN_NOADHOC(c)) + return EINVAL; + } + if (vap->iv_state == IEEE80211_S_RUN && + vap->iv_bss->ni_chan == c) return 0; /* NB: nothing to do */ } - ic->ic_des_chan = c; + vap->iv_des_chan = c; error = 0; - if ((ic->ic_opmode == IEEE80211_M_MONITOR || - ic->ic_opmode == IEEE80211_M_WDS) && - ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + if (vap->iv_opmode == IEEE80211_M_MONITOR && + vap->iv_des_chan != IEEE80211_CHAN_ANYC) { /* - * Monitor and wds modes can switch directly. + * Monitor mode can switch directly. */ - ic->ic_curchan = ic->ic_des_chan; - if (ic->ic_state == IEEE80211_S_RUN) - ic->ic_set_channel(ic); + if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) { + /* XXX need state machine for other vap's to follow */ + ieee80211_setcurchan(ic, vap->iv_des_chan); + vap->iv_bss->ni_chan = ic->ic_curchan; + } else + ic->ic_curchan = vap->iv_des_chan; } else { /* * Need to go through the state machine in case we * need to reassociate or the like. The state machine * will pickup the desired channel and avoid scanning. */ - if (IS_UP_AUTO(ic)) - error = ieee80211_init(ic, RESCAN); - else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + if (IS_UP_AUTO(vap)) + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { /* * When not up+running and a real channel has * been specified fix the current channel so * there is immediate feedback; e.g. via ifconfig. */ - ic->ic_curchan = ic->ic_des_chan; + ic->ic_curchan = vap->iv_des_chan; } } return error; @@ -1782,10 +1856,11 @@ setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) * Old api for setting the current channel; this is * deprecated because channel numbers are ambiguous. */ -static int -ieee80211_ioctl_setchannel(struct ieee80211com *ic, +static __noinline int +ieee80211_ioctl_setchannel(struct ieee80211vap *vap, const struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *c; /* XXX 0xffff overflows 16-bit signed */ @@ -1797,7 +1872,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic, } else { struct ieee80211_channel *c2; - c = findchannel(ic, ireq->i_val, ic->ic_des_mode); + c = findchannel(ic, ireq->i_val, vap->iv_des_mode); if (c == NULL) { c = findchannel(ic, ireq->i_val, IEEE80211_MODE_AUTO); @@ -1816,7 +1891,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic, * 11g channel returned, * otherwise we should be ok with what we've got. */ - switch (ic->ic_des_mode) { + switch (vap->iv_des_mode) { case IEEE80211_MODE_11B: if (IEEE80211_IS_CHAN_ANYG(c)) { c2 = findchannel(ic, ireq->i_val, @@ -1854,7 +1929,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic, break; } } - return setcurchan(ic, c); + return setcurchan(vap, c); } /* @@ -1862,10 +1937,11 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic, * channel description is provide so there is no ambiguity in * identifying the channel. */ -static int -ieee80211_ioctl_setcurchan(struct ieee80211com *ic, +static __noinline int +ieee80211_ioctl_setcurchan(struct ieee80211vap *vap, const struct ieee80211req *ireq) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel chan, *c; int error; @@ -1882,21 +1958,494 @@ ieee80211_ioctl_setcurchan(struct ieee80211com *ic, if (c == NULL) return EINVAL; } - return setcurchan(ic, c); + return setcurchan(vap, c); +} + +static __noinline int +ieee80211_ioctl_setregdomain(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211_regdomain_req *reg; + int error; + + if (ireq->i_len != sizeof(struct ieee80211_regdomain_req)) + return EINVAL; + MALLOC(reg, struct ieee80211_regdomain_req *, + sizeof(struct ieee80211_regdomain_req), M_TEMP, M_NOWAIT); + if (reg == NULL) + return ENOMEM; + error = copyin(ireq->i_data, reg, sizeof(*reg)); + if (error == 0) + error = ieee80211_setregdomain(vap, reg); + FREE(reg, M_TEMP); + + return (error == 0 ? ENETRESET : error); +} + +static int +ieee80211_ioctl_setroam(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + if (ireq->i_len != sizeof(vap->iv_roamparms)) + return EINVAL; + /* XXX validate params */ + /* XXX? ENETRESET to push to device? */ + return copyin(ireq->i_data, vap->iv_roamparms, + sizeof(vap->iv_roamparms)); +} + +static int +checkrate(const struct ieee80211_rateset *rs, int rate) +{ + int i; + + if (rate == IEEE80211_FIXED_RATE_NONE) + return 1; + for (i = 0; i < rs->rs_nrates; i++) + if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate) + return 1; + return 0; +} + +static int +checkmcs(int mcs) +{ + if (mcs == IEEE80211_FIXED_RATE_NONE) + return 1; + if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */ + return 0; + return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */ +} + +static __noinline int +ieee80211_ioctl_settxparams(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_txparams_req parms; /* XXX stack use? */ + struct ieee80211_txparam *src, *dst; + const struct ieee80211_rateset *rs; + int error, i, changed; + + if (ireq->i_len != sizeof(parms)) + return EINVAL; + error = copyin(ireq->i_data, &parms, sizeof(parms)); + if (error != 0) + return error; + changed = 0; + /* validate parameters and check if anything changed */ + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) { + if (isclr(ic->ic_modecaps, i)) + continue; + src = &parms.params[i]; + dst = &vap->iv_txparms[i]; + rs = &ic->ic_sup_rates[i]; + if (src->ucastrate != dst->ucastrate) { + if (!checkrate(rs, src->ucastrate)) + return EINVAL; + changed++; + } + if (src->mcastrate != dst->mcastrate) { + if (!checkrate(rs, src->mcastrate)) + return EINVAL; + changed++; + } + if (src->mgmtrate != dst->mgmtrate) { + if (!checkrate(rs, src->mgmtrate)) + return EINVAL; + changed++; + } + if (src->maxretry != dst->maxretry) /* NB: no bounds */ + changed++; + } + /* 11n parameters are handled differently */ + for (; i < IEEE80211_MODE_MAX; i++) { + if (isclr(ic->ic_modecaps, i)) + continue; + src = &parms.params[i]; + dst = &vap->iv_txparms[i]; + rs = &ic->ic_sup_rates[i == IEEE80211_MODE_11NA ? + IEEE80211_MODE_11A : IEEE80211_MODE_11G]; + if (src->ucastrate != dst->ucastrate) { + if (!checkmcs(src->ucastrate) && + !checkrate(rs, src->ucastrate)) + return EINVAL; + changed++; + } + if (src->mcastrate != dst->mcastrate) { + if (!checkmcs(src->mcastrate) && + !checkrate(rs, src->mcastrate)) + return EINVAL; + changed++; + } + if (src->mgmtrate != dst->mgmtrate) { + if (!checkmcs(src->mgmtrate) && + !checkrate(rs, src->mgmtrate)) + return EINVAL; + changed++; + } + if (src->maxretry != dst->maxretry) /* NB: no bounds */ + changed++; + } + if (changed) { + /* + * Copy new parameters in place and notify the + * driver so it can push state to the device. + */ + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { + if (isset(ic->ic_modecaps, i)) + vap->iv_txparms[i] = parms.params[i]; + } + /* XXX could be more intelligent, + e.g. don't reset if setting not being used */ + return ENETRESET; + } + return 0; +} + +/* + * Application Information Element support. + */ +static int +setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq) +{ + struct ieee80211_appie *app = *aie; + struct ieee80211_appie *napp; + int error; + + if (ireq->i_len == 0) { /* delete any existing ie */ + if (app != NULL) { + *aie = NULL; /* XXX racey */ + FREE(app, M_80211_NODE_IE); + } + return 0; + } + if (!(2 <= ireq->i_len && ireq->i_len <= IEEE80211_MAX_APPIE)) + return EINVAL; + /* + * Allocate a new appie structure and copy in the user data. + * When done swap in the new structure. Note that we do not + * guard against users holding a ref to the old structure; + * this must be handled outside this code. + * + * XXX bad bad bad + */ + MALLOC(napp, struct ieee80211_appie *, + sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_NOWAIT); + if (napp == NULL) + return ENOMEM; + /* XXX holding ic lock */ + error = copyin(ireq->i_data, napp->ie_data, ireq->i_len); + if (error) { + FREE(napp, M_80211_NODE_IE); + return error; + } + napp->ie_len = ireq->i_len; + *aie = napp; + if (app != NULL) + FREE(app, M_80211_NODE_IE); + return 0; +} + +static void +setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space) +{ + /* validate data is present as best we can */ + if (space == 0 || 2+ie[1] > space) + return; + if (ie[0] == IEEE80211_ELEMID_VENDOR) + vap->iv_wpa_ie = ie; + else if (ie[0] == IEEE80211_ELEMID_RSN) + vap->iv_rsn_ie = ie; +} + +static __noinline int +ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap, + const struct ieee80211req *ireq, int fc0) +{ + int error; + + IEEE80211_LOCK_ASSERT(vap->iv_ic); + + switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_BEACON: + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_IBSS) { + error = EINVAL; + break; + } + error = setappie(&vap->iv_appie_beacon, ireq); + if (error == 0) + ieee80211_beacon_notify(vap, IEEE80211_BEACON_APPIE); + break; + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + error = setappie(&vap->iv_appie_proberesp, ireq); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + error = setappie(&vap->iv_appie_assocresp, ireq); + else + error = EINVAL; + break; + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + error = setappie(&vap->iv_appie_probereq, ireq); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + if (vap->iv_opmode == IEEE80211_M_STA) + error = setappie(&vap->iv_appie_assocreq, ireq); + else + error = EINVAL; + break; + case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK): + error = setappie(&vap->iv_appie_wpa, ireq); + if (error == 0) { + /* + * Must split single blob of data into separate + * WPA and RSN ie's because they go in different + * locations in the mgt frames. + * XXX use IEEE80211_IOC_WPA2 so user code does split + */ + vap->iv_wpa_ie = NULL; + vap->iv_rsn_ie = NULL; + if (vap->iv_appie_wpa != NULL) { + struct ieee80211_appie *appie = + vap->iv_appie_wpa; + uint8_t *data = appie->ie_data; + + /* XXX ie length validate is painful, cheat */ + setwparsnie(vap, data, appie->ie_len); + setwparsnie(vap, data + 2 + data[1], + appie->ie_len - (2 + data[1])); + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + /* + * Must rebuild beacon frame as the update + * mechanism doesn't handle WPA/RSN ie's. + * Could extend it but it doesn't normally + * change; this is just to deal with hostapd + * plumbing the ie after the interface is up. + */ + error = ENETRESET; + } + } + break; + default: + error = EINVAL; + break; + } + return error; +} + +static __noinline int +ieee80211_ioctl_setappie(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + int error; + uint8_t fc0; + + fc0 = ireq->i_val & 0xff; + if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return EINVAL; + /* NB: could check iv_opmode and reject but hardly worth the effort */ + IEEE80211_LOCK(ic); + error = ieee80211_ioctl_setappie_locked(vap, ireq, fc0); + IEEE80211_UNLOCK(ic); + return error; +} + +static __noinline int +ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_chanswitch_req csr; + struct ieee80211_channel *c; + int error; + + if (ireq->i_len != sizeof(csr)) + return EINVAL; + error = copyin(ireq->i_data, &csr, sizeof(csr)); + if (error != 0) + return error; + if ((vap->iv_flags & IEEE80211_F_DOTH) == 0) + return EINVAL; + c = ieee80211_find_channel(ic, + csr.csa_chan.ic_freq, csr.csa_chan.ic_flags); + if (c == NULL) + return ENOENT; + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) + ieee80211_csa_startswitch(ic, c, csr.csa_mode, csr.csa_count); + else + error = EBUSY; + IEEE80211_UNLOCK(ic); + return error; +} + +static __noinline int +ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ +#define IEEE80211_IOC_SCAN_FLAGS \ + (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \ + IEEE80211_IOC_SCAN_PICK1ST | IEEE80211_IOC_SCAN_BGSCAN | \ + IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOBCAST | \ + IEEE80211_IOC_SCAN_NOJOIN | IEEE80211_IOC_SCAN_FLUSH | \ + IEEE80211_IOC_SCAN_CHECK) + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_req sr; /* XXX off stack? */ + int error, i; + + /* NB: parent must be running */ + if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return ENXIO; + + if (ireq->i_len != sizeof(sr)) + return EINVAL; + error = copyin(ireq->i_data, &sr, sizeof(sr)); + if (error != 0) + return error; + /* convert duration */ + if (sr.sr_duration == IEEE80211_IOC_SCAN_FOREVER) + sr.sr_duration = IEEE80211_SCAN_FOREVER; + else { + if (sr.sr_duration < IEEE80211_IOC_SCAN_DURATION_MIN || + sr.sr_duration > IEEE80211_IOC_SCAN_DURATION_MAX) + return EINVAL; + sr.sr_duration = msecs_to_ticks(sr.sr_duration); + if (sr.sr_duration < 1) + sr.sr_duration = 1; + } + /* convert min/max channel dwell */ + if (sr.sr_mindwell != 0) { + sr.sr_mindwell = msecs_to_ticks(sr.sr_mindwell); + if (sr.sr_mindwell < 1) + sr.sr_mindwell = 1; + } + if (sr.sr_maxdwell != 0) { + sr.sr_maxdwell = msecs_to_ticks(sr.sr_maxdwell); + if (sr.sr_maxdwell < 1) + sr.sr_maxdwell = 1; + } + /* NB: silently reduce ssid count to what is supported */ + if (sr.sr_nssid > IEEE80211_SCAN_MAX_SSID) + sr.sr_nssid = IEEE80211_SCAN_MAX_SSID; + for (i = 0; i < sr.sr_nssid; i++) + if (sr.sr_ssid[i].len > IEEE80211_NWID_LEN) + return EINVAL; + /* cleanse flags just in case, could reject if invalid flags */ + sr.sr_flags &= IEEE80211_IOC_SCAN_FLAGS; + /* + * Add an implicit NOPICK if the vap is not marked UP. This + * allows applications to scan without joining a bss (or picking + * a channel and setting up a bss) and without forcing manual + * roaming mode--you just need to mark the parent device UP. + */ + if ((vap->iv_ifp->if_flags & IFF_UP) == 0) + sr.sr_flags |= IEEE80211_IOC_SCAN_NOPICK; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: flags 0x%x%s duration 0x%x mindwell %u maxdwell %u nssid %d\n", + __func__, sr.sr_flags, + (vap->iv_ifp->if_flags & IFF_UP) == 0 ? " (!IFF_UP)" : "", + sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, sr.sr_nssid); + /* + * If we are in INIT state then the driver has never had a chance + * to setup hardware state to do a scan; we must use the state + * machine to get us up to the SCAN state but once we reach SCAN + * state we then want to use the supplied params. Stash the + * parameters in the vap and mark IEEE80211_FEXT_SCANREQ; the + * state machines will recognize this and use the stashed params + * to issue the scan request. + * + * Otherwise just invoke the scan machinery directly. + */ + IEEE80211_LOCK(ic); + if (vap->iv_state == IEEE80211_S_INIT) { + /* NB: clobbers previous settings */ + vap->iv_scanreq_flags = sr.sr_flags; + vap->iv_scanreq_duration = sr.sr_duration; + vap->iv_scanreq_nssid = sr.sr_nssid; + for (i = 0; i < sr.sr_nssid; i++) { + vap->iv_scanreq_ssid[i].len = sr.sr_ssid[i].len; + memcpy(vap->iv_scanreq_ssid[i].ssid, sr.sr_ssid[i].ssid, + sr.sr_ssid[i].len); + } + vap->iv_flags_ext |= IEEE80211_FEXT_SCANREQ; + IEEE80211_UNLOCK(ic); + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } else { + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; + IEEE80211_UNLOCK(ic); + /* XXX neeed error return codes */ + if (sr.sr_flags & IEEE80211_IOC_SCAN_CHECK) { + (void) ieee80211_check_scan(vap, sr.sr_flags, + sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, + sr.sr_nssid, + /* NB: cheat, we assume structures are compatible */ + (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]); + } else { + (void) ieee80211_start_scan(vap, sr.sr_flags, + sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, + sr.sr_nssid, + /* NB: cheat, we assume structures are compatible */ + (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]); + } + } + return error; +#undef IEEE80211_IOC_SCAN_FLAGS +} + +static __noinline int +ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_vlan vlan; + int error; + + if (ireq->i_len != sizeof(vlan)) + return EINVAL; + error = copyin(ireq->i_data, &vlan, sizeof(vlan)); + if (error != 0) + return error; + if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + vlan.sv_macaddr); + if (ni == NULL) + return ENOENT; + } else + ni = ieee80211_ref_node(vap->iv_bss); + ni->ni_vlan = vlan.sv_vlan; + ieee80211_free_node(ni); + return error; +} + +static int +isvap11g(const struct ieee80211vap *vap) +{ + const struct ieee80211_node *bss = vap->iv_bss; + return bss->ni_chan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(bss->ni_chan); } static int -ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) +isvapht(const struct ieee80211vap *vap) { - static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; - struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + const struct ieee80211_node *bss = vap->iv_bss; + return bss->ni_chan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(bss->ni_chan); +} + +static __noinline int +ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; 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; @@ -1908,39 +2457,37 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re error = copyin(ireq->i_data, tmpssid, ireq->i_len); if (error) break; - memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); - ic->ic_des_ssid[0].len = ireq->i_len; - memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len); - ic->ic_des_nssid = (ireq->i_len > 0); - if (IS_UP_AUTO(ic)) - error = ieee80211_init(ic, RESCAN); + memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); + vap->iv_des_ssid[0].len = ireq->i_len; + memcpy(vap->iv_des_ssid[0].ssid, tmpssid, ireq->i_len); + vap->iv_des_nssid = (ireq->i_len > 0); + 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; + vap->iv_flags &= ~IEEE80211_F_PRIVACY; + vap->iv_flags &= ~IEEE80211_F_DROPUNENC; break; case IEEE80211_WEP_ON: - ic->ic_flags |= IEEE80211_F_PRIVACY; - ic->ic_flags |= IEEE80211_F_DROPUNENC; + vap->iv_flags |= IEEE80211_F_PRIVACY; + vap->iv_flags |= IEEE80211_F_DROPUNENC; break; case IEEE80211_WEP_MIXED: - ic->ic_flags |= IEEE80211_F_PRIVACY; - ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + vap->iv_flags |= IEEE80211_F_PRIVACY; + vap->iv_flags &= ~IEEE80211_F_DROPUNENC; break; } - if (IS_UP_AUTO(ic)) - error = ieee80211_init(ic, RESCAN); + error = ENETRESET; break; case IEEE80211_IOC_WEPKEY: kid = (u_int) ireq->i_val; if (kid >= IEEE80211_WEP_NKID) return EINVAL; - k = &ic->ic_nw_keys[kid]; + k = &vap->iv_nw_keys[kid]; if (ireq->i_len == 0) { /* zero-len =>'s delete any existing key */ - (void) ieee80211_crypto_delkey(ic, k); + (void) ieee80211_crypto_delkey(vap, k); break; } if (ireq->i_len > sizeof(tmpkey)) @@ -1949,24 +2496,24 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re error = copyin(ireq->i_data, tmpkey, ireq->i_len); if (error) break; - ieee80211_key_update_begin(ic); + ieee80211_key_update_begin(vap); k->wk_keyix = kid; /* NB: force fixed key id */ - if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, + if (ieee80211_crypto_newkey(vap, 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)) + if (!ieee80211_crypto_setkey(vap, k, vap->iv_myaddr)) error = EINVAL; } else error = EINVAL; - ieee80211_key_update_end(ic); + ieee80211_key_update_end(vap); 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; + vap->iv_def_txkey = kid; break; case IEEE80211_IOC_AUTHMODE: switch (ireq->i_val) { @@ -1984,74 +2531,66 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re } switch (ireq->i_val) { case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ - ic->ic_flags |= IEEE80211_F_PRIVACY; + vap->iv_flags |= IEEE80211_F_PRIVACY; ireq->i_val = IEEE80211_AUTH_8021X; break; case IEEE80211_AUTH_OPEN: /* open */ - ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); + vap->iv_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); break; case IEEE80211_AUTH_SHARED: /* shared-key */ case IEEE80211_AUTH_8021X: /* 802.1x */ - ic->ic_flags &= ~IEEE80211_F_WPA; + vap->iv_flags &= ~IEEE80211_F_WPA; /* both require a key so mark the PRIVACY capability */ - ic->ic_flags |= IEEE80211_F_PRIVACY; + vap->iv_flags |= IEEE80211_F_PRIVACY; break; case IEEE80211_AUTH_AUTO: /* auto */ - ic->ic_flags &= ~IEEE80211_F_WPA; + vap->iv_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; + vap->iv_bss->ni_authmode = ireq->i_val; /* XXX mixed/mode/usage? */ - ic->ic_auth = auth; - if (IS_UP_AUTO(ic)) - error = ieee80211_init(ic, RESCAN); + vap->iv_auth = auth; + error = ENETRESET; break; case IEEE80211_IOC_CHANNEL: - error = ieee80211_ioctl_setchannel(ic, ireq); + error = ieee80211_ioctl_setchannel(vap, ireq); 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; + if (vap->iv_flags & IEEE80211_F_PMGTON) { + ieee80211_syncflag(vap, -IEEE80211_F_PMGTON); + error = ERESTART; } 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; + if ((vap->iv_caps & IEEE80211_C_PMGT) == 0) + error = EOPNOTSUPP; + else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) { + ieee80211_syncflag(vap, IEEE80211_F_PMGTON); + error = ERESTART; } break; default: error = EINVAL; break; } - if (error == ENETRESET) { - /* - * Switching in+out of power save mode - * should not require a state change. - */ - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; - } 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; + error = ERESTART; 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; + vap->iv_rtsthreshold = ireq->i_val; + error = ERESTART; break; case IEEE80211_IOC_PROTMODE: if (ireq->i_val > IEEE80211_PROT_RTSCTS) @@ -2060,158 +2599,96 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re /* NB: if not operating in 11g this can wait */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + error = ERESTART; break; case IEEE80211_IOC_TXPOWER: if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) - return EINVAL; + return EOPNOTSUPP; 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; + error = ERESTART; 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; + vap->iv_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; + vap->iv_flags |= IEEE80211_F_PRIVACY; } else - ic->ic_flags &= ~IEEE80211_F_PRIVACY; + vap->iv_flags &= ~IEEE80211_F_PRIVACY; + /* XXX ERESTART? */ break; case IEEE80211_IOC_DROPUNENCRYPTED: if (ireq->i_val) - ic->ic_flags |= IEEE80211_F_DROPUNENC; + vap->iv_flags |= IEEE80211_F_DROPUNENC; else - ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + vap->iv_flags &= ~IEEE80211_F_DROPUNENC; + /* XXX ERESTART? */ break; case IEEE80211_IOC_WPAKEY: - error = ieee80211_ioctl_setkey(ic, ireq); + error = ieee80211_ioctl_setkey(vap, ireq); break; case IEEE80211_IOC_DELKEY: - error = ieee80211_ioctl_delkey(ic, ireq); + error = ieee80211_ioctl_delkey(vap, ireq); break; case IEEE80211_IOC_MLME: - error = ieee80211_ioctl_setmlme(ic, ireq); - break; - case IEEE80211_IOC_OPTIE: - error = ieee80211_ioctl_setoptie(ic, ireq); + error = ieee80211_ioctl_setmlme(vap, 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; + if ((vap->iv_flags & IEEE80211_F_WPA) == 0) + return EOPNOTSUPP; + vap->iv_flags |= IEEE80211_F_COUNTERM; } else - ic->ic_flags &= ~IEEE80211_F_COUNTERM; + vap->iv_flags &= ~IEEE80211_F_COUNTERM; + /* XXX ERESTART? */ break; case IEEE80211_IOC_WPA: if (ireq->i_val > 3) return EINVAL; /* XXX verify ciphers available */ - ic->ic_flags &= ~IEEE80211_F_WPA; + vap->iv_flags &= ~IEEE80211_F_WPA; switch (ireq->i_val) { case 1: - ic->ic_flags |= IEEE80211_F_WPA1; + vap->iv_flags |= IEEE80211_F_WPA1; break; case 2: - ic->ic_flags |= IEEE80211_F_WPA2; + vap->iv_flags |= IEEE80211_F_WPA2; break; case 3: - ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; + vap->iv_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; break; } - error = ENETRESET; + error = ERESTART; /* NB: can change beacon frame */ 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; + if ((vap->iv_caps & IEEE80211_C_WME) == 0) + return EOPNOTSUPP; + ieee80211_syncflag(vap, IEEE80211_F_WME); } else - ic->ic_flags &= ~IEEE80211_F_WME; - if (IS_UP_AUTO(ic)) - error = ieee80211_init(ic, 0); + ieee80211_syncflag(vap, -IEEE80211_F_WME); + error = ERESTART; /* NB: can change beacon frame */ break; case IEEE80211_IOC_HIDESSID: if (ireq->i_val) - ic->ic_flags |= IEEE80211_F_HIDESSID; + vap->iv_flags |= IEEE80211_F_HIDESSID; else - ic->ic_flags &= ~IEEE80211_F_HIDESSID; - error = ENETRESET; + vap->iv_flags &= ~IEEE80211_F_HIDESSID; + error = ERESTART; /* XXX ENETRESET? */ break; case IEEE80211_IOC_APBRIDGE: if (ireq->i_val == 0) - ic->ic_flags |= IEEE80211_F_NOBRIDGE; + vap->iv_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<<j)) && - ((ic->ic_caps & cipher2cap(j)) || - ieee80211_crypto_available(j))) - caps |= 1<<j; - if (caps == 0) /* nothing available */ - return EINVAL; - /* XXX verify ciphers ok for unicast use? */ - /* XXX disallow if running as it'll have no effect */ - rsn->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; + vap->iv_flags &= ~IEEE80211_F_NOBRIDGE; break; case IEEE80211_IOC_BSSID: if (ireq->i_len != sizeof(tmpbssid)) @@ -2219,39 +2696,71 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re 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; + IEEE80211_ADDR_COPY(vap->iv_des_bssid, tmpbssid); + if (IEEE80211_ADDR_EQ(vap->iv_des_bssid, zerobssid)) + vap->iv_flags &= ~IEEE80211_F_DESBSSID; else - ic->ic_flags |= IEEE80211_F_DESBSSID; - if (IS_UP_AUTO(ic)) - error = ieee80211_init(ic, RESCAN); + vap->iv_flags |= IEEE80211_F_DESBSSID; + error = ENETRESET; break; case IEEE80211_IOC_CHANLIST: - error = ieee80211_ioctl_setchanlist(ic, ireq); + error = ieee80211_ioctl_setchanlist(vap, ireq); break; +#define OLD_IEEE80211_IOC_SCAN_REQ 23 +#ifdef OLD_IEEE80211_IOC_SCAN_REQ + case OLD_IEEE80211_IOC_SCAN_REQ: + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: active scan request\n", __func__); + /* + * If we are in INIT state then the driver has never + * had a chance to setup hardware state to do a scan; + * use the state machine to get us up the SCAN state. + * Otherwise just invoke the scan machinery to start + * a one-time scan. + */ + if (vap->iv_state == IEEE80211_S_INIT) + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + else + (void) ieee80211_start_scan(vap, + IEEE80211_SCAN_ACTIVE | + IEEE80211_SCAN_NOPICK | + IEEE80211_SCAN_ONCE, + IEEE80211_SCAN_FOREVER, 0, 0, + /* XXX use ioctl params */ + vap->iv_des_nssid, vap->iv_des_ssid); + break; +#endif /* OLD_IEEE80211_IOC_SCAN_REQ */ case IEEE80211_IOC_SCAN_REQ: - if (!IS_UP(ic)) - return EINVAL; - (void) ieee80211_start_scan(ic, - IEEE80211_SCAN_ACTIVE | - IEEE80211_SCAN_NOPICK | - IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER, - /* XXX use ioctl params */ - ic->ic_des_nssid, ic->ic_des_ssid); + error = ieee80211_ioctl_scanreq(vap, ireq); + break; + case IEEE80211_IOC_SCAN_CANCEL: + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel scan\n", __func__); + ieee80211_cancel_scan(vap); + break; + case IEEE80211_IOC_HTCONF: + if (ireq->i_val & 1) + ieee80211_syncflag_ext(vap, IEEE80211_FEXT_HT); + else + ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_HT); + if (ireq->i_val & 2) + ieee80211_syncflag_ext(vap, IEEE80211_FEXT_USEHT40); + else + ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_USEHT40); + error = ENETRESET; break; case IEEE80211_IOC_ADDMAC: case IEEE80211_IOC_DELMAC: - error = ieee80211_ioctl_macmac(ic, ireq); + error = ieee80211_ioctl_macmac(vap, ireq); break; case IEEE80211_IOC_MACCMD: - error = ieee80211_ioctl_setmaccmd(ic, ireq); + error = ieee80211_ioctl_setmaccmd(vap, ireq); break; case IEEE80211_IOC_STA_STATS: - error = ieee80211_ioctl_setstastats(ic, ireq); + error = ieee80211_ioctl_setstastats(vap, ireq); break; case IEEE80211_IOC_STA_TXPOW: - error = ieee80211_ioctl_setstatxpow(ic, ireq); + error = ieee80211_ioctl_setstatxpow(vap, ireq); break; case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ @@ -2259,22 +2768,22 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re 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); + error = ieee80211_ioctl_setwmeparam(vap, ireq); break; case IEEE80211_IOC_DTIM_PERIOD: - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_opmode != IEEE80211_M_IBSS) + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_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; + vap->iv_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) + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_IBSS) return EINVAL; if (IEEE80211_BINTVAL_MIN <= ireq->i_val && ireq->i_val <= IEEE80211_BINTVAL_MAX) { @@ -2285,210 +2794,258 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re break; case IEEE80211_IOC_PUREG: if (ireq->i_val) - ic->ic_flags |= IEEE80211_F_PUREG; + vap->iv_flags |= IEEE80211_F_PUREG; else - ic->ic_flags &= ~IEEE80211_F_PUREG; + vap->iv_flags &= ~IEEE80211_F_PUREG; /* NB: reset only if we're operating on an 11g channel */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) + if (isvap11g(vap)) error = ENETRESET; break; case IEEE80211_IOC_FF: if (ireq->i_val) { - if ((ic->ic_caps & IEEE80211_C_FF) == 0) - return EINVAL; - ic->ic_flags |= IEEE80211_F_FF; + if ((vap->iv_caps & IEEE80211_C_FF) == 0) + return EOPNOTSUPP; + vap->iv_flags |= IEEE80211_F_FF; } else - ic->ic_flags &= ~IEEE80211_F_FF; - error = ENETRESET; + vap->iv_flags &= ~IEEE80211_F_FF; + error = ERESTART; break; case IEEE80211_IOC_TURBOP: if (ireq->i_val) { - if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0) - return EINVAL; - ic->ic_flags |= IEEE80211_F_TURBOP; + if ((vap->iv_caps & IEEE80211_C_TURBOP) == 0) + return EOPNOTSUPP; + vap->iv_flags |= IEEE80211_F_TURBOP; } else - ic->ic_flags &= ~IEEE80211_F_TURBOP; + vap->iv_flags &= ~IEEE80211_F_TURBOP; error = ENETRESET; break; case IEEE80211_IOC_BGSCAN: if (ireq->i_val) { - if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0) - return EINVAL; - ic->ic_flags |= IEEE80211_F_BGSCAN; + if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0) + return EOPNOTSUPP; + vap->iv_flags |= IEEE80211_F_BGSCAN; } else - ic->ic_flags &= ~IEEE80211_F_BGSCAN; + vap->iv_flags &= ~IEEE80211_F_BGSCAN; break; case IEEE80211_IOC_BGSCAN_IDLE: if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN) - ic->ic_bgscanidle = ireq->i_val*hz/1000; + vap->iv_bgscanidle = ireq->i_val*hz/1000; else error = EINVAL; break; case IEEE80211_IOC_BGSCAN_INTERVAL: if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN) - ic->ic_bgscanintvl = ireq->i_val*hz; + vap->iv_bgscanintvl = ireq->i_val*hz; else error = EINVAL; break; case IEEE80211_IOC_SCANVALID: if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN) - ic->ic_scanvalid = ireq->i_val*hz; + vap->iv_scanvalid = ireq->i_val*hz; else error = EINVAL; break; - case IEEE80211_IOC_ROAM_RSSI_11A: - ic->ic_roam.rssi11a = ireq->i_val; - break; - case IEEE80211_IOC_ROAM_RSSI_11B: - ic->ic_roam.rssi11bOnly = ireq->i_val; - break; - case IEEE80211_IOC_ROAM_RSSI_11G: - ic->ic_roam.rssi11b = ireq->i_val; - break; - case IEEE80211_IOC_ROAM_RATE_11A: - ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL; - break; - case IEEE80211_IOC_ROAM_RATE_11B: - ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL; - break; - case IEEE80211_IOC_ROAM_RATE_11G: - ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL; - 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 && + if ((vap->iv_caps & IEEE80211_C_TXFRAG) == 0 && ireq->i_val != IEEE80211_FRAG_MAX) - return EINVAL; + return EOPNOTSUPP; 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; + vap->iv_fragthreshold = ireq->i_val; + error = ERESTART; 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; + if ((vap->iv_caps & IEEE80211_C_BURST) == 0) + return EOPNOTSUPP; + ieee80211_syncflag(vap, IEEE80211_F_BURST); } else - ic->ic_flags &= ~IEEE80211_F_BURST; - error = ENETRESET; /* XXX maybe not for station? */ + ieee80211_syncflag(vap, -IEEE80211_F_BURST); + error = ERESTART; break; case IEEE80211_IOC_BMISSTHRESHOLD: if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val && ireq->i_val <= IEEE80211_HWBMISS_MAX)) return EINVAL; - ic->ic_bmissthreshold = ireq->i_val; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + vap->iv_bmissthreshold = ireq->i_val; + error = ERESTART; break; case IEEE80211_IOC_CURCHAN: - error = ieee80211_ioctl_setcurchan(ic, ireq); + error = ieee80211_ioctl_setcurchan(vap, ireq); break; case IEEE80211_IOC_SHORTGI: if (ireq->i_val) { #define IEEE80211_HTCAP_SHORTGI \ (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) - if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0) + if (((ireq->i_val ^ vap->iv_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0) return EINVAL; if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20) - ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20; + vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20; if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40) - ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40; + vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40; #undef IEEE80211_HTCAP_SHORTGI } else - ic->ic_flags_ext &= + vap->iv_flags_ext &= ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40); - /* XXX kick state machine? */ - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + error = ERESTART; break; case IEEE80211_IOC_AMPDU: - if (ireq->i_val) { - if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0) - return EINVAL; - if (ireq->i_val & 1) - ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX; - if (ireq->i_val & 2) - ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX; - } else - ic->ic_flags_ext &= - ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX); + if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMPDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_TX; + if (ireq->i_val & 2) + vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_RX; /* NB: reset only if we're operating on an 11n channel */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) - error = ENETRESET; + if (isvapht(vap)) + error = ERESTART; break; case IEEE80211_IOC_AMPDU_LIMIT: - /* XXX validate */ - ic->ic_ampdu_limit = ireq->i_val; + if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val && + ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K)) + return EINVAL; + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + vap->iv_ampdu_rxmax = ireq->i_val; + else + vap->iv_ampdu_limit = ireq->i_val; + error = ERESTART; break; case IEEE80211_IOC_AMPDU_DENSITY: - /* XXX validate */ - ic->ic_ampdu_density = ireq->i_val; + if (!(IEEE80211_HTCAP_MPDUDENSITY_NA <= ireq->i_val && + ireq->i_val <= IEEE80211_HTCAP_MPDUDENSITY_16)) + return EINVAL; + vap->iv_ampdu_density = ireq->i_val; + error = ERESTART; break; case IEEE80211_IOC_AMSDU: - if (ireq->i_val) { - if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0) - return EINVAL; - if (ireq->i_val & 1) - ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX; - if (ireq->i_val & 2) - ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX; - } else - ic->ic_flags_ext &= - ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX); + if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMSDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_TX; + if (ireq->i_val & 2) + vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_RX; /* NB: reset only if we're operating on an 11n channel */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) - error = ENETRESET; + if (isvapht(vap)) + error = ERESTART; break; case IEEE80211_IOC_AMSDU_LIMIT: /* XXX validate */ - ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */ + vap->iv_amsdu_limit = ireq->i_val; /* XXX truncation? */ break; case IEEE80211_IOC_PUREN: if (ireq->i_val) { - if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) + if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0) return EINVAL; - ic->ic_flags_ext |= IEEE80211_FEXT_PUREN; + vap->iv_flags_ext |= IEEE80211_FEXT_PUREN; } else - ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN; + vap->iv_flags_ext &= ~IEEE80211_FEXT_PUREN; /* NB: reset only if we're operating on an 11n channel */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) - error = ENETRESET; + if (isvapht(vap)) + error = ERESTART; break; case IEEE80211_IOC_DOTH: if (ireq->i_val) { #if 0 /* XXX no capability */ - if ((ic->ic_caps & IEEE80211_C_DOTH) == 0) - return EINVAL; + if ((vap->iv_caps & IEEE80211_C_DOTH) == 0) + return EOPNOTSUPP; #endif - ic->ic_flags |= IEEE80211_F_DOTH; + vap->iv_flags |= IEEE80211_F_DOTH; } else - ic->ic_flags &= ~IEEE80211_F_DOTH; - error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + vap->iv_flags &= ~IEEE80211_F_DOTH; + error = ENETRESET; + break; + case IEEE80211_IOC_REGDOMAIN: + error = ieee80211_ioctl_setregdomain(vap, ireq); + break; + case IEEE80211_IOC_ROAM: + error = ieee80211_ioctl_setroam(vap, ireq); + break; + case IEEE80211_IOC_TXPARAMS: + error = ieee80211_ioctl_settxparams(vap, ireq); break; case IEEE80211_IOC_HTCOMPAT: if (ireq->i_val) { - if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0) - return EINVAL; - ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT; + if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0) + return EOPNOTSUPP; + vap->iv_flags_ext |= IEEE80211_FEXT_HTCOMPAT; } else - ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT; + vap->iv_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT; /* NB: reset only if we're operating on an 11n channel */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) - error = ENETRESET; + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_DWDS: + if (ireq->i_val) { + /* NB: DWDS only makes sense for WDS-capable devices */ + if ((ic->ic_caps & IEEE80211_C_WDS) == 0) + return EOPNOTSUPP; + /* NB: DWDS is used only with ap+sta vaps */ + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_STA) + return EINVAL; + vap->iv_flags |= IEEE80211_F_DWDS; + } else + vap->iv_flags &= ~IEEE80211_F_DWDS; break; case IEEE80211_IOC_INACTIVITY: if (ireq->i_val) - ic->ic_flags_ext |= IEEE80211_FEXT_INACT; + vap->iv_flags_ext |= IEEE80211_FEXT_INACT; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_INACT; + break; + case IEEE80211_IOC_APPIE: + error = ieee80211_ioctl_setappie(vap, ireq); + break; + case IEEE80211_IOC_WPS: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_WPA) == 0) + return EOPNOTSUPP; + vap->iv_flags_ext |= IEEE80211_FEXT_WPS; + } else + vap->iv_flags_ext &= ~IEEE80211_FEXT_WPS; + break; + case IEEE80211_IOC_TSN: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_WPA) == 0) + return EOPNOTSUPP; + vap->iv_flags_ext |= IEEE80211_FEXT_TSN; + } else + vap->iv_flags_ext &= ~IEEE80211_FEXT_TSN; + break; + case IEEE80211_IOC_CHANSWITCH: + error = ieee80211_ioctl_chanswitch(vap, ireq); + break; + case IEEE80211_IOC_DFS: + if (ireq->i_val) { +#if 0 + /* XXX no capability */ + if ((vap->iv_caps & IEEE80211_C_DFS) == 0) + return EOPNOTSUPP; +#endif + /* NB: DFS requires 11h support */ + if ((vap->iv_flags & IEEE80211_F_DOTH) == 0) + return EINVAL; + vap->iv_flags_ext |= IEEE80211_FEXT_DFS; + } else + vap->iv_flags_ext &= ~IEEE80211_FEXT_DFS; + break; + case IEEE80211_IOC_DOTD: + if (ireq->i_val) + vap->iv_flags_ext |= IEEE80211_FEXT_DOTD; else - ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT; + vap->iv_flags_ext &= ~IEEE80211_FEXT_DOTD; + if (vap->iv_opmode == IEEE80211_M_STA) + error = ENETRESET; break; case IEEE80211_IOC_HTPROTMODE: if (ireq->i_val > IEEE80211_PROT_RTSCTS) @@ -2496,57 +3053,143 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re ic->ic_htprotmode = ireq->i_val ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE; /* NB: if not operating in 11n this can wait */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + if (isvapht(vap)) error = ERESTART; break; - case IEEE80211_IOC_HTCONF: - if (ireq->i_val & 1) - ic->ic_flags_ext |= IEEE80211_FEXT_HT; - else - ic->ic_flags_ext &= ~IEEE80211_FEXT_HT; - if (ireq->i_val & 2) - ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40; - else - ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40; - error = ENETRESET; + case IEEE80211_IOC_STA_VLAN: + error = ieee80211_ioctl_setstavlan(vap, ireq); break; default: error = EINVAL; break; } - if (error == ENETRESET) - error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0; + /* + * The convention is that ENETRESET means an operation + * requires a complete re-initialization of the device (e.g. + * changing something that affects the association state). + * ERESTART means the request may be handled with only a + * reload of the hardware state. We hand ERESTART requests + * to the iv_reset callback so the driver can decide. If + * a device does not fillin iv_reset then it defaults to one + * that returns ENETRESET. Otherwise a driver may return + * ENETRESET (in which case a full reset will be done) or + * 0 to mean there's no need to do anything (e.g. when the + * change has no effect on the driver/device). + */ + if (error == ERESTART) + error = IFNET_IS_UP_RUNNING(vap->iv_ifp) ? + vap->iv_reset(vap, ireq->i_type) : 0; + if (error == ENETRESET) { + /* XXX need to re-think AUTO handling */ + if (IS_UP_AUTO(vap)) + ieee80211_init(vap); + error = 0; + } return error; } +/* + * Rebuild the parent's multicast address list after an add/del + * of a multicast address for a vap. We have no way to tell + * what happened above to optimize the work so we purge the entire + * list and rebuild from scratch. This is way expensive. + * Note also the half-baked workaround for if_addmulti calling + * back to the parent device; there's no way to insert mcast + * entries quietly and/or cheaply. + */ +static void +ieee80211_ioctl_updatemulti(struct ieee80211com *ic) +{ + struct ifnet *parent = ic->ic_ifp; + struct ieee80211vap *vap; + void *ioctl; + + IEEE80211_LOCK(ic); + if_purgemaddrs(parent); + ioctl = parent->if_ioctl; /* XXX WAR if_allmulti */ + parent->if_ioctl = NULL; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + struct ifmultiaddr *ifma; + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + (void) if_addmulti(parent, ifma->ifma_addr, NULL); + } + parent->if_ioctl = ioctl; + + ic->ic_update_mcast(ic->ic_ifp); + IEEE80211_UNLOCK(ic); +} + int -ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) +ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { - struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; + struct ieee80211com *ic; int error = 0; struct ifreq *ifr; struct ifaddr *ifa; /* XXX */ + vap = ifp->if_softc; + if (vap == NULL) { + /* + * During detach we clear the backpointer in the softc + * so any ioctl request through the ifnet that arrives + * before teardown is ignored/rejected. In particular + * this hack handles destroying a vap used by an app + * like wpa_supplicant that will respond to the vap + * being forced into INIT state by immediately trying + * to force it back up. We can yank this hack if/when + * we can destroy the ifnet before cleaning up vap state. + */ + return ENXIO; + } switch (cmd) { + case SIOCSIFFLAGS: + ic = vap->iv_ic; + IEEE80211_LOCK(ic); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + if (ifp->if_flags & IFF_UP) { + /* + * Bring ourself up unless we're already operational. + * If we're the first vap and the parent is not up + * then it will automatically be brought up as a + * side-effect of bringing ourself up. + */ + if (vap->iv_state == IEEE80211_S_INIT) + ieee80211_start_locked(vap); + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + /* + * Stop ourself. If we are the last vap to be + * marked down the parent will also be taken down. + */ + ieee80211_stop_locked(vap); + } + IEEE80211_UNLOCK(ic); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + ieee80211_ioctl_updatemulti(vap->iv_ic); + break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, (struct ifreq *) data, - &ic->ic_media, cmd); + ifr = (struct ifreq *)data; + error = ifmedia_ioctl(ifp, ifr, &vap->iv_media, cmd); break; case SIOCG80211: - error = ieee80211_ioctl_get80211(ic, cmd, + error = ieee80211_ioctl_get80211(vap, cmd, (struct ieee80211req *) data); break; case SIOCS80211: error = priv_check(curthread, PRIV_NET80211_MANAGE); if (error == 0) - error = ieee80211_ioctl_set80211(ic, cmd, + error = ieee80211_ioctl_set80211(vap, cmd, (struct ieee80211req *) data); break; case SIOCG80211STATS: ifr = (struct ifreq *)data; - copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); + copyout(&vap->iv_stats, ifr->ifr_data, sizeof (vap->iv_stats)); break; case SIOCSIFMTU: ifr = (struct ifreq *)data; @@ -2601,6 +3244,14 @@ ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) break; } break; + /* Pass NDIS ioctls up to the driver */ + case SIOCGDRVSPEC: + case SIOCSDRVSPEC: + case SIOCGPRIVATE_0: { + struct ifnet *parent = vap->iv_ic->ic_ifp; + error = parent->if_ioctl(parent, cmd, data); + break; + } default: error = ether_ioctl(ifp, cmd, data); break; diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index e96ab52..c40b929 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -196,6 +196,8 @@ struct ieee80211_stats { uint32_t is_tx_badstate; /* tx discard state != RUN */ uint32_t is_tx_notassoc; /* tx failed, sta not assoc */ uint32_t is_tx_classify; /* tx classification failed */ + uint32_t is_dwds_mcast; /* discard mcast over dwds */ + uint32_t is_dwds_qdrop; /* dwds pending frame q full */ uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */ uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */ uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */ @@ -208,7 +210,12 @@ struct ieee80211_stats { uint32_t is_ampdu_stop; /* A-MPDU stream stopped */ uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */ uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */ - uint32_t is_spare[16]; + uint32_t is_scan_bg; /* background scans started */ + uint8_t is_rx_deauth_code; /* last rx'd deauth reason */ + uint8_t is_rx_disassoc_code; /* last rx'd disassoc reason */ + uint8_t is_rx_authfail_code; /* last rx'd auth fail reason */ + uint32_t is_beacon_miss; /* beacon miss notification */ + uint32_t is_spare[14]; }; /* @@ -264,6 +271,7 @@ struct ieee80211req_mlme { #define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */ #define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */ #define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */ +#define IEEE80211_MLME_AUTH 6 /* authenticate 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]; @@ -281,6 +289,7 @@ enum { IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */ IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */ IEEE80211_MACCMD_LIST = 6, /* get ACL database */ + IEEE80211_MACCMD_POLICY_RADIUS = 7, /* set policy: RADIUS managed */ }; struct ieee80211req_maclist { @@ -335,7 +344,7 @@ struct ieee80211req_sta_stats { * to retrieve other data like stats, unicast key, etc. */ struct ieee80211req_sta_info { - uint16_t isi_len; /* length (mult of 4) */ + uint16_t isi_len; /* total length (mult of 4) */ uint16_t isi_ie_off; /* offset to IE data */ uint16_t isi_ie_len; /* IE length */ uint16_t isi_freq; /* MHz */ @@ -350,7 +359,7 @@ struct ieee80211req_sta_info { uint8_t isi_nrates; /* negotiated rates */ uint8_t isi_rates[IEEE80211_RATE_MAXSIZE]; - uint8_t isi_txrate; /* index to isi_rates[] */ + uint8_t isi_txrate; /* legacy/IEEE rate or MCS */ uint16_t isi_associd; /* assoc response */ uint16_t isi_txpower; /* current tx power */ uint16_t isi_vlan; /* vlan tag */ @@ -358,6 +367,9 @@ struct ieee80211req_sta_info { uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */ uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */ uint16_t isi_inact; /* inactivity timer */ + uint16_t isi_txmbps; /* current tx rate in .5 Mb/s */ + uint32_t isi_jointime; /* time of assoc/join */ + struct ieee80211_mimo_info isi_mimo; /* MIMO info for 11n sta's */ /* XXX frag state? */ /* variable length IE data */ }; @@ -384,15 +396,98 @@ struct ieee80211req_sta_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. + * WME parameters manipulated with IEEE80211_IOC_WME_CWMIN + * through IEEE80211_IOC_WME_ACKPOLICY 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 */ +/* + * Application Information Elements can be appended to a variety + * of frames with the IEE80211_IOC_APPIE request. This request + * piggybacks on a normal ieee80211req; the frame type is passed + * in i_val as the 802.11 FC0 bytes and the length of the IE data + * is passed in i_len. The data is referenced in i_data. If i_len + * is zero then any previously configured IE data is removed. At + * most IEEE80211_MAX_APPIE data be appened. Note that multiple + * IE's can be supplied; the data is treated opaquely. + */ +#define IEEE80211_MAX_APPIE 1024 /* max app IE data */ +/* + * Hack: the WPA authenticator uses this mechanism to specify WPA + * ie's that are used instead of the ones normally constructed using + * the cipher state setup with separate ioctls. This avoids issues + * like the authenticator ordering ie data differently than the + * net80211 layer and needing to keep separate state for WPA and RSN. + */ +#define IEEE80211_APPIE_WPA \ + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \ + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + +/* + * Station mode roaming parameters. These are maintained + * per band/mode and control the roaming algorithm. + */ +struct ieee80211_roamparams_req { + struct ieee80211_roamparam params[IEEE80211_MODE_MAX]; +}; + +/* + * Transmit parameters. These can be used to set fixed transmit + * rate for each operating mode when operating as client or on a + * per-client basis according to the capabilities of the client + * (e.g. an 11b client associated to an 11g ap) when operating as + * an ap. + * + * MCS are distinguished from legacy rates by or'ing in 0x80. + */ +struct ieee80211_txparams_req { + struct ieee80211_txparam params[IEEE80211_MODE_MAX]; +}; + +/* + * Set regulatory domain state with IEEE80211_IOC_REGDOMAIN. + * Note this is both the regulatory description and the channel + * list. The get request for IEEE80211_IOC_REGDOMAIN returns + * only the regdomain info; the channel list is obtained + * separately with IEEE80211_IOC_CHANINFO. + */ +struct ieee80211_regdomain_req { + struct ieee80211_regdomain rd; + struct ieee80211req_chaninfo chaninfo; +}; + +/* + * Get driver capabilities. Driver, hardware crypto, and + * HT/802.11n capabilities, and a table that describes what + * the radio can do. + */ +struct ieee80211_devcaps_req { + uint32_t dc_drivercaps; /* general driver caps */ + uint32_t dc_cryptocaps; /* hardware crypto support */ + uint32_t dc_htcaps; /* HT/802.11n support */ + struct ieee80211req_chaninfo dc_chaninfo; +}; + +struct ieee80211_chanswitch_req { + struct ieee80211_channel csa_chan; /* new channel */ + int csa_mode; /* CSA mode */ + int csa_count; /* beacon count to switch */ +}; + +/* + * Get/set per-station vlan tag. + */ +struct ieee80211req_sta_vlan { + uint8_t sv_macaddr[IEEE80211_ADDR_LEN]; + uint16_t sv_vlan; +}; + #ifdef __FreeBSD__ /* * FreeBSD-style ioctls. @@ -443,8 +538,8 @@ struct ieee80211req { #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 +/* 22 was IEEE80211_IOC_OPTIE, replaced by IEEE80211_IOC_APPIE */ +/* 23 was IEEE80211_IOC_SCAN_REQ */ /* 24 was IEEE80211_IOC_SCAN_RESULTS */ #define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */ #define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */ @@ -452,14 +547,8 @@ struct ieee80211req { #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 */ +/* 31-35,37-38 were for WPA authenticator settings */ +/* 36 was IEEE80211_IOC_DRIVER_CAPS */ #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 */ @@ -484,13 +573,7 @@ struct ieee80211req { #define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */ #define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */ #define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */ -#define IEEE80211_IOC_ROAM_RSSI_11A 66 /* rssi threshold in 11a */ -#define IEEE80211_IOC_ROAM_RSSI_11B 67 /* rssi threshold in 11b */ -#define IEEE80211_IOC_ROAM_RSSI_11G 68 /* rssi threshold in 11g */ -#define IEEE80211_IOC_ROAM_RATE_11A 69 /* tx rate threshold in 11a */ -#define IEEE80211_IOC_ROAM_RATE_11B 70 /* tx rate threshold in 11b */ -#define IEEE80211_IOC_ROAM_RATE_11G 71 /* tx rate threshold in 11g */ -#define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */ +/* 66-72 were IEEE80211_IOC_ROAM_* and IEEE80211_IOC_MCAST_RATE */ #define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */ #define IEEE80211_IOC_BURST 75 /* packet bursting */ #define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */ @@ -506,13 +589,84 @@ struct ieee80211req { #define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */ #define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */ #define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */ -#define IEEE80211_IOC_REGDOMAIN 89 /* regulatory domain */ -#define IEEE80211_IOC_COUNTRYCODE 90 /* ISO country code */ -#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */ +/* 89-91 were regulatory items */ #define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */ +#define IEEE80211_IOC_DWDS 93 /* DWDS/4-address handling */ #define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */ +#define IEEE80211_IOC_APPIE 95 /* application IE's */ +#define IEEE80211_IOC_WPS 96 /* WPS operation */ +#define IEEE80211_IOC_TSN 97 /* TSN operation */ +#define IEEE80211_IOC_DEVCAPS 98 /* driver+device capabilities */ +#define IEEE80211_IOC_CHANSWITCH 99 /* start 11h channel switch */ +#define IEEE80211_IOC_DFS 100 /* DFS (on, off) */ +#define IEEE80211_IOC_DOTD 101 /* 802.11d (on, off) */ #define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */ +#define IEEE80211_IOC_SCAN_REQ 103 /* scan w/ specified params */ +#define IEEE80211_IOC_SCAN_CANCEL 104 /* cancel ongoing scan */ #define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/ +#define IEEE80211_IOC_REGDOMAIN 106 /* regulatory domain info */ +#define IEEE80211_IOC_ROAM 107 /* roaming params en masse */ +#define IEEE80211_IOC_TXPARAMS 108 /* tx parameters */ +#define IEEE80211_IOC_STA_VLAN 109 /* per-station vlan tag */ + +/* + * Parameters for controlling a scan requested with + * IEEE80211_IOC_SCAN_REQ. + * + * Active scans cause ProbeRequest frames to be issued for each + * specified ssid and, by default, a broadcast ProbeRequest frame. + * The set of ssid's is specified in the request. + * + * By default the scan will cause a BSS to be joined (in station/adhoc + * mode) or a channel to be selected for operation (hostap mode). + * To disable that specify IEEE80211_IOC_SCAN_NOPICK and if the + * + * If the station is currently associated to an AP then a scan request + * will cause the station to leave the current channel and potentially + * miss frames from the AP. Alternatively the station may notify the + * AP that it is going into power save mode before it leaves the channel. + * This ensures frames for the station are buffered by the AP. This is + * termed a ``bg scan'' and is requested with the IEEE80211_IOC_SCAN_BGSCAN + * flag. Background scans may take longer than foreground scans and may + * be preempted by traffic. If a station is not associated to an AP + * then a request for a background scan is automatically done in the + * foreground. + * + * The results of the scan request are cached by the system. This + * information is aged out and/or invalidated based on events like not + * being able to associated to an AP. To flush the current cache + * contents before doing a scan the IEEE80211_IOC_SCAN_FLUSH flag may + * be specified. + * + * By default the scan will be done until a suitable AP is located + * or a channel is found for use. A scan can also be constrained + * to be done once (IEEE80211_IOC_SCAN_ONCE) or to last for no more + * than a specified duration. + */ +struct ieee80211_scan_req { + int sr_flags; +#define IEEE80211_IOC_SCAN_NOPICK 0x00001 /* scan only, no selection */ +#define IEEE80211_IOC_SCAN_ACTIVE 0x00002 /* active scan (probe req) */ +#define IEEE80211_IOC_SCAN_PICK1ST 0x00004 /* ``hey sailor'' mode */ +#define IEEE80211_IOC_SCAN_BGSCAN 0x00008 /* bg scan, exit ps at end */ +#define IEEE80211_IOC_SCAN_ONCE 0x00010 /* do one complete pass */ +#define IEEE80211_IOC_SCAN_NOBCAST 0x00020 /* don't send bcast probe req */ +#define IEEE80211_IOC_SCAN_NOJOIN 0x00040 /* no auto-sequencing */ +#define IEEE80211_IOC_SCAN_FLUSH 0x10000 /* flush scan cache first */ +#define IEEE80211_IOC_SCAN_CHECK 0x20000 /* check scan cache first */ + u_int sr_duration; /* duration (ms) */ +#define IEEE80211_IOC_SCAN_DURATION_MIN 1 +#define IEEE80211_IOC_SCAN_DURATION_MAX 0x7fffffff +#define IEEE80211_IOC_SCAN_FOREVER IEEE80211_IOC_SCAN_DURATION_MAX + u_int sr_mindwell; /* min channel dwelltime (ms) */ + u_int sr_maxdwell; /* max channel dwelltime (ms) */ + int sr_nssid; +#define IEEE80211_IOC_SCAN_MAX_SSID 3 + struct { + int len; /* length in bytes */ + uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */ + } sr_ssid[IEEE80211_IOC_SCAN_MAX_SSID]; +}; /* * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS. @@ -523,8 +677,8 @@ struct ieee80211req { * in isr_len. Result records are rounded to a multiple of 4 bytes. */ struct ieee80211req_scan_result { - uint16_t isr_len; /* length (mult of 4) */ - uint16_t isr_ie_off; /* offset to IE data */ + uint16_t isr_len; /* total length (mult of 4) */ + uint16_t isr_ie_off; /* offset to SSID+IE data */ uint16_t isr_ie_len; /* IE length */ uint16_t isr_freq; /* MHz */ uint16_t isr_flags; /* channel flags */ @@ -540,10 +694,47 @@ struct ieee80211req_scan_result { /* variable length SSID followed by IE data */ }; +/* + * Virtual AP cloning parameters. The parent device must + * be a vap-capable device. All parameters specified with + * the clone request are fixed for the lifetime of the vap. + * + * There are two flavors of WDS vaps: legacy and dynamic. + * Legacy WDS operation implements a static binding between + * two stations encapsulating traffic in 4-address frames. + * Dynamic WDS vaps are created when a station associates to + * an AP and sends a 4-address frame. If the AP vap is + * configured to support WDS then this will generate an + * event to user programs listening on the routing socket + * and a Dynamic WDS vap will be created to handle traffic + * to/from that station. In both cases the bssid of the + * peer must be specified when creating the vap. + * + * By default a vap will inherit the mac address/bssid of + * the underlying device. To request a unique address the + * IEEE80211_CLONE_BSSID flag should be supplied. This is + * meaningless for WDS vaps as they share the bssid of an + * AP vap that must otherwise exist. Note that some devices + * may not be able to support multiple addresses. + * + * Station mode vap's normally depend on the device to notice + * when the AP stops sending beacon frames. If IEEE80211_CLONE_NOBEACONS + * is specified the net80211 layer will do this in s/w. This + * is mostly useful when setting up a WDS repeater/extender where + * an AP vap is combined with a sta vap and the device isn't able + * to track beacon frames in hardware. + */ struct ieee80211_clone_params { char icp_parent[IFNAMSIZ]; /* parent device */ - int icp_opmode; /* operating mode */ + uint16_t icp_opmode; /* operating mode */ + uint16_t icp_flags; /* see below */ + uint8_t icp_bssid[IEEE80211_ADDR_LEN]; /* for WDS links */ + uint8_t icp_macaddr[IEEE80211_ADDR_LEN];/* local address */ }; +#define IEEE80211_CLONE_BSSID 0x0001 /* allocate unique mac/bssid */ +#define IEEE80211_CLONE_NOBEACONS 0x0002 /* don't setup beacon timers */ +#define IEEE80211_CLONE_WDSLEGACY 0x0004 /* legacy WDS processing */ +#define IEEE80211_CLONE_MACADDR 0x0008 /* use specified mac addr */ #endif /* __FreeBSD__ */ #endif /* _NET80211_IEEE80211_IOCTL_H_ */ diff --git a/sys/net80211/ieee80211_monitor.c b/sys/net80211/ieee80211_monitor.c new file mode 100644 index 0000000..b1e98eb --- /dev/null +++ b/sys/net80211/ieee80211_monitor.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 Monitor mode support. + */ +#include "opt_inet.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_llc.h> +#include <net/ethernet.h> + +#include <net/bpf.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_monitor.h> + +static void monitor_vattach(struct ieee80211vap *); +static int monitor_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int monitor_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp); + +void +ieee80211_monitor_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_MONITOR] = monitor_vattach; +} + +void +ieee80211_monitor_detach(struct ieee80211com *ic) +{ +} + +static void +monitor_vdetach(struct ieee80211vap *vap) +{ +} + +static void +monitor_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = monitor_newstate; + vap->iv_input = monitor_input; + vap->iv_opdetach = monitor_vdetach; +} + +/* + * IEEE80211_M_MONITOR vap state machine handler. + */ +static int +monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + enum ieee80211_state ostate; + + IEEE80211_LOCK_ASSERT(ic); + + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate], arg); + vap->iv_state = nstate; /* state transition */ + if (nstate == IEEE80211_S_RUN) { + switch (ostate) { + case IEEE80211_S_INIT: + ieee80211_create_ibss(vap, ic->ic_curchan); + break; + default: + break; + } + /* + * NB: this shouldn't be here but many people use + * monitor mode for raw packets; once we switch + * them over to adhoc demo mode remove this. + */ + ieee80211_node_authorize(vap->iv_bss); + } + return 0; +} + +/* + * Process a received frame in monitor mode. + */ +static int +monitor_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp) +{ + struct ieee80211vap *vap = ni->ni_vap; + + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + m_freem(m); + return -1; +} diff --git a/sys/net80211/ieee80211_monitor.h b/sys/net80211/ieee80211_monitor.h new file mode 100644 index 0000000..d7dd8e9 --- /dev/null +++ b/sys/net80211/ieee80211_monitor.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_MONITOR_H_ +#define _NET80211_IEEE80211_MONITOR_H_ + +/* + * Monitor implementation definitions. + */ +void ieee80211_monitor_attach(struct ieee80211com *); +void ieee80211_monitor_detach(struct ieee80211com *); +#endif /* !_NET80211_IEEE80211_MONITOR_H_ */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 0ed8b79..58cb33f 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,6 +27,8 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -40,18 +42,22 @@ __FBSDID("$FreeBSD$"); #include <net/ethernet.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_input.h> +#include <net80211/ieee80211_wds.h> #include <net/bpf.h> /* * 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))) +#define IEEE80211_AID_SET(_vap, b) \ + ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] |= \ + (1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_CLR(_vap, b) \ + ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] &= \ + ~(1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_ISSET(_vap, b) \ + ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32))) #ifdef IEEE80211_DEBUG_REFCNT #define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line @@ -64,117 +70,112 @@ static int ieee80211_sta_join1(struct ieee80211_node *); 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 void node_age(struct ieee80211_node *); static int8_t node_getrssi(const struct ieee80211_node *); static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); +static void node_getmimoinfo(const struct ieee80211_node *, + struct ieee80211_mimo_info *); -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_node_table_init(struct ieee80211com *ic, struct ieee80211_node_table *nt, const char *name, int inact, int keymaxix); -static void ieee80211_node_table_reset(struct ieee80211_node_table *); +static void ieee80211_node_table_reset(struct ieee80211_node_table *, + struct ieee80211vap *); +static void ieee80211_node_reclaim(struct ieee80211_node *); static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt); static void ieee80211_erp_timeout(struct ieee80211com *); MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); +MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie"); void ieee80211_node_attach(struct ieee80211com *ic) { + ieee80211_node_table_init(ic, &ic->ic_sta, "station", + IEEE80211_INACT_INIT, ic->ic_max_keyix); + callout_init(&ic->ic_inact, CALLOUT_MPSAFE); + callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, + ieee80211_node_timeout, ic); ic->ic_node_alloc = node_alloc; ic->ic_node_free = node_free; ic->ic_node_cleanup = node_cleanup; + ic->ic_node_age = node_age; + ic->ic_node_drain = node_age; /* NB: same as age */ ic->ic_node_getrssi = node_getrssi; ic->ic_node_getsignal = node_getsignal; + ic->ic_node_getmimoinfo = node_getmimoinfo; - /* 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; - - callout_init(&ic->ic_inact, CALLOUT_MPSAFE); - - /* NB: driver should override */ - ic->ic_max_aid = IEEE80211_AID_DEF; - + /* + * Set flags to be propagated to all vap's; + * these define default behaviour/configuration. + */ ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */ } void -ieee80211_node_lateattach(struct ieee80211com *ic) +ieee80211_node_detach(struct ieee80211com *ic) { - struct ieee80211_rsnparms *rsn; - if (ic->ic_max_aid > IEEE80211_AID_MAX) - ic->ic_max_aid = IEEE80211_AID_MAX; - MALLOC(ic->ic_aid_bitmap, uint32_t *, - howmany(ic->ic_max_aid, 32) * sizeof(uint32_t), - M_80211_NODE, M_NOWAIT | M_ZERO); - if (ic->ic_aid_bitmap == NULL) { - /* XXX no way to recover */ - printf("%s: no memory for AID bitmap!\n", __func__); - ic->ic_max_aid = 0; - } + callout_drain(&ic->ic_inact); + ieee80211_node_table_cleanup(&ic->ic_sta); +} - ieee80211_node_table_init(ic, &ic->ic_sta, "station", - IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix); +void +ieee80211_node_vattach(struct ieee80211vap *vap) +{ + /* NB: driver can override */ + vap->iv_max_aid = IEEE80211_AID_DEF; - 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<<IEEE80211_CIPHER_WEP; - rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP; - rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM; - if (ic->ic_caps & IEEE80211_C_AES) - rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB; - if (ic->ic_caps & IEEE80211_C_CKIP) - rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP; - /* - * Default unicast cipher to WEP for 802.1x use. If - * WPA is enabled the management code will set these - * values to reflect. - */ - rsn->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; + /* default station inactivity timer setings */ + vap->iv_inact_init = IEEE80211_INACT_INIT; + vap->iv_inact_auth = IEEE80211_INACT_AUTH; + vap->iv_inact_run = IEEE80211_INACT_RUN; + vap->iv_inact_probe = IEEE80211_INACT_PROBE; +} - /* - * 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; +void +ieee80211_node_latevattach(struct ieee80211vap *vap) +{ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* XXX should we allow max aid to be zero? */ + if (vap->iv_max_aid < IEEE80211_AID_MIN) { + vap->iv_max_aid = IEEE80211_AID_MIN; + if_printf(vap->iv_ifp, + "WARNING: max aid too small, changed to %d\n", + vap->iv_max_aid); + } + MALLOC(vap->iv_aid_bitmap, uint32_t *, + howmany(vap->iv_max_aid, 32) * sizeof(uint32_t), + M_80211_NODE, M_NOWAIT | M_ZERO); + if (vap->iv_aid_bitmap == NULL) { + /* XXX no way to recover */ + printf("%s: no memory for AID bitmap, max aid %d!\n", + __func__, vap->iv_max_aid); + vap->iv_max_aid = 0; + } + } + + ieee80211_reset_bss(vap); - ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode); + vap->iv_auth = ieee80211_authenticator_get(vap->iv_bss->ni_authmode); } void -ieee80211_node_detach(struct ieee80211com *ic) +ieee80211_node_vdetach(struct ieee80211vap *vap) { + struct ieee80211com *ic = vap->iv_ic; - if (ic->ic_bss != NULL) { - ieee80211_free_node(ic->ic_bss); - ic->ic_bss = NULL; + ieee80211_node_table_reset(&ic->ic_sta, vap); + if (vap->iv_bss != NULL) { + ieee80211_free_node(vap->iv_bss); + vap->iv_bss = NULL; } - ieee80211_node_table_cleanup(&ic->ic_sta); - if (ic->ic_aid_bitmap != NULL) { - FREE(ic->ic_aid_bitmap, M_80211_NODE); - ic->ic_aid_bitmap = NULL; + if (vap->iv_aid_bitmap != NULL) { + FREE(vap->iv_aid_bitmap, M_80211_NODE); + vap->iv_aid_bitmap = NULL; } } @@ -185,20 +186,16 @@ ieee80211_node_detach(struct ieee80211com *ic) 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; + ni->ni_inact_reload = ni->ni_vap->iv_inact_run; ni->ni_inact = ni->ni_inact_reload; } void ieee80211_node_unauthorize(struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; - ni->ni_flags &= ~IEEE80211_NODE_AUTH; - ni->ni_inact_reload = ic->ic_inact_auth; + ni->ni_inact_reload = ni->ni_vap->iv_inact_auth; if (ni->ni_inact > ni->ni_inact_reload) ni->ni_inact = ni->ni_inact_reload; } @@ -206,18 +203,16 @@ ieee80211_node_unauthorize(struct ieee80211_node *ni) /* * Set/change the channel. The rate set is also updated as * to insure a consistent view by drivers. + * XXX should be private but hostap needs it to deal with CSA */ -static void -ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni) +void +ieee80211_node_set_chan(struct ieee80211_node *ni, + struct ieee80211_channel *chan) { - struct ieee80211_channel *chan = ic->ic_bsschan; + struct ieee80211com *ic = ni->ni_ic; + + KASSERT(chan != IEEE80211_CHAN_ANYC, ("no channel")); -#if 0 - KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup")); -#else - if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */ - chan = ic->ic_curchan; -#endif ni->ni_chan = chan; if (IEEE80211_IS_CHAN_HT(chan)) { /* @@ -232,31 +227,6 @@ ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni) ni->ni_rates = *ieee80211_get_suprates(ic, chan); } -/* - * 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_ssid[0].ssid, ic->ic_des_ssid[0].len, - 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) { @@ -264,90 +234,87 @@ copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss) 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? */ + /* XXX legacy WDS bssid? */ } void -ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) +ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan) { - struct ieee80211_node_table *nt; + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: creating ibss\n", __func__); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: creating ibss on channel %u\n", __func__, + ieee80211_chan2ieee(ic, chan)); - /* - * 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; - IEEE80211_NODE_LOCK(nt); - 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; - } - IEEE80211_NODE_UNLOCK(nt); - - ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr); + ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); if (ni == NULL) { /* XXX recovery? */ return; } - IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); - ni->ni_esslen = ic->ic_des_ssid[0].len; - memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen); - if (ic->ic_bss != NULL) - copy_bss(ni, ic->ic_bss); + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); + ni->ni_esslen = vap->iv_des_ssid[0].len; + memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); + if (vap->iv_bss != NULL) + copy_bss(ni, vap->iv_bss); ni->ni_intval = ic->ic_bintval; - if (ic->ic_flags & IEEE80211_F_PRIVACY) + if (vap->iv_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; } - if (ic->ic_opmode == IEEE80211_M_IBSS) { - ic->ic_flags |= IEEE80211_F_SIBSS; + if (vap->iv_opmode == IEEE80211_M_IBSS) { + vap->iv_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); + if (vap->iv_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid); else { get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN); /* clear group bit, add local bit */ ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02; } - } 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 if (vap->iv_opmode == IEEE80211_M_AHDEMO) { + if (vap->iv_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid); else memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN); } /* * Fix the channel and related attributes. */ + /* clear DFS CAC state on previous channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + ic->ic_bsschan->ic_freq != chan->ic_freq && + IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) + ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); ic->ic_bsschan = chan; - ieee80211_node_set_chan(ic, ni); + ieee80211_node_set_chan(ni, chan); ic->ic_curmode = ieee80211_chan2mode(chan); /* - * Do mode-specific rate setup. + * Do mode-specific setup. */ if (IEEE80211_IS_CHAN_FULL(chan)) { if (IEEE80211_IS_CHAN_ANYG(chan)) { /* - * Use a mixed 11b/11g rate set. + * Use a mixed 11b/11g basic rate set. */ - ieee80211_set11gbasicrates(&ni->ni_rates, - IEEE80211_MODE_11G); + ieee80211_setbasicrates(&ni->ni_rates, + IEEE80211_MODE_11G); + if (vap->iv_flags & IEEE80211_F_PUREG) { + /* + * Also mark OFDM rates basic so 11b + * stations do not join (WiFi compliance). + */ + ieee80211_addbasicrates(&ni->ni_rates, + IEEE80211_MODE_11A); + } } else if (IEEE80211_IS_CHAN_B(chan)) { /* * Force pure 11b rate set. */ - ieee80211_set11gbasicrates(&ni->ni_rates, + ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11B); } } @@ -362,23 +329,25 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) * etc. state). */ void -ieee80211_reset_bss(struct ieee80211com *ic) +ieee80211_reset_bss(struct ieee80211vap *vap) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni, *obss; - callout_drain(&ic->ic_inact); - ieee80211_node_table_reset(&ic->ic_sta); + ieee80211_node_table_reset(&ic->ic_sta, vap); + /* XXX multi-bss: wrong */ ieee80211_reset_erp(ic); - ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr); + ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); KASSERT(ni != NULL, ("unable to setup inital BSS node")); - obss = ic->ic_bss; - ic->ic_bss = ieee80211_ref_node(ni); + obss = vap->iv_bss; + vap->iv_bss = ieee80211_ref_node(ni); if (obss != NULL) { copy_bss(ni, obss); ni->ni_intval = ic->ic_bintval; ieee80211_free_node(obss); - } + } else + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); } static int @@ -399,20 +368,21 @@ match_ssid(const struct ieee80211_node *ni, * Test a node for suitability/compatibility. */ static int -check_bss(struct ieee80211com *ic, struct ieee80211_node *ni) +check_bss(struct ieee80211vap *vap, struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; uint8_t rate; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) return 0; - if (ic->ic_opmode == IEEE80211_M_IBSS) { + if (vap->iv_opmode == IEEE80211_M_IBSS) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) return 0; } else { if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) return 0; } - if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_flags & IEEE80211_F_PRIVACY) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) return 0; } else { @@ -424,11 +394,11 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni) IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); if (rate & IEEE80211_RATE_BASIC) return 0; - if (ic->ic_des_nssid != 0 && - !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid)) + if (vap->iv_des_nssid != 0 && + !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid)) return 0; - if ((ic->ic_flags & IEEE80211_F_DESBSSID) && - !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) + if ((vap->iv_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid)) return 0; return 1; } @@ -438,22 +408,23 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni) * Display node suitability/compatibility. */ static void -check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni) +check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; uint8_t rate; int fail; fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) fail |= 0x01; - if (ic->ic_opmode == IEEE80211_M_IBSS) { + if (vap->iv_opmode == IEEE80211_M_IBSS) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) fail |= 0x02; } else { if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } - if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_flags & IEEE80211_F_PRIVACY) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { @@ -465,18 +436,17 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni) IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; - if (ic->ic_des_nssid != 0 && - !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid)) + if (vap->iv_des_nssid != 0 && + !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid)) fail |= 0x10; - if ((ic->ic_flags & IEEE80211_F_DESBSSID) && - !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) + if ((vap->iv_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid)) fail |= 0x20; printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr)); printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' '); printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); - printf(" %+4d", ni->ni_rssi); printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, fail & 0x08 ? '!' : ' '); printf(" %4s%c", @@ -507,25 +477,28 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni) int ieee80211_ibss_merge(struct ieee80211_node *ni) { + struct ieee80211vap *vap = ni->ni_vap; +#ifdef IEEE80211_DEBUG struct ieee80211com *ic = ni->ni_ic; +#endif - if (ni == ic->ic_bss || - IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) { + if (ni == vap->iv_bss || + IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) { /* unchanged, nothing to do */ return 0; } - if (!check_bss(ic, ni)) { + if (!check_bss(vap, ni)) { /* capabilities mismatch */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: merge failed, capabilities mismatch\n", __func__); #ifdef IEEE80211_DEBUG - if (ieee80211_msg_assoc(ic)) - check_bss_debug(ic, ni); + if (ieee80211_msg_assoc(vap)) + check_bss_debug(vap, ni); #endif - ic->ic_stats.is_ibss_capmismatch++; + vap->iv_stats.is_ibss_capmismatch++; return 0; } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: new bssid %s: %s preamble, %s slot time%s\n", __func__, ether_sprintf(ni->ni_bssid), ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", @@ -536,13 +509,72 @@ ieee80211_ibss_merge(struct ieee80211_node *ni) } /* - * Change the bss channel. + * Calculate HT channel promotion flags for all vaps. + * This assumes ni_chan have been setup for each vap. + */ +static int +gethtadjustflags(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + int flags; + + flags = 0; + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_state < IEEE80211_S_RUN) + continue; + switch (vap->iv_opmode) { + case IEEE80211_M_WDS: + case IEEE80211_M_STA: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_IBSS: + flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan); + break; + default: + break; + } + } + return flags; +} + +/* + * Check if the current channel needs to change based on whether + * any vap's are using HT20/HT40. This is used sync the state of + * ic_curchan after a channel width change on a running vap. */ void -ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c) +ieee80211_sync_curchan(struct ieee80211com *ic) { - ic->ic_bsschan = c; - ic->ic_curchan = ic->ic_bsschan; + struct ieee80211_channel *c; + + c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic)); + if (c != ic->ic_curchan) { + ic->ic_curchan = c; + ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); + ic->ic_set_channel(ic); + } +} + +/* + * Change the current channel. The request channel may be + * promoted if other vap's are operating with HT20/HT40. + */ +void +ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (ic->ic_htcaps & IEEE80211_HTC_HT) { + int flags = gethtadjustflags(ic); + /* + * Check for channel promotion required to support the + * set of running vap's. This assumes we are called + * after ni_chan is setup for each vap. + */ + /* NB: this assumes IEEE80211_FEXT_USEHT40 > IEEE80211_FEXT_HT */ + if (flags > ieee80211_htchanflags(c)) + c = ieee80211_ht_adjust_channel(ic, c, flags); + } + ic->ic_bsschan = ic->ic_curchan = c; ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); ic->ic_set_channel(ic); } @@ -554,61 +586,49 @@ ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c) static int ieee80211_sta_join1(struct ieee80211_node *selbs) { + struct ieee80211vap *vap = selbs->ni_vap; struct ieee80211com *ic = selbs->ni_ic; struct ieee80211_node *obss; int canreassoc; - if (ic->ic_opmode == IEEE80211_M_IBSS) { - struct ieee80211_node_table *nt; - /* - * 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; - IEEE80211_NODE_LOCK(nt); - nt->nt_name = "neighbor"; - nt->nt_inact_init = ic->ic_inact_run; - IEEE80211_NODE_UNLOCK(nt); - } - /* * Committed to selbs, setup state. */ - obss = ic->ic_bss; + obss = vap->iv_bss; /* * Check if old+new node have the same address in which * case we can reassociate when operating in sta mode. */ canreassoc = (obss != NULL && - ic->ic_state == IEEE80211_S_RUN && + vap->iv_state == IEEE80211_S_RUN && IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr)); - ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */ + vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */ if (obss != NULL) { copy_bss(selbs, obss); - ieee80211_free_node(obss); + ieee80211_node_reclaim(obss); + obss = NULL; /* NB: guard against later use */ } /* * Delete unusable rates; we've already checked * that the negotiated rate set is acceptable. */ - ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates, + ieee80211_fix_rate(vap->iv_bss, &vap->iv_bss->ni_rates, IEEE80211_F_DODEL | IEEE80211_F_JOIN); - ieee80211_setbsschan(ic, selbs->ni_chan); + ieee80211_setcurchan(ic, selbs->ni_chan); /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ ieee80211_reset_erp(ic); - ieee80211_wme_initparams(ic); + ieee80211_wme_initparams(vap); - if (ic->ic_opmode == IEEE80211_M_STA) { + if (vap->iv_opmode == IEEE80211_M_STA) { if (canreassoc) { /* Reassociate */ - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); } else { /* * Act as if we received a DEAUTH frame in case we @@ -616,21 +636,22 @@ ieee80211_sta_join1(struct ieee80211_node *selbs) * us to try to re-authenticate if we are operating * as a station. */ - ieee80211_new_state(ic, IEEE80211_S_AUTH, + ieee80211_new_state(vap, IEEE80211_S_AUTH, IEEE80211_FC0_SUBTYPE_DEAUTH); } } else - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + ieee80211_new_state(vap, IEEE80211_S_RUN, -1); return 1; } int -ieee80211_sta_join(struct ieee80211com *ic, +ieee80211_sta_join(struct ieee80211vap *vap, const struct ieee80211_scan_entry *se) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; - ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr); + ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr); if (ni == NULL) { /* XXX msg */ return 0; @@ -651,23 +672,21 @@ ieee80211_sta_join(struct ieee80211com *ic, ni->ni_fhdwell = se->se_fhdwell; ni->ni_fhindex = se->se_fhindex; ni->ni_erp = se->se_erp; - ni->ni_rssi = se->se_rssi; + IEEE80211_RSSI_LPF(ni->ni_avgrssi, se->se_rssi); ni->ni_noise = se->se_noise; - if (se->se_htcap_ie != NULL) { - ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie); - ieee80211_parse_htcap(ni, ni->ni_htcap_ie); + + if (ieee80211_ies_init(&ni->ni_ies, se->se_ies.data, se->se_ies.len)) { + ieee80211_ies_expand(&ni->ni_ies); + if (ni->ni_ies.ath_ie != NULL) + ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); + if (ni->ni_ies.htcap_ie != NULL) + ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie); + if (ni->ni_ies.htinfo_ie != NULL) + ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie); } - if (se->se_wpa_ie != NULL) - ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie); - if (se->se_rsn_ie != NULL) - ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie); - if (se->se_wme_ie != NULL) - ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie); - if (se->se_ath_ie != NULL) - ieee80211_saveath(ni, se->se_ath_ie); - - ic->ic_dtim_period = se->se_dtimperiod; - ic->ic_dtim_count = 0; + + vap->iv_dtim_period = se->se_dtimperiod; + vap->iv_dtim_count = 0; /* NB: must be after ni_chan is setup */ ieee80211_setup_rates(ni, se->se_rates, se->se_xrates, @@ -681,10 +700,26 @@ ieee80211_sta_join(struct ieee80211com *ic, * be passed in with a held reference. */ void -ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_sta_leave(struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; + ic->ic_node_cleanup(ni); - ieee80211_notify_node_leave(ic, ni); + ieee80211_notify_node_leave(ni); +} + +/* + * Send a deauthenticate frame and drop the station. + */ +void +ieee80211_node_deauth(struct ieee80211_node *ni, int reason) +{ + /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */ + ieee80211_ref_node(ni); + if (ni->ni_associd != 0) + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); + ieee80211_node_leave(ni); + ieee80211_free_node(ni); } static struct ieee80211_node * @@ -698,6 +733,80 @@ node_alloc(struct ieee80211_node_table *nt) } /* + * Initialize an ie blob with the specified data. If previous + * data exists re-use the data block. As a side effect we clear + * all references to specific ie's; the caller is required to + * recalculate them. + */ +int +ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len) +{ + /* NB: assumes data+len are the last fields */ + memset(ies, 0, offsetof(struct ieee80211_ies, data)); + if (ies->data != NULL && ies->len != len) { + /* data size changed */ + FREE(ies->data, M_80211_NODE_IE); + ies->data = NULL; + } + if (ies->data == NULL) { + MALLOC(ies->data, uint8_t *, len, M_80211_NODE_IE, M_NOWAIT); + if (ies->data == NULL) { + ies->len = 0; + /* NB: pointers have already been zero'd above */ + return 0; + } + } + memcpy(ies->data, data, len); + ies->len = len; + return 1; +} + +/* + * Reclaim storage for an ie blob. + */ +void +ieee80211_ies_cleanup(struct ieee80211_ies *ies) +{ + if (ies->data != NULL) + FREE(ies->data, M_80211_NODE_IE); +} + +/* + * Expand an ie blob data contents and to fillin individual + * ie pointers. The data blob is assumed to be well-formed; + * we don't do any validity checking of ie lengths. + */ +void +ieee80211_ies_expand(struct ieee80211_ies *ies) +{ + uint8_t *ie; + int ielen; + + ie = ies->data; + ielen = ies->len; + while (ielen > 0) { + switch (ie[0]) { + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(ie)) + ies->wpa_ie = ie; + else if (iswmeoui(ie)) + ies->wme_ie = ie; + else if (isatherosoui(ie)) + ies->ath_ie = ie; + break; + case IEEE80211_ELEMID_RSN: + ies->rsn_ie = ie; + break; + case IEEE80211_ELEMID_HTCAP: + ies->htcap_ie = ie; + break; + } + ielen -= 2 + ie[1]; + ie += 2 + ie[1]; + } +} + +/* * 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 @@ -707,17 +816,16 @@ static void node_cleanup(struct ieee80211_node *ni) { #define N(a) (sizeof(a)/sizeof(a[0])) - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; int i; /* NB: preserve ni_table */ if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { - if (ic->ic_opmode != IEEE80211_M_STA) - ic->ic_ps_sta--; + if (vap->iv_opmode != IEEE80211_M_STA) + vap->iv_ps_sta--; ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] power save mode off, %u sta's in ps mode\n", - ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "power save mode off, %u sta's in ps mode", vap->iv_ps_sta); } /* * Cleanup any HT-related state. @@ -735,8 +843,8 @@ node_cleanup(struct ieee80211_node *ni) /* * Drain power save queue and, if needed, clear TIM. */ - if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); + if (ieee80211_node_saveq_drain(ni) != 0 && vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); ni->ni_associd = 0; if (ni->ni_challenge != NULL) { @@ -773,39 +881,80 @@ node_free(struct ieee80211_node *ni) struct ieee80211com *ic = ni->ni_ic; ic->ic_node_cleanup(ni); - if (ni->ni_wpa_ie != NULL) - FREE(ni->ni_wpa_ie, M_80211_NODE); - if (ni->ni_rsn_ie != NULL) - FREE(ni->ni_rsn_ie, M_80211_NODE); - if (ni->ni_wme_ie != NULL) - FREE(ni->ni_wme_ie, M_80211_NODE); - if (ni->ni_ath_ie != NULL) - FREE(ni->ni_ath_ie, M_80211_NODE); + ieee80211_ies_cleanup(&ni->ni_ies); IEEE80211_NODE_SAVEQ_DESTROY(ni); + IEEE80211_NODE_WDSQ_DESTROY(ni); FREE(ni, M_80211_NODE); } +static void +node_age(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; +#if 0 + IEEE80211_NODE_LOCK_ASSERT(&ic->ic_sta); +#endif + /* + * Age frames on the power save queue. + */ + if (ieee80211_node_saveq_age(ni) != 0 && + IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 && + vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); + /* + * Age frames on the wds pending queue. + */ + if (IEEE80211_NODE_WDSQ_QLEN(ni) != 0) + ieee80211_node_wdsq_age(ni); + /* + * Age out HT resources (e.g. frames on the + * A-MPDU reorder queues). + */ + if (ni->ni_associd != 0 && (ni->ni_flags & IEEE80211_NODE_HT)) + ieee80211_ht_node_age(ni); +} + static int8_t node_getrssi(const struct ieee80211_node *ni) { - return ni->ni_rssi; + uint32_t avgrssi = ni->ni_avgrssi; + int32_t rssi; + + if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER) + return 0; + rssi = IEEE80211_RSSI_GET(avgrssi); + return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi; } static void node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) { - *rssi = ni->ni_rssi; + *rssi = node_getrssi(ni); *noise = ni->ni_noise; } static void -ieee80211_setup_node(struct ieee80211_node_table *nt, - struct ieee80211_node *ni, const uint8_t *macaddr) +node_getmimoinfo(const struct ieee80211_node *ni, + struct ieee80211_mimo_info *info) +{ + /* XXX zero data? */ +} + +struct ieee80211_node * +ieee80211_alloc_node(struct ieee80211_node_table *nt, + struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN]) { struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; int hash; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + ni = ic->ic_node_alloc(nt); + if (ni == NULL) { + vap->iv_stats.is_rx_nodealloc++; + return NULL; + } + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s %p<%s> in %s table\n", __func__, ni, ether_sprintf(macaddr), nt->nt_name); @@ -815,31 +964,22 @@ ieee80211_setup_node(struct ieee80211_node_table *nt, 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); + ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); + ni->ni_avgrssi = IEEE80211_RSSI_DUMMY_MARKER; ni->ni_inact_reload = nt->nt_inact_init; ni->ni_inact = ni->ni_inact_reload; ni->ni_ath_defkeyix = 0x7fff; IEEE80211_NODE_SAVEQ_INIT(ni, "unknown"); + IEEE80211_NODE_WDSQ_INIT(ni, "unknown"); IEEE80211_NODE_LOCK(nt); 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_vap = vap; ni->ni_ic = ic; IEEE80211_NODE_UNLOCK(nt); -} - -struct ieee80211_node * -ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) -{ - struct ieee80211com *ic = nt->nt_ic; - struct ieee80211_node *ni; - ni = ic->ic_node_alloc(nt); - if (ni != NULL) - ieee80211_setup_node(nt, ni, macaddr); - else - ic->ic_stats.is_rx_nodealloc++; return ni; } @@ -850,65 +990,129 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) * once the send completes. */ struct ieee80211_node * -ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr) +ieee80211_tmp_node(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; ni = ic->ic_node_alloc(&ic->ic_sta); if (ni != NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s %p<%s>\n", __func__, ni, ether_sprintf(macaddr)); + ni->ni_table = NULL; /* NB: pedantic */ + ni->ni_ic = ic; /* NB: needed to set channel */ + ni->ni_vap = vap; + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); - IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid); ieee80211_node_initref(ni); /* mark referenced */ - ni->ni_txpower = ic->ic_bss->ni_txpower; /* NB: required by ieee80211_fix_rate */ - ieee80211_node_set_chan(ic, ni); - ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, + ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan); + ni->ni_txpower = vap->iv_bss->ni_txpower; + ieee80211_crypto_resetkey(vap, &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; + IEEE80211_NODE_WDSQ_INIT(ni, "unknown"); } else { /* XXX msg */ - ic->ic_stats.is_rx_nodealloc++; + vap->iv_stats.is_rx_nodealloc++; } return ni; } struct ieee80211_node * -ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr) +ieee80211_dup_bss(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) { - struct ieee80211com *ic = nt->nt_ic; + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; - ni = ic->ic_node_alloc(nt); + ni = ieee80211_alloc_node(&ic->ic_sta, vap, macaddr); if (ni != NULL) { - ieee80211_setup_node(nt, ni, macaddr); /* - * Inherit from ic_bss. + * Inherit from iv_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); - ieee80211_node_set_chan(ic, ni); - ni->ni_rsn = ic->ic_bss->ni_rsn; - } else - ic->ic_stats.is_rx_nodealloc++; + ni->ni_authmode = vap->iv_bss->ni_authmode; + ni->ni_txpower = vap->iv_bss->ni_txpower; + ni->ni_vlan = vap->iv_bss->ni_vlan; /* XXX?? */ + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid); + ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan); + } return ni; } -static struct ieee80211_node * +/* + * Create a bss node for a legacy WDS vap. The far end does + * not associate so we just create create a new node and + * simulate an association. The caller is responsible for + * installing the node as the bss node and handling any further + * setup work like authorizing the port. + */ +struct ieee80211_node * +ieee80211_node_create_wds(struct ieee80211vap *vap, + const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + + /* XXX check if node already in sta table? */ + ni = ieee80211_alloc_node(&ic->ic_sta, vap, bssid); + if (ni != NULL) { + ni->ni_wdsvap = vap; + IEEE80211_ADDR_COPY(ni->ni_bssid, bssid); + /* + * Inherit any manually configured settings. + */ + ni->ni_authmode = vap->iv_bss->ni_authmode; + ni->ni_txpower = vap->iv_bss->ni_txpower; + ni->ni_vlan = vap->iv_bss->ni_vlan; + ieee80211_node_set_chan(ni, chan); + /* NB: propagate ssid so available to WPA supplicant */ + ni->ni_esslen = vap->iv_des_ssid[0].len; + memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); + /* NB: no associd for peer */ + /* + * There are no management frames to use to + * discover neighbor capabilities, so blindly + * propagate the local configuration. + */ + if (vap->iv_flags & IEEE80211_F_WME) + ni->ni_flags |= IEEE80211_NODE_QOS; + if (vap->iv_flags & IEEE80211_F_FF) + ni->ni_flags |= IEEE80211_NODE_FF; + if ((ic->ic_htcaps & IEEE80211_HTC_HT) && + (vap->iv_flags_ext & IEEE80211_FEXT_HT)) { + /* + * Device is HT-capable and HT is enabled for + * the vap; setup HT operation. On return + * ni_chan will be adjusted to an HT channel. + */ + ieee80211_ht_wds_init(ni); + } else { + struct ieee80211_channel *c = ni->ni_chan; + /* + * Force a legacy channel to be used. + */ + c = ieee80211_find_channel(ic, + c->ic_freq, c->ic_flags &~ IEEE80211_CHAN_HT); + KASSERT(c != NULL, ("no legacy channel, %u/%x", + ni->ni_chan->ic_freq, ni->ni_chan->ic_flags)); + ni->ni_chan = c; + } + } + return ni; +} + +struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT -_ieee80211_find_node_debug(struct ieee80211_node_table *nt, - const uint8_t *macaddr, const char *func, int line) +ieee80211_find_node_locked_debug(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) #else -_ieee80211_find_node(struct ieee80211_node_table *nt, - const uint8_t *macaddr) +ieee80211_find_node_locked(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) #endif { struct ieee80211_node *ni; @@ -921,7 +1125,7 @@ _ieee80211_find_node(struct ieee80211_node_table *nt, if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { ieee80211_ref_node(ni); /* mark referenced */ #ifdef IEEE80211_DEBUG_REFCNT - IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni, ether_sprintf(ni->ni_macaddr), @@ -932,23 +1136,73 @@ _ieee80211_find_node(struct ieee80211_node_table *nt, } return NULL; } + +struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT -#define _ieee80211_find_node(nt, mac) \ - _ieee80211_find_node_debug(nt, mac, func, line) +ieee80211_find_node_debug(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) +#else +ieee80211_find_node(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) #endif +{ + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_node_locked(nt, macaddr); + IEEE80211_NODE_UNLOCK(nt); + return ni; +} struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT -ieee80211_find_node_debug(struct ieee80211_node_table *nt, - const uint8_t *macaddr, const char *func, int line) +ieee80211_find_vap_node_locked_debug(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) #else -ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) +ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +#endif +{ + struct ieee80211_node *ni; + int hash; + + IEEE80211_NODE_LOCK_ASSERT(nt); + + hash = IEEE80211_NODE_HASH(macaddr); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (ni->ni_vap == vap && + IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { + ieee80211_ref_node(ni); /* mark referenced */ +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%s> refcnt %d\n", __func__, + func, line, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); +#endif + return ni; + } + } + return NULL; +} + +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_vap_node_debug(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) +#else +ieee80211_find_vap_node(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) #endif { struct ieee80211_node *ni; IEEE80211_NODE_LOCK(nt); - ni = _ieee80211_find_node(nt, macaddr); + ni = ieee80211_find_vap_node_locked(nt, vap, macaddr); IEEE80211_NODE_UNLOCK(nt); return ni; } @@ -960,21 +1214,20 @@ ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr) * it's private state. */ struct ieee80211_node * -ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt, +ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN]) { - struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; - IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s: mac<%s>\n", __func__, ether_sprintf(macaddr)); - ni = ieee80211_dup_bss(nt, macaddr); + ni = ieee80211_dup_bss(vap, macaddr); if (ni != NULL) { + struct ieee80211com *ic = vap->iv_ic; + /* XXX no rate negotiation; just dup */ - ni->ni_rates = ic->ic_bss->ni_rates; - if (ic->ic_newassoc != NULL) - ic->ic_newassoc(ni, 1); - if (ic->ic_opmode == IEEE80211_M_AHDEMO) { + ni->ni_rates = vap->iv_bss->ni_rates; + if (vap->iv_opmode == IEEE80211_M_AHDEMO) { /* * In adhoc demo mode there are no management * frames to use to discover neighbor capabilities, @@ -982,11 +1235,13 @@ ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt, * so we can do interesting things (e.g. use * WME to disable ACK's). */ - if (ic->ic_flags & IEEE80211_F_WME) + if (vap->iv_flags & IEEE80211_F_WME) ni->ni_flags |= IEEE80211_NODE_QOS; - if (ic->ic_flags & IEEE80211_F_FF) + if (vap->iv_flags & IEEE80211_F_FF) ni->ni_flags |= IEEE80211_NODE_FF; } + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, 1); /* XXX not right for 802.1x/WPA */ ieee80211_node_authorize(ni); } @@ -1009,14 +1264,12 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, 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); - if (sp->rsn != NULL) - ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn); - if (sp->ath != NULL) - ieee80211_saveath(ni, sp->ath); + + if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) { + ieee80211_ies_expand(&ni->ni_ies); + if (ni->ni_ies.ath_ie != NULL) + ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); + } /* NB: must be after ni_chan is setup */ ieee80211_setup_rates(ni, sp->rates, sp->xrates, @@ -1031,16 +1284,18 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, * driver has an opportunity to setup it's private state. */ struct ieee80211_node * -ieee80211_add_neighbor(struct ieee80211com *ic, +ieee80211_add_neighbor(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const struct ieee80211_scanparams *sp) { struct ieee80211_node *ni; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2)); - ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);/* XXX alloc_node? */ + ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */ if (ni != NULL) { + struct ieee80211com *ic = vap->iv_ic; + ieee80211_init_neighbor(ni, wh, sp); if (ic->ic_newassoc != NULL) ic->ic_newassoc(ni, 1); @@ -1077,16 +1332,13 @@ ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211_node_table *nt; struct ieee80211_node *ni; - /* XXX check ic_bss first in station mode */ /* XXX 4-address frames? */ nt = &ic->ic_sta; IEEE80211_NODE_LOCK(nt); if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/) - ni = _ieee80211_find_node(nt, wh->i_addr1); + ni = ieee80211_find_node_locked(nt, wh->i_addr1); else - ni = _ieee80211_find_node(nt, wh->i_addr2); - if (ni == NULL) - ni = ieee80211_ref_node(ic->ic_bss); + ni = ieee80211_find_node_locked(nt, wh->i_addr2); IEEE80211_NODE_UNLOCK(nt); return ni; @@ -1121,12 +1373,10 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic, ni = NULL; if (ni == NULL) { if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/) - ni = _ieee80211_find_node(nt, wh->i_addr1); + ni = ieee80211_find_node_locked(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) { + ni = ieee80211_find_node_locked(nt, wh->i_addr2); + if (ni != NULL && nt->nt_keyixmap != NULL) { /* * If the station has a unicast key cache slot * assigned update the key->node mapping table. @@ -1135,7 +1385,8 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic, /* XXX can keyixmap[keyix] != NULL? */ if (keyix < nt->nt_keyixmax && nt->nt_keyixmap[keyix] == NULL) { - IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(ni->ni_vap, + IEEE80211_MSG_NODE, "%s: add key map entry %p<%s> refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); @@ -1158,13 +1409,15 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic, */ struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT -ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr, +ieee80211_find_txnode_debug(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) #else -ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr) +ieee80211_find_txnode(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) #endif { - struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta; struct ieee80211_node *ni; /* @@ -1175,11 +1428,13 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr) */ /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ IEEE80211_NODE_LOCK(nt); - if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) - ni = ieee80211_ref_node(ic->ic_bss); + if (vap->iv_opmode == IEEE80211_M_STA || + vap->iv_opmode == IEEE80211_M_WDS || + IEEE80211_IS_MULTICAST(macaddr)) + ni = ieee80211_ref_node(vap->iv_bss); else { - ni = _ieee80211_find_node(nt, macaddr); - if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ni = ieee80211_find_node_locked(nt, macaddr); + if (vap->iv_opmode == IEEE80211_M_HOSTAP && (ni != NULL && ni->ni_associd == 0)) { /* * Station is not associated; don't permit the @@ -1193,91 +1448,44 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr) IEEE80211_NODE_UNLOCK(nt); if (ni == NULL) { - if (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO) { + if (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_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. + * caller to be consistent with + * ieee80211_find_node_locked. */ - ni = ieee80211_fakeup_adhoc_node(nt, macaddr); + ni = ieee80211_fakeup_adhoc_node(vap, macaddr); if (ni != NULL) (void) ieee80211_ref_node(ni); } else { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, - "[%s] no node, discard frame (%s)\n", - ether_sprintf(macaddr), __func__); - ic->ic_stats.is_tx_nonode++; - } - } - 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 ieee80211_node *ni; - int hash; - - IEEE80211_NODE_LOCK(nt); - /* - * 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; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, macaddr, + "no node, discard frame (%s)", __func__); + vap->iv_stats.is_tx_nonode++; } } - if (ni != NULL) { - ieee80211_ref_node(ni); /* mark referenced */ - IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, - REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr), - ieee80211_node_refcnt(ni)); - } - IEEE80211_NODE_UNLOCK(nt); return ni; -#undef MATCH_SSID } static void _ieee80211_free_node(struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_node_table *nt = ni->ni_table; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s %p<%s> in %s table\n", __func__, ni, ether_sprintf(ni->ni_macaddr), nt != NULL ? nt->nt_name : "<gone>"); - IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + if (vap->iv_aid_bitmap != NULL) + IEEE80211_AID_CLR(vap, ni->ni_associd); if (nt != NULL) { TAILQ_REMOVE(&nt->nt_node, ni, ni_list); LIST_REMOVE(ni, ni_hash); } - ic->ic_node_free(ni); + vap->iv_ic->ic_node_free(ni); } void @@ -1290,7 +1498,7 @@ ieee80211_free_node(struct ieee80211_node *ni) struct ieee80211_node_table *nt = ni->ni_table; #ifdef IEEE80211_DEBUG_REFCNT - IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1); #endif @@ -1310,7 +1518,8 @@ ieee80211_free_node(struct ieee80211_node *ni) keyix = ni->ni_ucastkey.wk_rxkeyix; if (keyix < nt->nt_keyixmax && nt->nt_keyixmap[keyix] == ni) { - IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(ni->ni_vap, + IEEE80211_MSG_NODE, "%s: %p<%s> clear key map entry", __func__, ni, ether_sprintf(ni->ni_macaddr)); nt->nt_keyixmap[keyix] = NULL; @@ -1331,8 +1540,9 @@ ieee80211_free_node(struct ieee80211_node *ni) int ieee80211_node_delucastkey(struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; - struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211vap *vap = ni->ni_vap; + /* XXX is ni_table safe? */ + struct ieee80211_node_table *nt = &ni->ni_ic->ic_sta; struct ieee80211_node *nikey; ieee80211_keyix keyix; int isowned, status; @@ -1353,19 +1563,19 @@ ieee80211_node_delucastkey(struct ieee80211_node *ni) if (!isowned) IEEE80211_NODE_LOCK(nt); keyix = ni->ni_ucastkey.wk_rxkeyix; - status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); + status = ieee80211_crypto_delkey(vap, &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 (!isowned) - IEEE80211_NODE_UNLOCK(&ic->ic_sta); + IEEE80211_NODE_UNLOCK(nt); 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, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s: delete key map entry %p<%s> refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1); @@ -1386,7 +1596,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) IEEE80211_NODE_LOCK_ASSERT(nt); - IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, "%s: remove %p<%s> from %s table, refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr), nt->nt_name, ieee80211_node_refcnt(ni)-1); @@ -1400,7 +1610,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) 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, + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, "%s: %p<%s> clear key map entry\n", __func__, ni, ether_sprintf(ni->ni_macaddr)); nt->nt_keyixmap[keyix] = NULL; @@ -1420,23 +1630,151 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) _ieee80211_free_node(ni); } +/* + * Reclaim a (bss) node. Decrement the refcnt and reclaim + * the node if the only other reference to it is in the sta + * table. This is effectively ieee80211_free_node followed + * by node_reclaim when the refcnt is 1 (after the free). + */ static void -ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt) +ieee80211_node_reclaim(struct ieee80211_node *ni) { - struct ieee80211com *ic = nt->nt_ic; - struct ieee80211_node *ni; + struct ieee80211_node_table *nt = ni->ni_table; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, - "%s: free all nodes in %s table\n", __func__, nt->nt_name); + KASSERT(nt != NULL, ("reclaim node not in table")); + +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1); +#endif + IEEE80211_NODE_LOCK(nt); + if (ieee80211_node_dectestref(ni)) { + /* + * Last reference, reclaim state. + */ + _ieee80211_free_node(ni); + nt = NULL; + } 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_vap, + IEEE80211_MSG_NODE, + "%s: %p<%s> clear key map entry", __func__, + ni, ether_sprintf(ni->ni_macaddr)); + nt->nt_keyixmap[keyix] = NULL; + ieee80211_node_decref(ni); /* XXX needed? */ + _ieee80211_free_node(ni); + nt = NULL; + } + } + if (nt != NULL && ieee80211_node_refcnt(ni) == 1) { + /* + * Last reference is in the sta table; complete + * the reclaim. This handles bss nodes being + * recycled: the node has two references, one for + * iv_bss and one for the table. After dropping + * the iv_bss ref above we need to reclaim the sta + * table reference. + */ + ieee80211_node_decref(ni); /* NB: be pendantic */ + _ieee80211_free_node(ni); + } + IEEE80211_NODE_UNLOCK(nt); +} - while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) { +/* + * Node table support. + */ + +static void +ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, + const char *name, int inact, int keyixmax) +{ + struct ifnet *ifp = ic->ic_ifp; + + nt->nt_ic = ic; + IEEE80211_NODE_LOCK_INIT(nt, ifp->if_xname); + IEEE80211_NODE_ITERATE_LOCK_INIT(nt, ifp->if_xname); + TAILQ_INIT(&nt->nt_node); + nt->nt_name = name; + nt->nt_scangen = 1; + nt->nt_inact_init = inact; + nt->nt_keyixmax = keyixmax; + if (nt->nt_keyixmax > 0) { + MALLOC(nt->nt_keyixmap, struct ieee80211_node **, + keyixmax * sizeof(struct ieee80211_node *), + M_80211_NODE, M_NOWAIT | M_ZERO); + if (nt->nt_keyixmap == NULL) + if_printf(ic->ic_ifp, + "Cannot allocate key index map with %u entries\n", + keyixmax); + } else + nt->nt_keyixmap = NULL; +} + +static void +ieee80211_node_table_reset(struct ieee80211_node_table *nt, + struct ieee80211vap *match) +{ + struct ieee80211_node *ni, *next; + + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) { + if (match != NULL && ni->ni_vap != match) + continue; + /* XXX can this happen? if so need's work */ 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); + struct ieee80211vap *vap = ni->ni_vap; + + if (vap->iv_auth->ia_node_leave != NULL) + vap->iv_auth->ia_node_leave(ni); + if (vap->iv_aid_bitmap != NULL) + IEEE80211_AID_CLR(vap, ni->ni_associd); } + ni->ni_wdsvap = NULL; /* clear reference */ node_reclaim(nt, ni); } + if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) { + /* + * Make a separate pass to clear references to this vap + * held by DWDS entries. They will not be matched above + * because ni_vap will point to the ap vap but we still + * need to clear ni_wdsvap when the WDS vap is destroyed + * and/or reset. + */ + TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) + if (ni->ni_wdsvap == match) + ni->ni_wdsvap = NULL; + } + IEEE80211_NODE_UNLOCK(nt); +} + +static void +ieee80211_node_table_cleanup(struct ieee80211_node_table *nt) +{ + ieee80211_node_table_reset(nt, NULL); + if (nt->nt_keyixmap != NULL) { +#ifdef DIAGNOSTIC + /* 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); +#endif + FREE(nt->nt_keyixmap, M_80211_NODE); + nt->nt_keyixmap = NULL; + } + IEEE80211_NODE_ITERATE_LOCK_DESTROY(nt); + IEEE80211_NODE_LOCK_DESTROY(nt); } /* @@ -1450,16 +1788,14 @@ ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt) * process each node only once. */ static void -ieee80211_timeout_stations(struct ieee80211_node_table *nt) +ieee80211_timeout_stations(struct ieee80211com *ic) { - struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211vap *vap; struct ieee80211_node *ni; - u_int gen; - int isadhoc; + int gen = 0; - isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO); - IEEE80211_SCAN_LOCK(nt); + IEEE80211_NODE_ITERATE_LOCK(nt); gen = ++nt->nt_scangen; restart: IEEE80211_NODE_LOCK(nt); @@ -1473,14 +1809,25 @@ restart: * will be reclaimed when the last reference to them * goes away (when frame xmits complete). */ - if ((ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_STA) && + vap = ni->ni_vap; + /* + * Only process stations when in RUN state. This + * insures, for example, that we don't timeout an + * inactive station during CAC. Note that CSA state + * is actually handled in ieee80211_node_timeout as + * it applies to more than timeout processing. + */ + if (vap->iv_state != IEEE80211_S_RUN) + continue; + /* XXX can vap be NULL? */ + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_STA) && (ni->ni_flags & IEEE80211_NODE_AREF) == 0) continue; /* * Free fragment if not needed anymore * (last fragment older than 1s). - * XXX doesn't belong here + * XXX doesn't belong here, move to node_age */ if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) { @@ -1492,17 +1839,17 @@ restart: /* * Special case ourself; we may be idle for extended periods * of time and regardless reclaiming our state is wrong. + * XXX run ic_node_age */ - if (ni == ic->ic_bss) + if (ni == vap->iv_bss) continue; - if (ni->ni_associd != 0 || isadhoc) { + if (ni->ni_associd != 0 || + (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO)) { /* - * Age frames on the power save queue. + * Age/drain resources held by the station. */ - if (ieee80211_node_saveq_age(ni) != 0 && - IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 && - ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); + ic->ic_node_age(ni); /* * Probe the station before time it out. We * send a null data frame which may not be @@ -1515,11 +1862,11 @@ restart: * of); this will get fixed more properly * soon with better handling of the rate set. */ - if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) && + if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) && (0 < ni->ni_inact && - ni->ni_inact <= ic->ic_inact_probe) && + ni->ni_inact <= vap->iv_inact_probe) && ni->ni_rates.rs_nrates != 0) { - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, "%s", "probe station due to inactivity"); @@ -1537,9 +1884,9 @@ restart: goto restart; } } - if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) && + if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) && ni->ni_inact <= 0) { - IEEE80211_NOTE(ic, + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, "station timed out due to inactivity " "(refcnt %u)", ieee80211_node_refcnt(ni)); @@ -1561,45 +1908,109 @@ restart: ieee80211_ref_node(ni); IEEE80211_NODE_UNLOCK(nt); if (ni->ni_associd != 0) { - IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_EXPIRE); } - ieee80211_node_leave(ic, ni); + ieee80211_node_leave(ni); ieee80211_free_node(ni); - ic->ic_stats.is_node_timeout++; + vap->iv_stats.is_node_timeout++; goto restart; } } IEEE80211_NODE_UNLOCK(nt); - IEEE80211_SCAN_UNLOCK(nt); + IEEE80211_NODE_ITERATE_UNLOCK(nt); +} + +/* + * Aggressively reclaim resources. This should be used + * only in a critical situation to reclaim mbuf resources. + */ +void +ieee80211_drain(struct ieee80211com *ic) +{ + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + /* + * 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). + */ + vap = ni->ni_vap; + /* + * Only process stations when in RUN state. This + * insures, for example, that we don't timeout an + * inactive station during CAC. Note that CSA state + * is actually handled in ieee80211_node_timeout as + * it applies to more than timeout processing. + */ + if (vap->iv_state != IEEE80211_S_RUN) + continue; + /* XXX can vap be NULL? */ + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_STA) && + (ni->ni_flags & IEEE80211_NODE_AREF) == 0) + continue; + /* + * Free fragments. + * XXX doesn't belong here, move to node_drain + */ + if (ni->ni_rxfrag[0] != NULL) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + /* + * Drain resources held by the station. + */ + ic->ic_node_drain(ni); + } + IEEE80211_NODE_UNLOCK(nt); } +/* + * Per-ieee80211com inactivity timer callback. + */ void ieee80211_node_timeout(void *arg) { struct ieee80211com *ic = arg; - ieee80211_scan_timeout(ic); - ieee80211_timeout_stations(&ic->ic_sta); - - IEEE80211_LOCK(ic); - ieee80211_erp_timeout(ic); - ieee80211_ht_timeout(ic); - IEEE80211_UNLOCK(ic); + /* + * Defer timeout processing if a channel switch is pending. + * We typically need to be mute so not doing things that + * might generate frames is good to handle in one place. + * Supressing the station timeout processing may extend the + * lifetime of inactive stations (by not decrementing their + * idle counters) but this should be ok unless the CSA is + * active for an unusually long time. + */ + if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) { + ieee80211_scan_timeout(ic); + ieee80211_timeout_stations(ic); + IEEE80211_LOCK(ic); + ieee80211_erp_timeout(ic); + ieee80211_ht_timeout(ic); + IEEE80211_UNLOCK(ic); + } callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, ieee80211_node_timeout, ic); } void -ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg) +ieee80211_iterate_nodes(struct ieee80211_node_table *nt, + ieee80211_iter_func *f, void *arg) { struct ieee80211_node *ni; u_int gen; - IEEE80211_SCAN_LOCK(nt); + IEEE80211_NODE_ITERATE_LOCK(nt); gen = ++nt->nt_scangen; restart: IEEE80211_NODE_LOCK(nt); @@ -1615,7 +2026,7 @@ restart: } IEEE80211_NODE_UNLOCK(nt); - IEEE80211_SCAN_UNLOCK(nt); + IEEE80211_NODE_ITERATE_UNLOCK(nt); } void @@ -1633,14 +2044,14 @@ ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK, ni->ni_rxfragstamp); printf("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n", - ni->ni_rstamp, ni->ni_rssi, ni->ni_noise, + ni->ni_rstamp, node_getrssi(ni), ni->ni_noise, ni->ni_intval, ni->ni_capinfo); printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n", ether_sprintf(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); + printf("\tinact %u txrate %u\n", + ni->ni_inact, ni->ni_txrate); printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n", ni->ni_htcap, ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan); @@ -1658,16 +2069,22 @@ ieee80211_dump_nodes(struct ieee80211_node_table *nt) void ieee80211_notify_erp(struct ieee80211com *ic) { - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - ieee80211_beacon_notify(ic, IEEE80211_BEACON_ERP); + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP); } /* * Handle a station joining an 11g network. */ static void -ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_node_join_11g(struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; IEEE80211_LOCK_ASSERT(ic); @@ -1680,7 +2097,7 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) */ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { ic->ic_longslotsta++; - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "station needs long slot time, count %d", ic->ic_longslotsta); /* XXX vap's w/ conflicting needs won't work */ @@ -1698,9 +2115,9 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) * then bump the counter and enable protection * if configured. */ - if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) { + if (!ieee80211_iserp_rateset(&ni->ni_rates)) { ic->ic_nonerpsta++; - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "station is !ERP, %d non-ERP stations associated", ic->ic_nonerpsta); /* @@ -1708,18 +2125,19 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) * then we must enable use of Barker preamble. */ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "%s", "station needs long preamble"); ic->ic_flags |= IEEE80211_F_USEBARKER; ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; } /* - * If protection is configured, enable it. + * If protection is configured and this is the first + * indication we should use protection, enable it. */ if (ic->ic_protmode != IEEE80211_PROT_NONE && ic->ic_nonerpsta == 1 && (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, "%s: enable use of protection\n", __func__); ic->ic_flags |= IEEE80211_F_USEPROT; ieee80211_notify_erp(ic); @@ -1729,47 +2147,49 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) } void -ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp) +ieee80211_node_join(struct ieee80211_node *ni, int resp) { + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; int newassoc; if (ni->ni_associd == 0) { uint16_t aid; - IEEE80211_LOCK(ic); + KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap")); /* * 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)) + for (aid = 1; aid < vap->iv_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(vap, aid)) break; } - if (aid >= ic->ic_max_aid) { - IEEE80211_UNLOCK(ic); - IEEE80211_SEND_MGMT(ic, ni, resp, + if (aid >= vap->iv_max_aid) { + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_REASON_ASSOC_TOOMANY); - ieee80211_node_leave(ic, ni); + ieee80211_node_leave(ni); return; } ni->ni_associd = aid | 0xc000; ni->ni_jointime = time_uptime; - IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); + IEEE80211_LOCK(ic); + IEEE80211_AID_SET(vap, ni->ni_associd); + vap->iv_sta_assoc++; ic->ic_sta_assoc++; if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) ieee80211_ht_node_join(ni); if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) - ieee80211_node_join_11g(ic, ni); + ieee80211_node_join_11g(ni); IEEE80211_UNLOCK(ic); newassoc = 1; } else newassoc = 0; - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s", IEEE80211_NODE_AID(ni), ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long", @@ -1779,20 +2199,20 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp ni->ni_flags & IEEE80211_NODE_HT ? (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "", ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", - IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ? + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? ", fast-frames" : "", - IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ? + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? ", turbo" : "" ); /* give driver a chance to setup state like ni_txrate */ if (ic->ic_newassoc != NULL) ic->ic_newassoc(ni, newassoc); - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); + IEEE80211_SEND_MGMT(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, + if (vap->iv_auth->ia_node_join != NULL) + vap->iv_auth->ia_node_join(ni); + ieee80211_notify_node_join(ni, resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); } @@ -1817,14 +2237,15 @@ disable_protection(struct ieee80211com *ic) * Handle a station leaving an 11g network. */ static void -ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_node_leave_11g(struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; IEEE80211_LOCK_ASSERT(ic); KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan), - ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq, - ic->ic_bsschan->ic_flags, ic->ic_curmode)); + ("not in 11g, bss %u:0x%x", ic->ic_bsschan->ic_freq, + ic->ic_bsschan->ic_flags)); /* * If a long slot station do the slot time bookkeeping. @@ -1833,7 +2254,7 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) KASSERT(ic->ic_longslotsta > 0, ("bogus long slot station count %d", ic->ic_longslotsta)); ic->ic_longslotsta--; - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "long slot time station leaves, count now %d", ic->ic_longslotsta); if (ic->ic_longslotsta == 0) { @@ -1843,7 +2264,8 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) */ if ((ic->ic_caps & IEEE80211_C_SHSLOT) && ic->ic_opmode != IEEE80211_M_IBSS) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + IEEE80211_DPRINTF(ni->ni_vap, + IEEE80211_MSG_ASSOC, "%s: re-enable use of short slot time\n", __func__); ieee80211_set_shortslottime(ic, 1); @@ -1857,13 +2279,13 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) KASSERT(ic->ic_nonerpsta > 0, ("bogus non-ERP station count %d", ic->ic_nonerpsta)); ic->ic_nonerpsta--; - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni, + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "non-ERP station leaves, count now %d%s", ic->ic_nonerpsta, (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ? " (non-ERP sta present)" : ""); if (ic->ic_nonerpsta == 0 && (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, "%s: disable use of protection\n", __func__); disable_protection(ic); } @@ -1886,8 +2308,10 @@ ieee80211_erp_timeout(struct ieee80211com *ic) if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) && time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "%s\n", "age out non-ERP sta present on channel"); +#if 0 + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "%s", "age out non-ERP sta present on channel"); +#endif ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; if (ic->ic_nonerpsta == 0) disable_protection(ic); @@ -1899,15 +2323,17 @@ ieee80211_erp_timeout(struct ieee80211com *ic) * when operating as an ap. */ void -ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_node_leave(struct ieee80211_node *ni) { + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_node_table *nt = ni->ni_table; - IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, "station with aid %d leaves", IEEE80211_NODE_AID(ni)); - KASSERT(ic->ic_opmode != IEEE80211_M_STA, - ("unexpected operating mode %u", ic->ic_opmode)); + KASSERT(vap->iv_opmode != IEEE80211_M_STA, + ("unexpected operating mode %u", vap->iv_opmode)); /* * If node wasn't previously associated all * we need to do is reclaim the reference. @@ -1921,19 +2347,20 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) * 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); + if (vap->iv_auth->ia_node_leave != NULL) + vap->iv_auth->ia_node_leave(ni); IEEE80211_LOCK(ic); - IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + IEEE80211_AID_CLR(vap, ni->ni_associd); ni->ni_associd = 0; + vap->iv_sta_assoc--; ic->ic_sta_assoc--; if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) ieee80211_ht_node_leave(ni); if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) - ieee80211_node_leave_11g(ic, ni); + ieee80211_node_leave_11g(ni); IEEE80211_UNLOCK(ic); /* * Cleanup station state. In particular clear various @@ -1941,7 +2368,7 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) * is reused before the reference count goes to zero * (and memory is reclaimed). */ - ieee80211_sta_leave(ic, ni); + ieee80211_sta_leave(ni); done: /* * Remove the node from any table it's recorded in and @@ -1957,121 +2384,89 @@ done: ieee80211_free_node(ni); } +struct rssiinfo { + struct ieee80211vap *vap; + int rssi_samples; + uint32_t rssi_total; +}; + +static void +get_hostap_rssi(void *arg, struct ieee80211_node *ni) +{ + struct rssiinfo *info = arg; + struct ieee80211vap *vap = ni->ni_vap; + int8_t rssi; + + if (info->vap != vap) + return; + /* only associated stations */ + if (ni->ni_associd == 0) + return; + rssi = vap->iv_ic->ic_node_getrssi(ni); + if (rssi != 0) { + info->rssi_samples++; + info->rssi_total += rssi; + } +} + +static void +get_adhoc_rssi(void *arg, struct ieee80211_node *ni) +{ + struct rssiinfo *info = arg; + struct ieee80211vap *vap = ni->ni_vap; + int8_t rssi; + + if (info->vap != vap) + return; + /* only neighbors */ + /* XXX check bssid */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + return; + rssi = vap->iv_ic->ic_node_getrssi(ni); + if (rssi != 0) { + info->rssi_samples++; + info->rssi_total += rssi; + } +} + int8_t -ieee80211_getrssi(struct ieee80211com *ic) +ieee80211_getrssi(struct ieee80211vap *vap) { #define NZ(x) ((x) == 0 ? 1 : (x)) - struct ieee80211_node_table *nt = &ic->ic_sta; - int rssi_samples; - int32_t rssi_total; - struct ieee80211_node *ni; + struct ieee80211com *ic = vap->iv_ic; + struct rssiinfo info; - rssi_total = 0; - rssi_samples = 0; - switch (ic->ic_opmode) { + info.rssi_total = 0; + info.rssi_samples = 0; + info.vap = vap; + switch (vap->iv_opmode) { case IEEE80211_M_IBSS: /* average of all ibss neighbors */ case IEEE80211_M_AHDEMO: /* average of all neighbors */ + ieee80211_iterate_nodes(&ic->ic_sta, get_adhoc_rssi, &info); + break; case IEEE80211_M_HOSTAP: /* average of all associated stations */ - /* XXX locking */ - TAILQ_FOREACH(ni, &nt->nt_node, ni_list) - if (ic->ic_opmode == IEEE80211_M_HOSTAP || - (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) { - int8_t rssi = ic->ic_node_getrssi(ni); - if (rssi != 0) { - rssi_samples++; - rssi_total += rssi; - } - } + ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info); 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; + if (vap->iv_bss != NULL) + info.rssi_total = ic->ic_node_getrssi(vap->iv_bss); + info.rssi_samples = 1; break; } - return rssi_total / NZ(rssi_samples); + return info.rssi_total / NZ(info.rssi_samples); #undef NZ } void -ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise) +ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise) { - if (ic->ic_bss == NULL) /* NB: shouldn't happen */ + if (vap->iv_bss == NULL) /* NB: shouldn't happen */ return; - ic->ic_node_getsignal(ic->ic_bss, rssi, noise); + vap->iv_ic->ic_node_getsignal(vap->iv_bss, rssi, noise); /* for non-station mode return avg'd rssi accounting */ - if (ic->ic_opmode != IEEE80211_M_STA) - *rssi = ieee80211_getrssi(ic); -} - -/* - * Node table support. - */ - -static void -ieee80211_node_table_init(struct ieee80211com *ic, - struct ieee80211_node_table *nt, - const char *name, int inact, int keyixmax) -{ - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, - "%s %s table, inact %u\n", __func__, name, inact); - - nt->nt_ic = ic; - /* XXX need unit */ - IEEE80211_NODE_LOCK_INIT(nt, ic->ic_ifp->if_xname); - IEEE80211_SCAN_LOCK_INIT(nt, ic->ic_ifp->if_xname); - TAILQ_INIT(&nt->nt_node); - nt->nt_name = name; - nt->nt_scangen = 1; - nt->nt_inact_init = inact; - nt->nt_keyixmax = keyixmax; - if (nt->nt_keyixmax > 0) { - MALLOC(nt->nt_keyixmap, struct ieee80211_node **, - keyixmax * sizeof(struct ieee80211_node *), - M_80211_NODE, M_NOWAIT | M_ZERO); - if (nt->nt_keyixmap == NULL) - if_printf(ic->ic_ifp, - "Cannot allocate key index map with %u entries\n", - keyixmax); - } else - nt->nt_keyixmap = NULL; -} - -static void -ieee80211_node_table_reset(struct ieee80211_node_table *nt) -{ - - IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, - "%s %s table\n", __func__, nt->nt_name); - - IEEE80211_NODE_LOCK(nt); - ieee80211_free_allnodes_locked(nt); - IEEE80211_NODE_UNLOCK(nt); -} - -static void -ieee80211_node_table_cleanup(struct ieee80211_node_table *nt) -{ - - IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, - "%s %s table\n", __func__, nt->nt_name); - - IEEE80211_NODE_LOCK(nt); - ieee80211_free_allnodes_locked(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; - } - IEEE80211_SCAN_LOCK_DESTROY(nt); - IEEE80211_NODE_LOCK_DESTROY(nt); + if (vap->iv_opmode != IEEE80211_M_STA) + *rssi = ieee80211_getrssi(vap); } diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 2799ed4..fb569a9 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,18 +32,16 @@ #include <net80211/ieee80211_ht.h> /* for aggregation state */ /* - * 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 + * Each ieee80211com instance has a single timer that fires every + * IEEE80211_INACT_WAIT seconds to handle "inactivity processing". + * This is used to do node inactivity processing when operating + * as an AP or in adhoc mode. 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) */ @@ -64,19 +62,28 @@ (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \ IEEE80211_NODE_HASHSIZE) -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; +struct ieee80211vap; + +/* + * Information element ``blob''. We use this structure + * to capture management frame payloads that need to be + * retained. Information elemnts within the payload that + * we need to consult have references recorded. + */ +struct ieee80211_ies { + /* the following are either NULL or point within data */ + uint8_t *wpa_ie; /* captured WPA ie */ + uint8_t *rsn_ie; /* captured RSN ie */ + uint8_t *wme_ie; /* captured WME ie */ + uint8_t *ath_ie; /* captured Atheros ie */ + uint8_t *htcap_ie; /* captured HTCAP ie */ + uint8_t *htinfo_ie; /* captured HTINFO ie */ + /* NB: these must be the last members of this structure */ + uint8_t *data; /* frame data > 802.11 header */ + int len; /* data size in bytes */ +}; /* * Node specific information. Note that drivers are expected @@ -85,11 +92,12 @@ struct ieee80211com; * 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; + struct ieee80211vap *ni_vap; /* associated vap */ + struct ieee80211com *ni_ic; /* copy from vap to save deref*/ + struct ieee80211_node_table *ni_table; /* NB: may be NULL */ + TAILQ_ENTRY(ieee80211_node) ni_list; /* list of all nodes */ + LIST_ENTRY(ieee80211_node) ni_hash; /* hash collision list */ + u_int ni_refcnt; /* count of held references */ u_int ni_scangen; /* gen# for timeout scan */ uint8_t ni_authmode; /* authentication algorithm */ uint8_t ni_ath_flags; /* Atheros feature flags */ @@ -99,7 +107,7 @@ struct ieee80211_node { #define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */ #define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */ #define IEEE80211_NODE_AR 0x0010 /* AR capable */ -#define IEEE80211_NODE_BOOST 0x0080 +#define IEEE80211_NODE_BOOST 0x0080 #define IEEE80211_NODE_PSUPDATE 0x0200 /* power save state changed */ #define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */ uint16_t ni_flags; /* special-purpose state */ @@ -111,6 +119,8 @@ struct ieee80211_node { #define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */ #define IEEE80211_NODE_HT 0x0040 /* HT enabled */ #define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */ +#define IEEE80211_NODE_WPS 0x0100 /* WPS association */ +#define IEEE80211_NODE_TSN 0x0200 /* TSN association */ #define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */ #define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */ uint16_t ni_ath_defkeyix;/* Atheros def key index */ @@ -119,22 +129,18 @@ struct ieee80211_node { uint16_t ni_vlan; /* vlan tag */ uint32_t ni_jointime; /* time of join (secs) */ uint32_t *ni_challenge; /* shared-key challenge */ - uint8_t *ni_wpa_ie; /* captured WPA ie */ - uint8_t *ni_rsn_ie; /* captured RSN ie */ - uint8_t *ni_wme_ie; /* captured WME ie */ - uint8_t *ni_ath_ie; /* captured Atheros ie */ + struct ieee80211_ies ni_ies; /* captured ie's */ /* tx seq per-tid */ uint16_t ni_txseqs[IEEE80211_TID_SIZE]; /* rx seq previous per-tid*/ uint16_t ni_rxseqs[IEEE80211_TID_SIZE]; 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 */ - int8_t ni_rssi; /* recv ssi */ + uint32_t ni_avgrssi; /* recv ssi state */ int8_t ni_noise; /* noise floor */ /* header */ @@ -144,7 +150,7 @@ struct ieee80211_node { /* beacon, probe response */ union { uint8_t data[8]; - uint64_t tsf; + u_int64_t tsf; } ni_tstamp; /* from last rcv'd beacon */ uint16_t ni_intval; /* beacon interval */ uint16_t ni_capinfo; /* capabilities */ @@ -154,13 +160,12 @@ struct ieee80211_node { struct ieee80211_channel *ni_chan; uint16_t ni_fhdwell; /* FH only */ uint8_t ni_fhindex; /* FH only */ - uint8_t ni_erp; /* ERP from beacon/probe resp */ + uint16_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 */ /* 11n state */ - uint8_t *ni_htcap_ie; /* captured HTCAP ie */ uint16_t ni_htcap; /* HT capabilities */ uint8_t ni_htparam; /* HT params */ uint8_t ni_htctlchan; /* HT control channel */ @@ -174,14 +179,18 @@ struct ieee80211_node { struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID]; /* others */ - int ni_fails; /* failure count to associate */ 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 */ + int ni_txrate; /* legacy rate/MCS */ + struct ifqueue ni_savedq; /* ps-poll queue */ struct ieee80211_nodestats ni_stats; /* per-node statistics */ + + struct ieee80211vap *ni_wdsvap; /* associated WDS vap */ + /* XXX move to vap? */ + struct ifqueue ni_wdsq; /* wds pending queue */ }; MALLOC_DECLARE(M_80211_NODE); +MALLOC_DECLARE(M_80211_NODE_IE); #define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP) #define IEEE80211_NODE_AMPDU \ @@ -193,6 +202,38 @@ MALLOC_DECLARE(M_80211_NODE); #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) +/* + * Filtered rssi calculation support. The receive rssi is maintained + * as an average over the last 10 frames received using a low pass filter + * (all frames for now, possibly need to be more selective). Calculations + * are designed such that a good compiler can optimize them. The avg + * rssi state should be initialized to IEEE80211_RSSI_DUMMY_MARKER and + * each sample incorporated with IEEE80211_RSSI_LPF. Use IEEE80211_RSSI_GET + * to extract the current value. + * + * Note that we assume rssi data are in the range [-127..127] and we + * discard values <-20. This is consistent with assumptions throughout + * net80211 that signal strength data are in .5 dBm units relative to + * the current noise floor (linear, not log). + */ +#define IEEE80211_RSSI_LPF_LEN 10 +#define IEEE80211_RSSI_DUMMY_MARKER 127 +/* NB: pow2 to optimize out * and / */ +#define IEEE80211_RSSI_EP_MULTIPLIER (1<<7) +#define IEEE80211_RSSI_IN(x) ((x) * IEEE80211_RSSI_EP_MULTIPLIER) +#define _IEEE80211_RSSI_LPF(x, y, len) \ + (((x) != IEEE80211_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) +#define IEEE80211_RSSI_LPF(x, y) do { \ + if ((y) >= -20) { \ + x = _IEEE80211_RSSI_LPF((x), IEEE80211_RSSI_IN((y)), \ + IEEE80211_RSSI_LPF_LEN); \ + } \ +} while (0) +#define IEEE80211_RSSI_EP_RND(x, mul) \ + ((((x) % (mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define IEEE80211_RSSI_GET(x) \ + IEEE80211_RSSI_EP_RND(x, IEEE80211_RSSI_EP_MULTIPLIER) + static __inline struct ieee80211_node * ieee80211_ref_node(struct ieee80211_node *ni) { @@ -212,6 +253,9 @@ struct ieee80211com; void ieee80211_node_attach(struct ieee80211com *); void ieee80211_node_lateattach(struct ieee80211com *); void ieee80211_node_detach(struct ieee80211com *); +void ieee80211_node_vattach(struct ieee80211vap *); +void ieee80211_node_latevattach(struct ieee80211vap *); +void ieee80211_node_vdetach(struct ieee80211vap *); static __inline int ieee80211_node_is_authorized(const struct ieee80211_node *ni) @@ -222,21 +266,32 @@ ieee80211_node_is_authorized(const struct ieee80211_node *ni) void ieee80211_node_authorize(struct ieee80211_node *); void ieee80211_node_unauthorize(struct ieee80211_node *); -void ieee80211_probe_curchan(struct ieee80211com *, int); -void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *); -void ieee80211_reset_bss(struct ieee80211com *); -void ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *); +void ieee80211_node_set_chan(struct ieee80211_node *, + struct ieee80211_channel *); +void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *); +void ieee80211_reset_bss(struct ieee80211vap *); +void ieee80211_sync_curchan(struct ieee80211com *); +void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *); int ieee80211_ibss_merge(struct ieee80211_node *); struct ieee80211_scan_entry; -int ieee80211_sta_join(struct ieee80211com *, +int ieee80211_sta_join(struct ieee80211vap *, const struct ieee80211_scan_entry *); -void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *); +void ieee80211_sta_leave(struct ieee80211_node *); +void ieee80211_node_deauth(struct ieee80211_node *, int); + +int ieee80211_ies_init(struct ieee80211_ies *, const uint8_t *, int); +void ieee80211_ies_cleanup(struct ieee80211_ies *); +void ieee80211_ies_expand(struct ieee80211_ies *); +#define ieee80211_ies_setie(_ies, _ie, _off) do { \ + (_ies)._ie = (_ies).data + (_off); \ +} while (0) /* * 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. + * has one that holds association stations (when operating + * as an ap) or neighbors (in ibss mode). + * + * XXX embed this in ieee80211com instead of indirect? */ struct ieee80211_node_table { struct ieee80211com *nt_ic; /* back reference */ @@ -245,23 +300,41 @@ struct ieee80211_node_table { LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE]; struct ieee80211_node **nt_keyixmap; /* key ix -> node map */ int nt_keyixmax; /* keyixmap size */ - const char *nt_name; /* for debugging */ + const char *nt_name; /* table name for debug msgs */ ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */ - u_int nt_scangen; /* gen# for timeout scan */ + u_int nt_scangen; /* gen# for iterators */ int nt_inact_init; /* initial node inact setting */ }; -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 *); +struct ieee80211_node *ieee80211_alloc_node(struct ieee80211_node_table *, + struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +struct ieee80211_node *ieee80211_tmp_node(struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +struct ieee80211_node *ieee80211_node_create_wds(struct ieee80211vap *, + const uint8_t bssid[IEEE80211_ADDR_LEN], + struct ieee80211_channel *); #ifdef IEEE80211_DEBUG_REFCNT void ieee80211_free_node_debug(struct ieee80211_node *, const char *func, int line); +struct ieee80211_node *ieee80211_find_node_locked_debug( + struct ieee80211_node_table *, + const uint8_t macaddr[IEEE80211_ADDR_LEN], + const char *func, int line); struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *, - const uint8_t *, + const uint8_t macaddr[IEEE80211_ADDR_LEN], + const char *func, int line); +struct ieee80211_node *ieee80211_find_vap_node_locked_debug( + struct ieee80211_node_table *, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], + const char *func, int line); +struct ieee80211_node *ieee80211_find_vap_node_debug( + struct ieee80211_node_table *, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line); struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *, const struct ieee80211_frame_min *, @@ -270,42 +343,43 @@ 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_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 *, +struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *, const uint8_t *, 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_locked(nt, mac) \ + ieee80211_find_node_locked_debug(nt, mac, __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_ssid(nt, mac, sl, ss) \ - ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__) +#define ieee80211_find_vap_node_locked(nt, vap, mac) \ + ieee80211_find_vap_node_locked_debug(nt, vap, mac, __func__, __LINE__) +#define ieee80211_find_vap_node(nt, vap, mac) \ + ieee80211_find_vap_node_debug(nt, vap, mac, __func__, __LINE__) +#define ieee80211_find_rxnode(ic, wh) \ + ieee80211_find_rxnode_debug(ic, wh, __func__, __LINE__) +#define ieee80211_find_rxnode_withkey(ic, wh, keyix) \ + ieee80211_find_rxnode_withkey_debug(ic, wh, keyix, __func__, __LINE__) +#define ieee80211_find_txnode(vap, mac) \ + ieee80211_find_txnode_debug(vap, mac, __func__, __LINE__) #else void ieee80211_free_node(struct ieee80211_node *); +struct ieee80211_node *ieee80211_find_node_locked(struct ieee80211_node_table *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *, - const uint8_t *); + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +struct ieee80211_node *ieee80211_find_vap_node_locked( + struct ieee80211_node_table *, const struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +struct ieee80211_node *ieee80211_find_vap_node( + struct ieee80211_node_table *, const struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); 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_ssid( - struct ieee80211_node_table *, const uint8_t *macaddr, - u_int ssidlen, const uint8_t *ssid); +struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); #endif int ieee80211_node_delucastkey(struct ieee80211_node *); void ieee80211_node_timeout(void *arg); @@ -314,23 +388,22 @@ typedef void ieee80211_iter_func(void *, struct ieee80211_node *); void ieee80211_iterate_nodes(struct ieee80211_node_table *, ieee80211_iter_func *, void *); +void ieee80211_notify_erp(struct ieee80211com *); void ieee80211_dump_node(struct ieee80211_node_table *, struct ieee80211_node *); void ieee80211_dump_nodes(struct ieee80211_node_table *); -void ieee80211_notify_erp(struct ieee80211com *); - -struct ieee80211_node *ieee80211_fakeup_adhoc_node( - struct ieee80211_node_table *, const uint8_t macaddr[]); +struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *, + const uint8_t macaddr[IEEE80211_ADDR_LEN]); struct ieee80211_scanparams; void ieee80211_init_neighbor(struct ieee80211_node *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); -struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *, +struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); -void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int); -void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *); -int8_t ieee80211_getrssi(struct ieee80211com *); -void ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *); +void ieee80211_node_join(struct ieee80211_node *,int); +void ieee80211_node_leave(struct ieee80211_node *); +int8_t ieee80211_getrssi(struct ieee80211vap *); +void ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *); #endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 6e31d1d..cc2a911 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_wlan.h" #include <sys/param.h> #include <sys/systm.h> @@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_wds.h> #ifdef INET #include <netinet/in.h> @@ -57,10 +59,10 @@ __FBSDID("$FreeBSD$"); #define ETHER_HEADER_COPY(dst, src) \ memcpy(dst, src, sizeof(struct ether_header)) -static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic, +static struct mbuf *ieee80211_encap_fastframe(struct ieee80211vap *, struct mbuf *m1, const struct ether_header *eh1, struct mbuf *m2, const struct ether_header *eh2); -static int ieee80211_fragment(struct ieee80211com *, struct mbuf *, +static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, u_int hdrsize, u_int ciphdrsize, u_int mtu); static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); @@ -72,24 +74,344 @@ static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); * (e.g. beacons). */ static __inline int -doprint(struct ieee80211com *ic, int subtype) +doprint(struct ieee80211vap *vap, int subtype) { switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - return (ic->ic_opmode == IEEE80211_M_IBSS); + return (vap->iv_opmode == IEEE80211_M_IBSS); } return 1; } #endif /* + * Start method for vap's. All packets from the stack come + * through here. We handle common processing of the packets + * before dispatching them to the underlying device. + */ +void +ieee80211_start(struct ifnet *ifp) +{ +#define IS_DWDS(vap) \ + (vap->iv_opmode == IEEE80211_M_WDS && \ + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *parent = ic->ic_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + struct ether_header *eh; + int error; + + /* NB: parent must be up and running */ + if (!IFNET_IS_UP_RUNNING(parent)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, parent %s not up+running\n", + __func__, parent->if_xname); + /* XXX stat */ + return; + } + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* + * In power save, wakeup device for transmit. + */ + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); + return; + } + /* + * No data frames go out unless we're running. + * Note in particular this covers CAC and CSA + * states (though maybe we should check muting + * for CSA). + */ + if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_LOCK(ic); + /* re-check under the com lock to avoid races */ + if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, in %s state\n", + __func__, ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_tx_badstate++; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IEEE80211_UNLOCK(ic); + return; + } + IEEE80211_UNLOCK(ic); + } + for (;;) { + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + /* + * Sanitize mbuf flags for net80211 use. We cannot + * clear M_PWR_SAV because this may be set for frames + * that are re-submitted from the power save queue. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~(M_80211_TX - M_PWR_SAV); + /* + * Cancel any background scan. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_anyscan(vap); + /* + * Find the node for the destination so we can do + * things like power save and fast frames aggregation. + * + * NB: past this point various code assumes the first + * mbuf has the 802.3 header present (and contiguous). + */ + ni = NULL; + if (m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "discard frame, %s\n", "m_pullup failed"); + vap->iv_stats.is_tx_nobuf++; /* XXX */ + ifp->if_oerrors++; + continue; + } + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + if (IS_DWDS(vap)) { + /* + * Only unicast frames from the above go out + * DWDS vaps; multicast frames are handled by + * dispatching the frame as it comes through + * the AP vap (see below). + */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, + eh->ether_dhost, "mcast", "%s", "on DWDS"); + vap->iv_stats.is_dwds_mcast++; + m_freem(m); + continue; + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + } + ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + ifp->if_oerrors++; + m_freem(m); + continue; + } + /* XXX AUTH'd */ + if (ni->ni_associd == 0) { + /* + * Destination is not associated; must special + * case DWDS where we point iv_bss at the node + * for the associated station. + * XXX adhoc mode? + */ + if (ni != vap->iv_bss || IS_DWDS(vap)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "sta not associated (type 0x%04x)", + htons(eh->ether_type)); + vap->iv_stats.is_tx_notassoc++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + continue; + } + } + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m->m_flags & M_PWR_SAV) == 0) { + /* + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + * XXX lose WDS vap linkage? + */ + ieee80211_pwrsave(ni, m); + ieee80211_free_node(ni); + continue; + } + /* calculate priority so drivers can find the tx queue */ + if (ieee80211_classify(ni, m)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "%s", "classification failure"); + vap->iv_stats.is_tx_classify++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + continue; + } + + BPF_MTAP(ifp, m); /* 802.11 tx path */ + + /* + * XXX When ni is associated with a WDS link then + * the vap will be the WDS vap but ni_vap will point + * to the ap vap the station associated to. Once + * we handoff the packet to the driver the callback + * to ieee80211_encap won't be able to tell if the + * packet should be encapsulated for WDS or not (e.g. + * multicast frames will not be handled correctly). + * We hack this by marking the mbuf so ieee80211_encap + * can do the right thing. + */ + if (vap->iv_opmode == IEEE80211_M_WDS) + m->m_flags |= M_WDS; + else + m->m_flags &= ~M_WDS; + + /* + * Stash the node pointer and hand the frame off to + * the underlying device. Note that we do this after + * any call to ieee80211_dwds_mcast because that code + * uses any existing value for rcvif. + */ + m->m_pkthdr.rcvif = (void *)ni; + + /* XXX defer if_start calls? */ + IFQ_HANDOFF(parent, m, error); + if (error != 0) { + /* NB: IFQ_HANDOFF reclaims mbuf */ + ieee80211_free_node(ni); + } else { + ifp->if_opackets++; + } + ic->ic_lastdata = ticks; + } +#undef IS_DWDS +} + +/* + * 802.11 output routine. This is (currently) used only to + * connect bpf write calls to the 802.11 layer for injecting + * raw 802.11 frames. Note we locate the ieee80211com from + * the ifnet using a spare field setup at attach time. This + * will go away when the virtual ap support comes in. + */ +int +ieee80211_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt0) +{ +#define senderr(e) do { error = (e); goto bad;} while (0) + struct ieee80211_node *ni = NULL; + struct ieee80211vap *vap; + struct ieee80211_frame *wh; + int error; + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { + /* + * Short-circuit requests if the vap is marked OACTIVE + * as this is used when tearing down state to indicate + * the vap may be gone. This can also happen because a + * packet came down through ieee80211_start before the + * vap entered RUN state in which case it's also ok to + * just drop the frame. This should not be necessary + * but callers of if_output don't check OACTIVE. + */ + senderr(ENETDOWN); + } + vap = ifp->if_softc; + /* + * Hand to the 802.3 code if not tagged as + * a raw 802.11 frame. + */ + if (dst->sa_family != AF_IEEE80211) + return vap->iv_output(ifp, m, dst, rt0); +#ifdef MAC + error = mac_check_ifnet_transmit(ifp, m); + if (error) + senderr(error); +#endif + if (ifp->if_flags & IFF_MONITOR) + senderr(ENETDOWN); + if (!IFNET_IS_UP_RUNNING(ifp)) + senderr(ENETDOWN); + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + "block %s frame in CAC state\n", "raw data"); + vap->iv_stats.is_tx_badstate++; + senderr(EIO); /* XXX */ + } + /* XXX bypass bridge, pfil, carp, etc. */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) + senderr(EIO); /* XXX */ + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) + senderr(EIO); /* XXX */ + + /* locate destination node */ + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + case IEEE80211_FC1_DIR_FROMDS: + ni = ieee80211_find_txnode(vap, wh->i_addr1); + break; + case IEEE80211_FC1_DIR_TODS: + case IEEE80211_FC1_DIR_DSTODS: + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) + senderr(EIO); /* XXX */ + ni = ieee80211_find_txnode(vap, wh->i_addr3); + break; + default: + senderr(EIO); /* XXX */ + } + if (ni == NULL) { + /* + * Permit packets w/ bpf params through regardless + * (see below about sa_len). + */ + if (dst->sa_len == 0) + senderr(EHOSTUNREACH); + ni = ieee80211_ref_node(vap->iv_bss); + } + + /* + * Sanitize mbuf for net80211 flags leaked from above. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~M_80211_TX; + + /* calculate priority so drivers can find the tx queue */ + /* XXX assumes an 802.3 frame */ + if (ieee80211_classify(ni, m)) + senderr(EIO); /* XXX */ + + BPF_MTAP(ifp, m); + + /* + * NB: DLT_IEEE802_11_RADIO identifies the parameters are + * present by setting the sa_len field of the sockaddr (yes, + * this is a hack). + * NB: we assume sa_data is suitably aligned to cast. + */ + return vap->iv_ic->ic_raw_xmit(ni, m, + (const struct ieee80211_bpf_params *)(dst->sa_len ? + dst->sa_data : NULL)); +bad: + if (m != NULL) + m_freem(m); + if (ni != NULL) + ieee80211_free_node(ni); + return error; +#undef senderr +} + +/* * 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, +ieee80211_send_setup( struct ieee80211_node *ni, struct ieee80211_frame *wh, int type, @@ -101,7 +423,9 @@ ieee80211_send_setup(struct ieee80211com *ic, wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { - switch (ic->ic_opmode) { + struct ieee80211vap *vap = ni->ni_vap; + + switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, bssid); @@ -123,9 +447,8 @@ ieee80211_send_setup(struct ieee80211com *ic, break; case IEEE80211_M_WDS: wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; - /* XXX cheat, bssid holds RA */ - IEEE80211_ADDR_COPY(wh->i_addr1, bssid); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, da); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); break; @@ -139,6 +462,7 @@ ieee80211_send_setup(struct ieee80211com *ic, IEEE80211_ADDR_COPY(wh->i_addr3, bssid); } *(uint16_t *)&wh->i_dur[0] = 0; + /* XXX probe response use per-vap seq#? */ /* NB: use non-QoS tid */ *(uint16_t *)&wh->i_seq[0] = htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT); @@ -151,55 +475,55 @@ ieee80211_send_setup(struct ieee80211com *ic, * must have a reference as the pointer will be passed to the driver * and potentially held for a long time. If the frame is successfully * dispatched to the driver, then it is responsible for freeing the - * reference (and potentially free'ing up any associated storage). + * reference (and potentially free'ing up any associated storage); + * otherwise deal with reclaiming any reference (on error). */ int -ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m, int type) +ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type) { - struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; KASSERT(ni != NULL, ("null node")); - /* - * Yech, hack alert! We want to pass the node down to the - * driver's start routine. If we don't do so then the start - * routine must immediately look it up again and that can - * cause a lock order reversal if, for example, this frame - * is being sent because the station is being timedout and - * the frame being sent is a DEAUTH message. We could stick - * this in an m_tag and tack that on to the mbuf. However - * that's rather expensive to do for every frame so instead - * we stuff it in the rcvif field since outbound frames do - * not (presently) use this. - */ + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + ni, "block %s frame in CAC state", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT]); + vap->iv_stats.is_tx_badstate++; + ieee80211_free_node(ni); + m_freem(m); + return EIO; /* XXX */ + } + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); - if (m == NULL) + if (m == NULL) { + ieee80211_free_node(ni); 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_send_setup(ni, wh, IEEE80211_FC0_TYPE_MGT | type, - ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + vap->iv_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, - "[%s] encrypting frame (%s)\n", - ether_sprintf(wh->i_addr1), __func__); + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, + "encrypting frame (%s)", __func__); wh->i_fc[1] |= IEEE80211_FC1_WEP; } - if (ni->ni_flags & IEEE80211_NODE_QOS) { - /* NB: force all management frames to the highest queue */ + if (type != IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + /* NB: force non-ProbeResp frames to the highest queue */ M_WME_SETAC(m, WME_AC_VO); } else M_WME_SETAC(m, WME_AC_BE); #ifdef IEEE80211_DEBUG /* avoid printing too many frames */ - if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || - ieee80211_msg_dumppkts(ic)) { + if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || + ieee80211_msg_dumppkts(vap)) { printf("[%s] send %s on channel %u\n", ether_sprintf(wh->i_addr1), ieee80211_mgt_subtype_name[ @@ -209,135 +533,8 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, } #endif IEEE80211_NODE_STAT(ni, tx_mgmt); - IF_ENQUEUE(&ic->ic_mgtq, m); - if_start(ifp); - ifp->if_opackets++; - - return 0; -} - -/* - * Raw packet transmit stub for legacy drivers. - * Send the packet through the mgt q so we bypass - * the normal encapsulation work. - */ -int -ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - - m->m_pkthdr.rcvif = (void *) ni; - IF_ENQUEUE(&ic->ic_mgtq, m); - if_start(ifp); - ifp->if_opackets++; - - return 0; -} - -/* - * 802.11 output routine. This is (currently) used only to - * connect bpf write calls to the 802.11 layer for injecting - * raw 802.11 frames. Note we locate the ieee80211com from - * the ifnet using a spare field setup at attach time. This - * will go away when the virtual ap support comes in. - */ -int -ieee80211_output(struct ifnet *ifp, struct mbuf *m, - struct sockaddr *dst, struct rtentry *rt0) -{ -#define senderr(e) do { error = (e); goto bad;} while (0) - struct ieee80211com *ic = ifp->if_llsoftc; /* XXX */ - struct ieee80211_node *ni = NULL; - struct ieee80211_frame *wh; - int error; - - /* - * Hand to the 802.3 code if not tagged as - * a raw 802.11 frame. - */ - if (dst->sa_family != AF_IEEE80211) - return ether_output(ifp, m, dst, rt0); -#ifdef MAC - error = mac_check_ifnet_transmit(ifp, m); - if (error) - senderr(error); -#endif - if (ifp->if_flags & IFF_MONITOR) - senderr(ENETDOWN); - if ((ifp->if_flags & IFF_UP) == 0) - senderr(ENETDOWN); - /* XXX bypass bridge, pfil, carp, etc. */ - - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) - senderr(EIO); /* XXX */ - wh = mtod(m, struct ieee80211_frame *); - if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != - IEEE80211_FC0_VERSION_0) - senderr(EIO); /* XXX */ - - /* locate destination node */ - switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { - case IEEE80211_FC1_DIR_NODS: - case IEEE80211_FC1_DIR_FROMDS: - ni = ieee80211_find_txnode(ic, wh->i_addr1); - break; - case IEEE80211_FC1_DIR_TODS: - case IEEE80211_FC1_DIR_DSTODS: - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) - senderr(EIO); /* XXX */ - ni = ieee80211_find_txnode(ic, wh->i_addr3); - break; - default: - senderr(EIO); /* XXX */ - } - if (ni == NULL) { - /* - * Permit packets w/ bpf params through regardless - * (see below about sa_len). - */ - if (dst->sa_len == 0) - senderr(EHOSTUNREACH); - ni = ieee80211_ref_node(ic->ic_bss); - } - - /* XXX ctrl frames should go through */ - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - (m->m_flags & M_PWR_SAV) == 0) { - /* - * Station in power save mode; pass the frame - * to the 802.11 layer and continue. We'll get - * the frame back when the time is right. - */ - ieee80211_pwrsave(ni, m); - error = 0; - goto reclaim; - } - - /* calculate priority so drivers can find the tx queue */ - /* XXX assumes an 802.3 frame */ - if (ieee80211_classify(ic, m, ni)) - senderr(EIO); /* XXX */ - - BPF_MTAP(ifp, m); - /* - * NB: DLT_IEEE802_11_RADIO identifies the parameters are - * present by setting the sa_len field of the sockaddr (yes, - * this is a hack). - * NB: we assume sa_data is suitably aligned to cast. - */ - return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *) - (dst->sa_len ? dst->sa_data : NULL)); -bad: - if (m != NULL) - m_freem(m); -reclaim: - if (ni != NULL) - ieee80211_free_node(ni); - return error; -#undef senderr + return ic->ic_raw_xmit(ni, m, NULL); } /* @@ -345,50 +542,61 @@ reclaim: * * 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. + * when probing for inactive stations. Like ieee80211_mgmt_output + * we must cleanup any node reference on error; however we + * can safely just unref it as we know it will never be the + * last reference to the node. */ int ieee80211_send_nulldata(struct ieee80211_node *ni) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; struct mbuf *m; struct ieee80211_frame *wh; - MGETHDR(m, M_NOWAIT, MT_DATA); + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + ni, "block %s frame in CAC state", "null data"); + ieee80211_unref_node(&ni); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + m = m_gethdr(M_NOWAIT, MT_HEADER); if (m == NULL) { /* XXX debug msg */ ieee80211_unref_node(&ni); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return ENOMEM; } MH_ALIGN(m, sizeof(struct ieee80211_frame)); - m->m_pkthdr.rcvif = (void *) ni; wh = mtod(m, struct ieee80211_frame *); - ieee80211_send_setup(ic, ni, wh, + ieee80211_send_setup(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 && - ic->ic_opmode != IEEE80211_M_WDS) - wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; - m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); + vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); + if (vap->iv_opmode != IEEE80211_M_WDS) { + /* NB: power management bit is never sent by an AP */ + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + vap->iv_opmode != IEEE80211_M_HOSTAP) + wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; + m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); + } else { + /* NB: 4-address frame */ + m->m_len = m->m_pkthdr.len = + sizeof(struct ieee80211_frame_addr4); + } M_WME_SETAC(m, WME_AC_BE); 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", - ether_sprintf(ni->ni_macaddr), + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, + "send null data frame on channel %u, pwr mgt %s", ieee80211_chan2ieee(ic, ic->ic_curchan), wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); - IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ - if_start(ifp); - - return 0; + return ic->ic_raw_xmit(ni, m, NULL); } /* @@ -398,13 +606,23 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) * applied. */ int -ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) +ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m) { + const struct ether_header *eh = mtod(m, struct ether_header *); int v_wme_ac, d_wme_ac, ac; -#ifdef INET - struct ether_header *eh; -#endif + /* + * Always promote PAE/EAPOL frames to high priority. + */ + if (eh->ether_type == htons(ETHERTYPE_PAE)) { + /* NB: mark so others don't need to check header */ + m->m_flags |= M_EAPOL; + ac = WME_AC_VO; + goto done; + } + /* + * Non-qos traffic goes to BE. + */ if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { ac = WME_AC_BE; goto done; @@ -430,7 +648,6 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod } #ifdef INET - eh = mtod(m, struct ether_header *); if (eh->ether_type == htons(ETHERTYPE_IP)) { uint8_t tos; /* @@ -459,13 +676,15 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod /* * Apply ACM policy. */ - if (ic->ic_opmode == IEEE80211_M_STA) { + if (ni->ni_vap->iv_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 */ }; + struct ieee80211com *ic = ni->ni_ic; + while (ac != WME_AC_BK && ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) ac = acmap[ac]; @@ -482,11 +701,11 @@ done: * and fail rudely if they don't find the space they need. */ static struct mbuf * -ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, +ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, struct ieee80211_key *key, struct mbuf *m) { #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) - int needed_space = ic->ic_headroom + hdrsize; + int needed_space = vap->iv_ic->ic_headroom + hdrsize; if (key != NULL) { /* XXX belongs in crypto code? */ @@ -501,9 +720,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) { m = m_unshare(m, M_NOWAIT); if (m == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: cannot get writable mbuf\n", __func__); - ic->ic_stats.is_tx_nobuf++; /* XXX new stat */ + vap->iv_stats.is_tx_nobuf++; /* XXX new stat */ return NULL; } } @@ -520,9 +739,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, 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, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: cannot expand storage\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; m_freem(m); return NULL; } @@ -564,13 +783,14 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, * we fall back to the default transmit key. */ static __inline struct ieee80211_key * -ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_crypto_getucastkey(struct ieee80211vap *vap, + struct ieee80211_node *ni) { if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { - if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || - IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey])) + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || + IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) return NULL; - return &ic->ic_nw_keys[ic->ic_def_txkey]; + return &vap->iv_nw_keys[vap->iv_def_txkey]; } else { return &ni->ni_ucastkey; } @@ -582,12 +802,13 @@ ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) * the default tx key. */ static __inline struct ieee80211_key * -ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_crypto_getmcastkey(struct ieee80211vap *vap, + struct ieee80211_node *ni) { - if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || - IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey])) + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || + IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) return NULL; - return &ic->ic_nw_keys[ic->ic_def_txkey]; + return &vap->iv_nw_keys[vap->iv_def_txkey]; } /* @@ -595,16 +816,21 @@ ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) * 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. + * + * NB: Packet is assumed to be processed by ieee80211_classify which + * marked EAPOL frames w/ M_EAPOL. */ struct mbuf * -ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni) +ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m) { +#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct ether_header eh; struct ieee80211_frame *wh; struct ieee80211_key *key; struct llc *llc; - int hdrsize, datalen, addqos, txfrag, isff; + int hdrsize, hdrspace, datalen, addqos, txfrag, isff, is4addr; /* * Copy existing Ethernet header to a safe place. The @@ -612,7 +838,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * reorganizing state for the final encapsulation. */ KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); - memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); + ETHER_HEADER_COPY(&eh, mtod(m, caddr_t)); /* * Insure space for additional headers. First identify @@ -626,23 +852,24 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * 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); + if (vap->iv_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_opmode == IEEE80211_M_STA || + !IEEE80211_IS_MULTICAST(eh.ether_dhost) || + (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) + key = ieee80211_crypto_getucastkey(vap, ni); else - key = ieee80211_crypto_getmcastkey(ic, ni); - if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] no default transmit key (%s) deftxkey %u\n", - ether_sprintf(eh.ether_dhost), __func__, - ic->ic_def_txkey); - ic->ic_stats.is_tx_nodefkey++; + key = ieee80211_crypto_getmcastkey(vap, ni); + if (key == NULL && (m->m_flags & M_EAPOL) == 0) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, + eh.ether_dhost, + "no default transmit key (%s) deftxkey %u", + __func__, vap->iv_def_txkey); + vap->iv_stats.is_tx_nodefkey++; goto bad; } } 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 @@ -651,13 +878,31 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * configurable. */ addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && - eh.ether_type != htons(ETHERTYPE_PAE); + (m->m_flags & M_EAPOL) == 0; if (addqos) hdrsize = sizeof(struct ieee80211_qosframe); else hdrsize = sizeof(struct ieee80211_frame); + /* + * 4-address frames need to be generated for: + * o packets sent through a WDS vap (M_WDS || IEEE80211_M_WDS) + * o packets relayed by a station operating with dynamic WDS + * (IEEE80211_M_STA+IEEE80211_F_DWDS and src address) + */ + is4addr = (m->m_flags & M_WDS) || + vap->iv_opmode == IEEE80211_M_WDS || /* XXX redundant? */ + (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_flags & IEEE80211_F_DWDS) && + !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)); + if (is4addr) + hdrsize += IEEE80211_ADDR_LEN; + /* + * Honor driver DATAPAD requirement. + */ if (ic->ic_flags & IEEE80211_F_DATAPAD) - hdrsize = roundup(hdrsize, sizeof(uint32_t)); + hdrspace = roundup(hdrsize, sizeof(uint32_t)); + else + hdrspace = hdrsize; if ((isff = m->m_flags & M_FF) != 0) { struct mbuf *m2; @@ -671,7 +916,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, */ m2 = m->m_nextpkt; if (m2 == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: only one frame\n", __func__); goto bad; } @@ -681,8 +926,8 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * layout; this allocates space according to what * ieee80211_encap_fastframe will do. */ - m = ieee80211_mbuf_adjust(ic, - hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 + + m = ieee80211_mbuf_adjust(vap, + hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 + sizeof(struct ether_header), key, m); if (m == NULL) { @@ -697,22 +942,22 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * at the end of first frame. */ KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); - memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header)); - m2 = ieee80211_mbuf_adjust(ic, + ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t)); + m2 = ieee80211_mbuf_adjust(vap, ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header), NULL, m2); if (m2 == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; } - m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2); + m = ieee80211_encap_fastframe(vap, m, &eh, m2, &eh2); if (m == NULL) goto bad; } else { /* * Normal frame. */ - m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); + m = ieee80211_mbuf_adjust(vap, hdrspace, key, m); if (m == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; @@ -729,15 +974,21 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, } datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ - M_PREPEND(m, hdrsize, M_DONTWAIT); + M_PREPEND(m, hdrspace, M_DONTWAIT); if (m == NULL) { - ic->ic_stats.is_tx_nobuf++; + vap->iv_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; - switch (ic->ic_opmode) { + if (is4addr) { + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); + } else switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); @@ -750,10 +1001,10 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); /* - * NB: always use the bssid from ic_bss as the + * NB: always use the bssid from iv_bss as the * neighbor's may be stale after an ibss merge */ - IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid); break; case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; @@ -762,42 +1013,47 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); break; case IEEE80211_M_MONITOR: - case IEEE80211_M_WDS: + case IEEE80211_M_WDS: /* NB: is4addr should always be true */ goto bad; } 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; + uint8_t *qos; int ac, tid; + if (is4addr) { + qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; + } else + qos = ((struct ieee80211_qosframe *) wh)->i_qos; 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; + qos[0] = tid & IEEE80211_QOS_TID; /* * Check if A-MPDU tx aggregation is setup or if we * should try to enable it. The sta must be associated - * with HT and A-MPDU enabled for use. On the first - * frame that goes out We issue an ADDBA request and - * wait for a reply. The frame being encapsulated - * will go out w/o using A-MPDU, or possibly it might - * be collected by the driver and held/retransmit. - * ieee80211_ampdu_request handles staggering requests - * in case the receiver NAK's us or we are otherwise - * unable to establish a BA stream. + * with HT and A-MPDU enabled for use. When the policy + * routine decides we should enable A-MPDU we issue an + * ADDBA request and wait for a reply. The frame being + * encapsulated will go out w/o using A-MPDU, or possibly + * it might be collected by the driver and held/retransmit. + * The default ic_ampdu_enable routine handles staggering + * ADDBA requests in case the receiver NAK's us or we are + * otherwise unable to establish a BA stream. */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && - (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) { + (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)) { struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; + ieee80211_txampdu_count_packet(tap); if (IEEE80211_AMPDU_RUNNING(tap)) { /* * Operational, mark frame for aggregation. */ - qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA; - } else if (!IEEE80211_AMPDU_REQUESTED(tap)) { + qos[0] |= IEEE80211_QOS_ACKPOLICY_BA; + } else if (!IEEE80211_AMPDU_REQUESTED(tap) && + ic->ic_ampdu_enable(ni, tap)) { /* * Not negotiated yet, request service. */ @@ -806,9 +1062,9 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, } /* XXX works even when BA marked above */ if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) - qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; - qwh->i_qos[1] = 0; - qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; + qos[1] = 0; + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; *(uint16_t *)wh->i_seq = htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); @@ -819,38 +1075,32 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, ni->ni_txseqs[IEEE80211_NONQOS_TID]++; } /* check if xmit fragmentation is required */ - txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold && + txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold && !IEEE80211_IS_MULTICAST(wh->i_addr1) && - (ic->ic_caps & IEEE80211_C_TXFRAG) && + (vap->iv_caps & IEEE80211_C_TXFRAG) && !isff); /* NB: don't fragment ff's */ 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 ? + if ((m->m_flags & M_EAPOL) == 0 || + ((vap->iv_flags & IEEE80211_F_WPA) && + (vap->iv_opmode == IEEE80211_M_STA ? !IEEE80211_KEY_UNDEFINED(key) : !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { wh->i_fc[1] |= IEEE80211_FC1_WEP; - if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, - "[%s] enmic failed, discard frame\n", - ether_sprintf(eh.ether_dhost)); - ic->ic_stats.is_crypto_enmicfail++; + if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, + eh.ether_dhost, + "%s", "enmic failed, discard frame"); + vap->iv_stats.is_crypto_enmicfail++; goto bad; } } } - /* - * NB: frag flags may leak from above; they should only - * be set on return to the caller if we fragment at - * the 802.11 layer. - */ - m->m_flags &= ~(M_FRAG | M_FIRSTFRAG); - if (txfrag && !ieee80211_fragment(ic, m, hdrsize, - key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold)) + if (txfrag && !ieee80211_fragment(vap, m, hdrsize, + key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold)) goto bad; IEEE80211_NODE_STAT(ni, tx_data); @@ -860,11 +1110,16 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_NODE_STAT(ni, tx_ucast); IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); + /* XXX fragmented frames not handled */ + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + return m; bad: if (m != NULL) m_freem(m); return NULL; +#undef WH4 } /* @@ -875,7 +1130,7 @@ bad: * type that specifies the payload size). */ static struct mbuf * -ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, +ieee80211_encap1(struct ieee80211vap *vap, struct mbuf *m, const struct ether_header *eh) { struct llc *llc; @@ -894,9 +1149,9 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); if (m == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for ether_header\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return NULL; } ETHER_HEADER_COPY(mtod(m, void *), eh); @@ -914,7 +1169,7 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, * problem (should not happen). */ static struct mbuf * -ieee80211_encap_fastframe(struct ieee80211com *ic, +ieee80211_encap_fastframe(struct ieee80211vap *vap, struct mbuf *m1, const struct ether_header *eh1, struct mbuf *m2, const struct ether_header *eh2) { @@ -925,12 +1180,12 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, /* * First, each frame gets a standard encapsulation. */ - m1 = ieee80211_encap1(ic, m1, eh1); + m1 = ieee80211_encap1(vap, m1, eh1); if (m1 == NULL) { m_freem(m2); return NULL; } - m2 = ieee80211_encap1(ic, m2, eh2); + m2 = ieee80211_encap1(vap, m2, eh2); if (m2 == NULL) { m_freem(m1); return NULL; @@ -969,18 +1224,18 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, m1->m_pkthdr.len += m2->m_pkthdr.len; M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT); if (m1 == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for tunnel header\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return NULL; } memset(mtod(m1, void *), 0, sizeof(uint32_t)+2); M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT); if (m1 == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for llc header\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return NULL; } llc = mtod(m1, struct llc *); @@ -991,7 +1246,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2; llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE); - ic->ic_stats.is_ff_encap++; + vap->iv_stats.is_ff_encap++; return m1; } @@ -1005,7 +1260,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, * packet's mbufs but that is significantly more complicated. */ static int -ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, +ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, u_int hdrsize, u_int ciphdrsize, u_int mtu) { struct ieee80211_frame *wh, *whf; @@ -1028,6 +1283,7 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, fragsize = totalhdrsize + remainder; if (fragsize > mtu) fragsize = mtu; + /* XXX fragsize can be >2048! */ KASSERT(fragsize < MCLBYTES, ("fragment size %u too big!", fragsize)); if (fragsize > MHLEN) @@ -1073,8 +1329,8 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); m0->m_flags |= M_FIRSTFRAG | M_FRAG; - ic->ic_stats.is_tx_fragframes++; - ic->ic_stats.is_tx_frags += fragno-1; + vap->iv_stats.is_tx_fragframes++; + vap->iv_stats.is_tx_frags += fragno-1; return 1; bad: @@ -1125,7 +1381,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) } /* - * Add an ssid elemet to a frame. + * Add an ssid element to a frame. */ static uint8_t * ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) @@ -1157,188 +1413,39 @@ ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) return frm; } +/* + * Add a CFParams element to a frame. + */ static uint8_t * -ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie) +ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) { -#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<<IEEE80211_CIPHER_AES_CCM)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); - } - if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); - } - - /* authenticator selector list */ - selcnt = frm; - ADDSHORT(frm, 0); /* selector count */ - if (rsn->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))); + *frm++ = IEEE80211_ELEMID_CFPARMS; + *frm++ = 6; + *frm++ = 0; /* CFP count */ + *frm++ = 2; /* CFP period */ + ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */ + ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */ return frm; #undef ADDSHORT -#undef ADDSELECTOR -#undef WPA_OUI_BYTES } -static uint8_t * -ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie) +static __inline uint8_t * +add_appie(uint8_t *frm, const struct ieee80211_appie *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<<IEEE80211_CIPHER_AES_CCM)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); - } - if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); - } - - /* authenticator selector list */ - selcnt = frm; - ADDSHORT(frm, 0); /* selector count */ - if (rsn->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 + memcpy(frm, ie->ie_data, ie->ie_len); + return frm + ie->ie_len; } -/* - * Add a WPA/RSN element to a frame. - */ -static uint8_t * -ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic) +static __inline uint8_t * +add_ie(uint8_t *frm, const uint8_t *ie) { - - 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; + memcpy(frm, ie, 2 + ie[1]); + return frm + 2 + ie[1]; } #define WME_OUI_BYTES 0x00, 0x50, 0xf2 @@ -1432,6 +1539,94 @@ ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix) #undef ATH_OUI_BYTES /* + * Add an 11h Power Constraint element to a frame. + */ +static uint8_t * +ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap) +{ + const struct ieee80211_channel *c = vap->iv_bss->ni_chan; + /* XXX per-vap tx power limit? */ + int8_t limit = vap->iv_ic->ic_txpowlimit / 2; + + frm[0] = IEEE80211_ELEMID_PWRCNSTR; + frm[1] = 1; + frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0; + return frm + 3; +} + +/* + * Add an 11h Power Capability element to a frame. + */ +static uint8_t * +ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c) +{ + frm[0] = IEEE80211_ELEMID_PWRCAP; + frm[1] = 2; + frm[2] = c->ic_minpower; + frm[3] = c->ic_maxpower; + return frm + 4; +} + +/* + * Add an 11h Supported Channels element to a frame. + */ +static uint8_t * +ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) +{ + static const int ielen = 26; + + frm[0] = IEEE80211_ELEMID_SUPPCHAN; + frm[1] = ielen; + /* XXX not correct */ + memcpy(frm+2, ic->ic_chan_avail, ielen); + return frm + 2 + ielen; +} + +/* + * Add an 11h Channel Switch Announcement element to a frame. + * Note that we use the per-vap CSA count to adjust the global + * counter so we can use this routine to form probe response + * frames and get the current count. + */ +static uint8_t * +ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm; + + csa->csa_ie = IEEE80211_ELEMID_CHANSWITCHANN; + csa->csa_len = 3; + csa->csa_mode = 1; /* XXX force quiet on channel */ + csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan); + csa->csa_count = ic->ic_csa_count - vap->iv_csa_count; + return frm + sizeof(*csa); +} + +/* + * Add an 11h country information element to a frame. + */ +static uint8_t * +ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) +{ + + if (ic->ic_countryie == NULL || + ic->ic_countryie_chan != ic->ic_bsschan) { + /* + * Handle lazy construction of ie. This is done on + * first use and after a channel change that requires + * re-calculation. + */ + if (ic->ic_countryie != NULL) + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = ieee80211_alloc_countryie(ic); + if (ic->ic_countryie == NULL) + return frm; + ic->ic_countryie_chan = ic->ic_bsschan; + } + return add_appie(frm, ic->ic_countryie); +} + +/* * Send a probe request frame with the specified ssid * and any optional information element data. */ @@ -1440,21 +1635,28 @@ 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) + const uint8_t *ssid, size_t ssidlen) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; const struct ieee80211_rateset *rs; struct mbuf *m; uint8_t *frm; + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, + "block %s frame in CAC state", "probe request"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + /* * 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_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), @@ -1465,18 +1667,23 @@ ieee80211_send_probereq(struct ieee80211_node *ni, * prreq frame format * [tlv] ssid * [tlv] supported rates + * [tlv] RSN (optional) * [tlv] extended supported rates + * [tlv] WPA (optional) * [tlv] user-specified ie's */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), - 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + + sizeof(struct ieee80211_ie_wpa) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - + (optie != NULL ? optielen : 0) + + sizeof(struct ieee80211_ie_wpa) + + (vap->iv_appie_probereq != NULL ? + vap->iv_appie_probereq->ie_len : 0) ); if (m == NULL) { - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); return ENOMEM; } @@ -1484,62 +1691,70 @@ ieee80211_send_probereq(struct ieee80211_node *ni, frm = ieee80211_add_ssid(frm, ssid, ssidlen); rs = ieee80211_get_suprates(ic, ic->ic_curchan); frm = ieee80211_add_rates(frm, rs); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } frm = ieee80211_add_xrates(frm, rs); - - if (optie != NULL) { - memcpy(frm, optie, optielen); - frm += optielen; + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain? */ } + if (vap->iv_appie_probereq != NULL) + frm = add_appie(frm, vap->iv_appie_probereq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_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_send_setup(ni, wh, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, sa, da, bssid); /* XXX power management? */ + M_WME_SETAC(m, WME_AC_BE); + IEEE80211_NODE_STAT(ni, tx_probereq); IEEE80211_NODE_STAT(ni, tx_mgmt); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, - "[%s] send probe req on channel %u\n", - ether_sprintf(wh->i_addr1), - ieee80211_chan2ieee(ic, ic->ic_curchan)); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "send probe req on channel %u bssid %s ssid \"%.*s\"\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), + ssidlen, ssid); - IF_ENQUEUE(&ic->ic_mgtq, m); - if_start(ic->ic_ifp); - return 0; + return ic->ic_raw_xmit(ni, m, NULL); } /* * Calculate capability information for mgt frames. */ static uint16_t -getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) +getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) { + struct ieee80211com *ic = vap->iv_ic; uint16_t capinfo; - KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode")); + KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode")); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) + if (vap->iv_opmode == IEEE80211_M_HOSTAP) capinfo = IEEE80211_CAPINFO_ESS; - else if (ic->ic_opmode == IEEE80211_M_IBSS) + else if (vap->iv_opmode == IEEE80211_M_IBSS) capinfo = IEEE80211_CAPINFO_IBSS; else capinfo = 0; - if (ic->ic_flags & IEEE80211_F_PRIVACY) + if (vap->iv_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; + if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) + capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; return capinfo; } @@ -1549,12 +1764,13 @@ getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) * count bumped to reflect our use for an indeterminant time. */ int -ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int arg) +ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) { #define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT) -#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) - const struct ieee80211_rateset *rs; +#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node *bss = vap->iv_bss; struct mbuf *m; uint8_t *frm; uint16_t capinfo; @@ -1567,7 +1783,7 @@ 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. */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), @@ -1575,112 +1791,6 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, ieee80211_ref_node(ni); switch (type) { - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - /* - * probe response frame format - * [8] time stamp - * [2] beacon interval - * [2] cabability information - * [tlv] ssid - * [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) - * [tlv] HT capabilities - * [tlv] HT information - * [tlv] Vendor OUI HT capabilities (optional) - * [tlv] Vendor OUI HT information (optional) - * [tlv] Atheros capabilities - */ - m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), - 8 - + sizeof(uint16_t) - + sizeof(uint16_t) - + 2 + IEEE80211_NWID_LEN - + 2 + IEEE80211_RATE_SIZE - + 7 /* max(7,3) */ - + 6 - + 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) - /* XXX check for cluster requirement */ - + 2*sizeof(struct ieee80211_ie_htcap) + 4 - + 2*sizeof(struct ieee80211_ie_htinfo) + 4 - + sizeof(struct ieee80211_ath_ie) - ); - if (m == NULL) - 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; - 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); - rs = ieee80211_get_suprates(ic, ic->ic_curchan); - frm = ieee80211_add_rates(frm, rs); - - if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) { - *frm++ = IEEE80211_ELEMID_FHPARMS; - *frm++ = 5; - *frm++ = ni->ni_fhdwell & 0x00ff; - *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; - *frm++ = IEEE80211_FH_CHANSET( - ieee80211_chan2ieee(ic, ic->ic_curchan)); - *frm++ = IEEE80211_FH_CHANPAT( - ieee80211_chan2ieee(ic, ic->ic_curchan)); - *frm++ = ni->ni_fhindex; - } else { - *frm++ = IEEE80211_ELEMID_DSPARMS; - *frm++ = 1; - *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 */ - } - if (ic->ic_flags & IEEE80211_F_WPA) - frm = ieee80211_add_wpa(frm, ic); - if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) - frm = ieee80211_add_erp(frm, ic); - frm = ieee80211_add_xrates(frm, rs); - /* - * NB: legacy 11b clients do not get certain ie's. - * The caller identifies such clients by passing - * a token in arg to us. Could expand this to be - * any legacy client for stuff like HT ie's. - */ - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && - arg != IEEE80211_SEND_LEGACY_11B) { - frm = ieee80211_add_htcap(frm, ni); - frm = ieee80211_add_htinfo(frm, ni); - } - if (ic->ic_flags & IEEE80211_F_WME) - frm = ieee80211_add_wme_param(frm, &ic->ic_wme); - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && - (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) && - arg != IEEE80211_SEND_LEGACY_11B) { - frm = ieee80211_add_htcap_vendor(frm, ni); - frm = ieee80211_add_htinfo_vendor(frm, ni); - } - if (ni->ni_ath_ie != NULL) - frm = ieee80211_add_ath(frm, ni->ni_ath_flags, - ni->ni_ath_defkeyix); - m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); - break; case IEEE80211_FC0_SUBTYPE_AUTH: status = arg >> 16; @@ -1699,10 +1809,10 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, is_shared_key = has_challenge || arg >= IEEE80211_AUTH_SHARED_RESPONSE || (arg == IEEE80211_AUTH_SHARED_REQUEST && - ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + bss->ni_authmode == IEEE80211_AUTH_SHARED); m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), + ic->ic_headroom + sizeof(struct ieee80211_frame), 3 * sizeof(uint16_t) + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) @@ -1725,9 +1835,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, 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, - "[%s] request encrypt frame (%s)\n", - ether_sprintf(ni->ni_macaddr), __func__); + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "request encrypt frame (%s)", __func__); m->m_flags |= M_LINK0; /* WEP-encrypt, please */ } } else @@ -1739,15 +1848,14 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, else IEEE80211_NODE_STAT(ni, tx_auth_fail); - if (ic->ic_opmode == IEEE80211_M_STA) + if (vap->iv_opmode == IEEE80211_M_STA) ieee80211_add_callback(m, ieee80211_tx_mgt_cb, - (void *) ic->ic_state); + (void *) vap->iv_state); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] send station deauthenticate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), arg); + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "send station deauthenticate (reason %d)", arg); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); @@ -1772,11 +1880,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates - * [tlv] WME + * [4] power capability (optional) + * [28] supported channels (optional) * [tlv] HT capabilities + * [tlv] WME (optional) * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Atheros capabilities (if negotiated) - * [tlv] user-specified ie's + * [tlv] AppIE's (optional) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), @@ -1786,18 +1896,24 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + 4 + + 2 + 26 + sizeof(struct ieee80211_wme_info) - + 2*sizeof(struct ieee80211_ie_htcap) + 4 + + sizeof(struct ieee80211_ie_htcap) + + 4 + sizeof(struct ieee80211_ie_htcap) + sizeof(struct ieee80211_ath_ie) - + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) + + (vap->iv_appie_wpa != NULL ? + vap->iv_appie_wpa->ie_len : 0) + + (vap->iv_appie_assocreq != NULL ? + vap->iv_appie_assocreq->ie_len : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - KASSERT(ic->ic_opmode == IEEE80211_M_STA, - ("wrong mode %u", ic->ic_opmode)); + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_PRIVACY) + if (vap->iv_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; /* * NB: Some 11a AP's reject the request when @@ -1810,50 +1926,63 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, (ic->ic_caps & IEEE80211_C_SHSLOT)) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) && - (ic->ic_flags & IEEE80211_F_DOTH)) + (vap->iv_flags & IEEE80211_F_DOTH)) capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; *(uint16_t *)frm = htole16(capinfo); frm += 2; - KASSERT(ic->ic_bss->ni_intval != 0, - ("beacon interval is zero!")); + KASSERT(bss->ni_intval != 0, ("beacon interval is zero!")); *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, - ic->ic_bss->ni_intval)); + bss->ni_intval)); frm += 2; if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { - IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); + IEEE80211_ADDR_COPY(frm, bss->ni_bssid); frm += IEEE80211_ADDR_LEN; } frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } frm = ieee80211_add_xrates(frm, &ni->ni_rates); - if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) && - ni->ni_htcap_ie != NULL && - ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP) + if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { + frm = ieee80211_add_powercapability(frm, + ic->ic_curchan); + frm = ieee80211_add_supportedchannels(frm, ic); + } + if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) && + ni->ni_ies.htcap_ie != NULL && + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) frm = ieee80211_add_htcap(frm, ni); - if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain */ + } + if ((ic->ic_flags & IEEE80211_F_WME) && + ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); - if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) && - ni->ni_htcap_ie != NULL && - ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR) + if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) && + ni->ni_ies.htcap_ie != NULL && + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) frm = ieee80211_add_htcap_vendor(frm, ni); - if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) frm = ieee80211_add_ath(frm, - IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), - (ic->ic_flags & IEEE80211_F_WPA) == 0 && + IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), + (vap->iv_flags & IEEE80211_F_WPA) == 0 && ni->ni_authmode != IEEE80211_AUTH_8021X && - ic->ic_def_txkey != IEEE80211_KEYIX_NONE ? - ic->ic_def_txkey : 0x7fff); - if (ic->ic_opt_ie != NULL) { - memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); - frm += ic->ic_opt_ie_len; - } + vap->iv_def_txkey != IEEE80211_KEYIX_NONE ? + vap->iv_def_txkey : 0x7fff); + if (vap->iv_appie_assocreq != NULL) + frm = add_appie(frm, vap->iv_appie_assocreq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); ieee80211_add_callback(m, ieee80211_tx_mgt_cb, - (void *) ic->ic_state); + (void *) vap->iv_state); break; case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: @@ -1865,10 +1994,13 @@ 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) - * [tlv] HT capabilities (standard or vendor OUI) - * [tlv] HT information (standard or vendor OUI) - * [tlv] Atheros capabilities (if enabled and STA enabled) + * [tlv] HT capabilities (standard, if STA enabled) + * [tlv] HT information (standard, if STA enabled) + * [tlv] WME (if configured and STA enabled) + * [tlv] HT capabilities (vendor OUI, if STA enabled) + * [tlv] HT information (vendor OUI, if STA enabled) + * [tlv] Atheros capabilities (if STA enabled) + * [tlv] AppIE's (optional) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), @@ -1877,15 +2009,17 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + sizeof(uint16_t) + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - + sizeof(struct ieee80211_wme_param) + sizeof(struct ieee80211_ie_htcap) + 4 + sizeof(struct ieee80211_ie_htinfo) + 4 + + sizeof(struct ieee80211_wme_param) + sizeof(struct ieee80211_ath_ie) + + (vap->iv_appie_assocresp != NULL ? + vap->iv_appie_assocresp->ie_len : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - capinfo = getcapinfo(ic, ic->ic_curchan); + capinfo = getcapinfo(vap, bss->ni_chan); *(uint16_t *)frm = htole16(capinfo); frm += 2; @@ -1906,23 +2040,25 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_htcap(frm, ni); frm = ieee80211_add_htinfo(frm, ni); } - if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + if ((vap->iv_flags & IEEE80211_F_WME) && + ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } - if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) frm = ieee80211_add_ath(frm, - IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), + IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), ni->ni_ath_defkeyix); + if (vap->iv_appie_assocresp != NULL) + frm = add_appie(frm, vap->iv_appie_assocresp); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] send station disassociate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), arg); + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "send station disassociate (reason %d)", arg); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); @@ -1936,17 +2072,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, break; default: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] invalid mgmt frame type %u\n", - ether_sprintf(ni->ni_macaddr), type); + IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, + "invalid mgmt frame type %u", type); senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } - ret = ieee80211_mgmt_output(ic, ni, m, type); - if (ret != 0) - goto bad; - return 0; + return ieee80211_mgmt_output(ni, m, type); bad: ieee80211_free_node(ni); return ret; @@ -1954,19 +2086,283 @@ bad: #undef HTFLAGS } +/* + * Return an mbuf with a probe response frame in it. + * Space is left to prepend and 802.11 header at the + * front but it's left to the caller to fill in. + */ +struct mbuf * +ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) +{ + struct ieee80211vap *vap = bss->ni_vap; + struct ieee80211com *ic = bss->ni_ic; + const struct ieee80211_rateset *rs; + struct mbuf *m; + uint16_t capinfo; + uint8_t *frm; + + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (FH/DS) + * [tlv] parameter set (IBSS) + * [tlv] country (optional) + * [tlv] RSN (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] WPA (optional) + * [tlv] WME (optional) + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * [tlv] Atheros capabilities + * [tlv] AppIE's (optional) + */ + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + 8 + + sizeof(uint16_t) + + sizeof(uint16_t) + + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 7 /* max(7,3) */ + + IEEE80211_COUNTRY_MAX_SIZE + + sizeof(struct ieee80211_ie_wpa) + + 3 + + sizeof(struct ieee80211_csa_ie) + + 3 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_ie_htcap) + + sizeof(struct ieee80211_ie_htinfo) + + sizeof(struct ieee80211_ie_wpa) + + sizeof(struct ieee80211_wme_param) + + 4 + sizeof(struct ieee80211_ie_htcap) + + 4 + sizeof(struct ieee80211_ie_htinfo) + + sizeof(struct ieee80211_ath_ie) + + (vap->iv_appie_proberesp != NULL ? + vap->iv_appie_proberesp->ie_len : 0) + ); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + + memset(frm, 0, 8); /* timestamp should be filled later */ + frm += 8; + *(uint16_t *)frm = htole16(bss->ni_intval); + frm += 2; + capinfo = getcapinfo(vap, bss->ni_chan); + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen); + rs = ieee80211_get_suprates(ic, bss->ni_chan); + frm = ieee80211_add_rates(frm, rs); + + if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) { + *frm++ = IEEE80211_ELEMID_FHPARMS; + *frm++ = 5; + *frm++ = bss->ni_fhdwell & 0x00ff; + *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff; + *frm++ = IEEE80211_FH_CHANSET( + ieee80211_chan2ieee(ic, bss->ni_chan)); + *frm++ = IEEE80211_FH_CHANPAT( + ieee80211_chan2ieee(ic, bss->ni_chan)); + *frm++ = bss->ni_fhindex; + } else { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan); + } + + if (vap->iv_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + } + if ((vap->iv_flags & IEEE80211_F_DOTH) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) + frm = ieee80211_add_countryie(frm, ic); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan)) + frm = ieee80211_add_powerconstraint(frm, vap); + if (ic->ic_flags & IEEE80211_F_CSAPENDING) + frm = ieee80211_add_csa(frm, vap); + } + if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) + frm = ieee80211_add_erp(frm, ic); + frm = ieee80211_add_xrates(frm, rs); + /* + * NB: legacy 11b clients do not get certain ie's. + * The caller identifies such clients by passing + * a token in legacy to us. Could expand this to be + * any legacy client for stuff like HT ie's. + */ + if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && + legacy != IEEE80211_SEND_LEGACY_11B) { + frm = ieee80211_add_htcap(frm, bss); + frm = ieee80211_add_htinfo(frm, bss); + } + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain? */ + } + if (vap->iv_flags & IEEE80211_F_WME) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && + (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) && + legacy != IEEE80211_SEND_LEGACY_11B) { + frm = ieee80211_add_htcap_vendor(frm, bss); + frm = ieee80211_add_htinfo_vendor(frm, bss); + } + if (bss->ni_ies.ath_ie != NULL && legacy != IEEE80211_SEND_LEGACY_11B) + frm = ieee80211_add_ath(frm, bss->ni_ath_flags, + bss->ni_ath_defkeyix); + if (vap->iv_appie_proberesp != NULL) + frm = add_appie(frm, vap->iv_appie_proberesp); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + return m; +} + +/* + * Send a probe response frame to the specified mac address. + * This does not go through the normal mgt frame api so we + * can specify the destination address and re-use the bss node + * for the sta reference. + */ +int +ieee80211_send_proberesp(struct ieee80211vap *vap, + const uint8_t da[IEEE80211_ADDR_LEN], int legacy) +{ + struct ieee80211_node *bss = vap->iv_bss; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_frame *wh; + struct mbuf *m; + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, + "block %s frame in CAC state", "probe response"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + /* + * 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(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr), + ieee80211_node_refcnt(bss)+1); + ieee80211_ref_node(bss); + + m = ieee80211_alloc_proberesp(bss, legacy); + if (m == NULL) { + ieee80211_free_node(bss); + return ENOMEM; + } + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + KASSERT(m != NULL, ("no room for header")); + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(bss, wh, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, + vap->iv_myaddr, da, bss->ni_bssid); + /* XXX power management? */ + + M_WME_SETAC(m, WME_AC_BE); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "send probe resp on channel %u to %s%s\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da), + legacy ? " <legacy>" : ""); + IEEE80211_NODE_STAT(bss, tx_mgmt); + + return ic->ic_raw_xmit(bss, m, NULL); +} + +/* + * Allocate and build a RTS (Request To Send) control frame. + */ +struct mbuf * +ieee80211_alloc_rts(struct ieee80211com *ic, + const uint8_t ra[IEEE80211_ADDR_LEN], + const uint8_t ta[IEEE80211_ADDR_LEN], + uint16_t dur) +{ + struct ieee80211_frame_rts *rts; + struct mbuf *m; + + /* XXX honor ic_headroom */ + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m != NULL) { + rts = mtod(m, struct ieee80211_frame_rts *); + rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS; + rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)rts->i_dur = htole16(dur); + IEEE80211_ADDR_COPY(rts->i_ra, ra); + IEEE80211_ADDR_COPY(rts->i_ta, ta); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); + } + return m; +} + +/* + * Allocate and build a CTS (Clear To Send) control frame. + */ +struct mbuf * +ieee80211_alloc_cts(struct ieee80211com *ic, + const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur) +{ + struct ieee80211_frame_cts *cts; + struct mbuf *m; + + /* XXX honor ic_headroom */ + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m != NULL) { + cts = mtod(m, struct ieee80211_frame_cts *); + cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS; + cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)cts->i_dur = htole16(dur); + IEEE80211_ADDR_COPY(cts->i_ra, ra); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); + } + return m; +} + static void ieee80211_tx_mgt_timeout(void *arg) { struct ieee80211_node *ni = arg; - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; - if (ic->ic_state != IEEE80211_S_INIT && - (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + if (vap->iv_state != IEEE80211_S_INIT && + (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* * NB: it's safe to specify a timeout as the reason here; * it'll only be used in the right state. */ - ieee80211_new_state(ic, IEEE80211_S_SCAN, + ieee80211_new_state(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT); } } @@ -1974,7 +2370,7 @@ ieee80211_tx_mgt_timeout(void *arg) static void ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; enum ieee80211_state ostate = (enum ieee80211_state) arg; /* @@ -1988,27 +2384,20 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) * * XXX what happens if !acked but response shows up before callback? */ - if (ic->ic_state == ostate) - callout_reset(&ic->ic_mgtsend, + if (vap->iv_state == ostate) + callout_reset(&vap->iv_mgtsend, status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, ieee80211_tx_mgt_timeout, ni); } -/* - * Allocate a beacon frame and fillin the appropriate bits. - */ -struct mbuf * -ieee80211_beacon_alloc(struct ieee80211_node *ni, - struct ieee80211_beacon_offsets *bo) +static void +ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, + struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211_frame *wh; - struct mbuf *m; - int pktlen; - uint8_t *frm; + struct ieee80211_rateset *rs = &ni->ni_rates; uint16_t capinfo; - struct ieee80211_rateset *rs; /* * beacon frame format @@ -2018,46 +2407,23 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [3] parameter set (DS) + * [8] CF parameter set (optional) * [tlv] parameter set (IBSS/TIM) - * [tlv] country code + * [tlv] country (optional) + * [tlv] RSN parameters + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) * [tlv] extended rate phy (ERP) * [tlv] extended supported rates - * [tlv] WME parameters - * [tlv] WPA/RSN parameters * [tlv] HT capabilities * [tlv] HT information + * XXX Vendor-specific OIDs (e.g. Atheros) + * [tlv] WPA parameters + * [tlv] WME parameters * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Vendor OUI HT information (optional) - * XXX Vendor-specific OIDs (e.g. Atheros) - * NB: we allocate the max space required for the TIM bitmap. + * [tlv] application data (optional) */ - 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 */ - + sizeof(struct ieee80211_country_ie) /* country code */ - + 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) - /* XXX conditional? */ - + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ - + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ - ; - m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), 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(bo, 0, sizeof(*bo)); @@ -2065,68 +2431,169 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, frm += 8; *(uint16_t *)frm = htole16(ni->ni_intval); frm += 2; - capinfo = getcapinfo(ic, ni->ni_chan); + capinfo = getcapinfo(vap, 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) { + if ((vap->iv_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 (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) { + if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) { *frm++ = IEEE80211_ELEMID_DSPARMS; *frm++ = 1; - *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + if (ic->ic_flags & IEEE80211_F_PCF) { + bo->bo_cfp = frm; + frm = ieee80211_add_cfparms(frm, ic); } bo->bo_tim = frm; - if (ic->ic_opmode == IEEE80211_M_IBSS) { + if (vap->iv_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) { + } else if (vap->iv_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_period = vap->iv_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_tim_trailer = frm; - if (ic->ic_flags & IEEE80211_F_DOTH) - frm = ieee80211_add_countryie(frm, ic, - ic->ic_countrycode, ic->ic_location); - if (ic->ic_flags & IEEE80211_F_WPA) - frm = ieee80211_add_wpa(frm, ic); - if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { + if ((vap->iv_flags & IEEE80211_F_DOTH) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) + frm = ieee80211_add_countryie(frm, ic); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain */ + } + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) + frm = ieee80211_add_powerconstraint(frm, vap); + bo->bo_csa = frm; + if (ic->ic_flags & IEEE80211_F_CSAPENDING) + frm = ieee80211_add_csa(frm, vap); + } else + bo->bo_csa = frm; + if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { bo->bo_erp = frm; frm = ieee80211_add_erp(frm, ic); } frm = ieee80211_add_xrates(frm, rs); - if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) { + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { frm = ieee80211_add_htcap(frm, ni); bo->bo_htinfo = frm; frm = ieee80211_add_htinfo(frm, ni); } - if (ic->ic_flags & IEEE80211_F_WME) { + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain */ + } + if (vap->iv_flags & IEEE80211_F_WME) { bo->bo_wme = frm; frm = ieee80211_add_wme_param(frm, &ic->ic_wme); } - if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) && - (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) { + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && + (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT)) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } + if (vap->iv_appie_beacon != NULL) { + bo->bo_appie = frm; + bo->bo_appie_len = vap->iv_appie_beacon->ie_len; + frm = add_appie(frm, vap->iv_appie_beacon); + } bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; + bo->bo_csa_trailer_len = frm - bo->bo_csa; m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); +} + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +struct mbuf * +ieee80211_beacon_alloc(struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct mbuf *m; + int pktlen; + uint8_t *frm; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [8] CF parameter set (optional) + * [tlv] parameter set (IBSS/TIM) + * [tlv] country (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] RSN parameters + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * XXX Vendor-specific OIDs (e.g. Atheros) + * [tlv] WPA parameters + * [tlv] WME parameters + * [tlv] application data (optional) + * NB: we allocate the max space required for the TIM bitmap. + * XXX how big is this? + */ + 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 + 6 /* CF parameters */ + + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ + + IEEE80211_COUNTRY_MAX_SIZE /* country */ + + 2 + 1 /* power control */ + + sizeof(struct ieee80211_csa_ie) /* CSA */ + + 2 + 1 /* ERP */ + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ + 2*sizeof(struct ieee80211_ie_wpa) : 0) + /* XXX conditional? */ + + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ + + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ + + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ + sizeof(struct ieee80211_wme_param) : 0) + + IEEE80211_MAX_APPIE + ; + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); + if (m == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "%s: cannot get buf; size %u\n", __func__, pktlen); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + ieee80211_beacon_construct(m, frm, bo, ni); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); KASSERT(m != NULL, ("no space for 802.11 header?")); @@ -2136,7 +2603,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, 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_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); *(uint16_t *)wh->i_seq = 0; @@ -2150,16 +2617,46 @@ int ieee80211_beacon_update(struct ieee80211_node *ni, struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int len_changed = 0; uint16_t capinfo; - IEEE80211_BEACON_LOCK(ic); + IEEE80211_LOCK(ic); + /* + * Handle 11h channel change when we've reached the count. + * We must recalculate the beacon frame contents to account + * for the new channel. Note we do this only for the first + * vap that reaches this point; subsequent vaps just update + * their beacon state to reflect the recalculated channel. + */ + if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) && + vap->iv_csa_count == ic->ic_csa_count) { + vap->iv_csa_count = 0; + /* + * Effect channel change before reconstructing the beacon + * frame contents as many places reference ni_chan. + */ + if (ic->ic_csa_newchan != NULL) + ieee80211_csa_completeswitch(ic); + /* + * NB: ieee80211_beacon_construct clears all pending + * updates in bo_flags so we don't need to explicitly + * clear IEEE80211_BEACON_CSA. + */ + ieee80211_beacon_construct(m, + mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni); + + /* XXX do WME aggressive mode processing? */ + IEEE80211_UNLOCK(ic); + return 1; /* just assume length changed */ + } + /* XXX faster to recalculate entirely or just changes? */ - capinfo = getcapinfo(ic, ni->ni_chan); + capinfo = getcapinfo(vap, ni->ni_chan); *bo->bo_caps = htole16(capinfo); - if (ic->ic_flags & IEEE80211_F_WME) { + if (vap->iv_flags & IEEE80211_F_WME) { struct ieee80211_wme_state *wme = &ic->ic_wme; /* @@ -2172,11 +2669,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, if (wme->wme_flags & WME_F_AGGRMODE) { if (wme->wme_hipri_traffic > wme->wme_hipri_switch_thresh) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: traffic %u, disable aggressive mode\n", __func__, wme->wme_hipri_traffic); wme->wme_flags &= ~WME_F_AGGRMODE; - ieee80211_wme_updateparams_locked(ic); + ieee80211_wme_updateparams_locked(vap); wme->wme_hipri_traffic = wme->wme_hipri_switch_hysteresis; } else @@ -2184,11 +2681,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, } else { if (wme->wme_hipri_traffic <= wme->wme_hipri_switch_thresh) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: traffic %u, enable aggressive mode\n", __func__, wme->wme_hipri_traffic); wme->wme_flags |= WME_F_AGGRMODE; - ieee80211_wme_updateparams_locked(ic); + ieee80211_wme_updateparams_locked(vap); wme->wme_hipri_traffic = 0; } else wme->wme_hipri_traffic = @@ -2200,12 +2697,12 @@ ieee80211_beacon_update(struct ieee80211_node *ni, } } - if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { - ieee80211_ht_update_beacon(ic, bo); + if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { + ieee80211_ht_update_beacon(vap, bo); clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); } - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) bo->bo_tim; if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { @@ -2217,7 +2714,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, * 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). + * maximal-size virtual bitmap (based on iv_max_aid). */ /* * Calculate the bitmap size and offset, copy any @@ -2226,16 +2723,16 @@ ieee80211_beacon_update(struct ieee80211_node *ni, * Note that the tim bitmap must contain at least * one byte and any offset must be even. */ - if (ic->ic_ps_pending != 0) { + if (vap->iv_ps_pending != 0) { timoff = 128; /* impossibly large */ - for (i = 0; i < ic->ic_tim_len; i++) - if (ic->ic_tim_bitmap[i]) { + for (i = 0; i < vap->iv_tim_len; i++) + if (vap->iv_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]) + for (i = vap->iv_tim_len-1; i >= timoff; i--) + if (vap->iv_tim_bitmap[i]) break; timlen = 1 + (i - timoff); } else { @@ -2250,9 +2747,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, bo->bo_tim_trailer+adjust, bo->bo_tim_trailer_len); bo->bo_tim_trailer += adjust; - bo->bo_wme += adjust; bo->bo_erp += adjust; bo->bo_htinfo += adjust; + bo->bo_appie += adjust; + bo->bo_wme += adjust; + bo->bo_csa += adjust; bo->bo_tim_len = timlen; /* update information element */ @@ -2260,14 +2759,14 @@ ieee80211_beacon_update(struct ieee80211_node *ni, tie->tim_bitctl = timoff; len_changed = 1; } - memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff, + memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff, bo->bo_tim_len); clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s: TIM updated, pending %u, off %u, len %u\n", - __func__, ic->ic_ps_pending, timoff, timlen); + __func__, vap->iv_ps_pending, timoff, timlen); } /* count down DTIM period */ if (tie->tim_count == 0) @@ -2279,6 +2778,33 @@ ieee80211_beacon_update(struct ieee80211_node *ni, tie->tim_bitctl |= 1; else tie->tim_bitctl &= ~1; + if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) { + struct ieee80211_csa_ie *csa = + (struct ieee80211_csa_ie *) bo->bo_csa; + + /* + * Insert or update CSA ie. If we're just starting + * to count down to the channel switch then we need + * to insert the CSA ie. Otherwise we just need to + * drop the count. The actual change happens above + * when the vap's count reaches the target count. + */ + if (vap->iv_csa_count == 0) { + memmove(&csa[1], csa, bo->bo_csa_trailer_len); + bo->bo_erp += sizeof(*csa); + bo->bo_wme += sizeof(*csa); + bo->bo_appie += sizeof(*csa); + bo->bo_csa_trailer_len += sizeof(*csa); + bo->bo_tim_trailer_len += sizeof(*csa); + m->m_len += sizeof(*csa); + m->m_pkthdr.len += sizeof(*csa); + + ieee80211_add_csa(bo->bo_csa, vap); + } else + csa->csa_count--; + vap->iv_csa_count++; + /* NB: don't clear IEEE80211_BEACON_CSA */ + } if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { /* * ERP element needs updating. @@ -2287,7 +2813,31 @@ ieee80211_beacon_update(struct ieee80211_node *ni, clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); } } - IEEE80211_BEACON_UNLOCK(ic); + if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) { + const struct ieee80211_appie *aie = vap->iv_appie_beacon; + int aielen; + uint8_t *frm; + + aielen = 0; + if (aie != NULL) + aielen += aie->ie_len; + if (aielen != bo->bo_appie_len) { + /* copy up/down trailer */ + int adjust = aielen - bo->bo_appie_len; + ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, + bo->bo_tim_trailer_len); + bo->bo_tim_trailer += adjust; + bo->bo_appie += adjust; + bo->bo_appie_len = aielen; + + len_changed = 1; + } + frm = bo->bo_appie; + if (aie != NULL) + frm = add_appie(frm, aie); + clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE); + } + IEEE80211_UNLOCK(ic); return len_changed; } diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c new file mode 100644 index 0000000..e1c847f --- /dev/null +++ b/sys/net80211/ieee80211_phy.c @@ -0,0 +1,472 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 PHY-related support. + */ + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_phy.h> + +#ifdef notyet +struct ieee80211_ds_plcp_hdr { + uint8_t i_signal; + uint8_t i_service; + uint16_t i_length; + uint16_t i_crc; +} __packed; + +#endif /* notyet */ + +/* shorthands to compact tables for readability */ +#define OFDM IEEE80211_T_OFDM +#define CCK IEEE80211_T_CCK +#define TURBO IEEE80211_T_TURBO +#define PBCC (IEEE80211_T_HT+1) /* XXX */ + +static struct ieee80211_rate_table ieee80211_11b_table = { + 4, /* number of rates, XXX no PBCC */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 }, +/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 }, +/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 1 }, +/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 1 }, +/* 22 Mb */ { PBCC, 22000, 0x04, 44, 3 } + }, +}; + + +static struct ieee80211_rate_table ieee80211_11g_table = { + 12, /* number of rates */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 }, +/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 }, +/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 2 }, +/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 3 }, +/* 6 Mb */ { OFDM, 6000, 0x00, 12, 4 }, +/* 9 Mb */ { OFDM, 9000, 0x00, 18, 4 }, +/* 12 Mb */ { OFDM, 12000, 0x00, 24, 6 }, +/* 18 Mb */ { OFDM, 18000, 0x00, 36, 6 }, +/* 24 Mb */ { OFDM, 24000, 0x00, 48, 8 }, +/* 36 Mb */ { OFDM, 36000, 0x00, 72, 8 }, +/* 48 Mb */ { OFDM, 48000, 0x00, 96, 8 }, +/* 54 Mb */ { OFDM, 54000, 0x00, 108, 8 } + }, +}; + +static struct ieee80211_rate_table ieee80211_11a_table = { + 8, /* number of rates */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 6 Mb */ { OFDM, 6000, 0x00, (0x80|12), 0 }, +/* 9 Mb */ { OFDM, 9000, 0x00, 18, 0 }, +/* 12 Mb */ { OFDM, 12000, 0x00, (0x80|24), 2 }, +/* 18 Mb */ { OFDM, 18000, 0x00, 36, 2 }, +/* 24 Mb */ { OFDM, 24000, 0x00, (0x80|48), 4 }, +/* 36 Mb */ { OFDM, 36000, 0x00, 72, 4 }, +/* 48 Mb */ { OFDM, 48000, 0x00, 96, 4 }, +/* 54 Mb */ { OFDM, 54000, 0x00, 108, 4 } + }, +}; + +static struct ieee80211_rate_table ieee80211_half_table = { + 8, /* number of rates */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 6 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 0 }, +/* 9 Mb */ { OFDM, 4500, 0x00, 9, 0 }, +/* 12 Mb */ { OFDM, 6000, 0x00, (0x80|12), 2 }, +/* 18 Mb */ { OFDM, 9000, 0x00, 18, 2 }, +/* 24 Mb */ { OFDM, 12000, 0x00, (0x80|24), 4 }, +/* 36 Mb */ { OFDM, 18000, 0x00, 36, 4 }, +/* 48 Mb */ { OFDM, 24000, 0x00, 48, 4 }, +/* 54 Mb */ { OFDM, 27000, 0x00, 54, 4 } + }, +}; + +static struct ieee80211_rate_table ieee80211_quarter_table = { + 8, /* number of rates */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 6 Mb */ { OFDM, 1500, 0x00, (0x80| 3), 0 }, +/* 9 Mb */ { OFDM, 2250, 0x00, 4, 0 }, +/* 12 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 2 }, +/* 18 Mb */ { OFDM, 4500, 0x00, 9, 2 }, +/* 24 Mb */ { OFDM, 6000, 0x00, (0x80|12), 4 }, +/* 36 Mb */ { OFDM, 9000, 0x00, 18, 4 }, +/* 48 Mb */ { OFDM, 12000, 0x00, 24, 4 }, +/* 54 Mb */ { OFDM, 13500, 0x00, 27, 4 } + }, +}; + +static struct ieee80211_rate_table ieee80211_turbog_table = { + 7, /* number of rates */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 }, +/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 1 }, +/* 18 Mb */ { TURBO, 18000, 0x00, 36, 1 }, +/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 3 }, +/* 36 Mb */ { TURBO, 36000, 0x00, 72, 3 }, +/* 48 Mb */ { TURBO, 48000, 0x00, 96, 3 }, +/* 54 Mb */ { TURBO, 54000, 0x00, 108, 3 } + }, +}; + +static struct ieee80211_rate_table ieee80211_turboa_table = { + 8, /* number of rates */ + { 0 }, + { +/* short ctrl */ +/* Preamble dot11Rate Rate */ +/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 }, +/* 9 Mb */ { TURBO, 9000, 0x00, 18, 0 }, +/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 2 }, +/* 18 Mb */ { TURBO, 18000, 0x00, 36, 2 }, +/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 4 }, +/* 36 Mb */ { TURBO, 36000, 0x00, 72, 4 }, +/* 48 Mb */ { TURBO, 48000, 0x00, 96, 4 }, +/* 54 Mb */ { TURBO, 54000, 0x00, 108, 4 } + }, +}; + +#undef OFDM +#undef CCK +#undef TURBO +#undef XR + +/* + * Setup a rate table's reverse lookup table and fill in + * ack durations. The reverse lookup tables are assumed + * to be initialized to zero (or at least the first entry). + * We use this as a key that indicates whether or not + * we've previously setup the reverse lookup table. + * + * XXX not reentrant, but shouldn't matter + */ +static void +ieee80211_setup_ratetable(struct ieee80211_rate_table *rt) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) +#define WLAN_CTRL_FRAME_SIZE \ + (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) + + int i; + + for (i = 0; i < N(rt->rateCodeToIndex); i++) + rt->rateCodeToIndex[i] = (uint8_t) -1; + for (i = 0; i < rt->rateCount; i++) { + uint8_t code = rt->info[i].dot11Rate; + uint8_t cix = rt->info[i].ctlRateIndex; + uint8_t ctl_rate = rt->info[cix].dot11Rate; + + rt->rateCodeToIndex[code] = i; + if (code & IEEE80211_RATE_BASIC) { + /* + * Map w/o basic rate bit too. + */ + code &= IEEE80211_RATE_VAL; + rt->rateCodeToIndex[code] = i; + } + + /* + * XXX for 11g the control rate to use for 5.5 and 11 Mb/s + * depends on whether they are marked as basic rates; + * the static tables are setup with an 11b-compatible + * 2Mb/s rate which will work but is suboptimal + * + * NB: Control rate is always less than or equal to the + * current rate, so control rate's reverse lookup entry + * has been installed and following call is safe. + */ + rt->info[i].lpAckDuration = ieee80211_compute_duration(rt, + WLAN_CTRL_FRAME_SIZE, ctl_rate, 0); + rt->info[i].spAckDuration = ieee80211_compute_duration(rt, + WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE); + } + +#undef WLAN_CTRL_FRAME_SIZE +#undef N +} + +/* Setup all rate tables */ +static void +ieee80211_phy_init(void) +{ +#define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) + static struct ieee80211_rate_table * const ratetables[] = { + &ieee80211_half_table, + &ieee80211_quarter_table, + &ieee80211_11a_table, + &ieee80211_11g_table, + &ieee80211_turbog_table, + &ieee80211_turboa_table, + &ieee80211_turboa_table, + &ieee80211_11a_table, + &ieee80211_11g_table, + &ieee80211_11b_table + }; + int i; + + for (i = 0; i < N(ratetables); ++i) + ieee80211_setup_ratetable(ratetables[i]); + +#undef N +} +SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL); + +const struct ieee80211_rate_table * +ieee80211_get_ratetable(struct ieee80211_channel *c) +{ + const struct ieee80211_rate_table *rt; + + /* XXX HT */ + if (IEEE80211_IS_CHAN_HALF(c)) + rt = &ieee80211_half_table; + else if (IEEE80211_IS_CHAN_QUARTER(c)) + rt = &ieee80211_quarter_table; + else if (IEEE80211_IS_CHAN_HTA(c)) + rt = &ieee80211_11a_table; /* XXX */ + else if (IEEE80211_IS_CHAN_HTG(c)) + rt = &ieee80211_11g_table; /* XXX */ + else if (IEEE80211_IS_CHAN_108G(c)) + rt = &ieee80211_turbog_table; + else if (IEEE80211_IS_CHAN_ST(c)) + rt = &ieee80211_turboa_table; + else if (IEEE80211_IS_CHAN_TURBO(c)) + rt = &ieee80211_turboa_table; + else if (IEEE80211_IS_CHAN_A(c)) + rt = &ieee80211_11a_table; + else if (IEEE80211_IS_CHAN_ANYG(c)) + rt = &ieee80211_11g_table; + else if (IEEE80211_IS_CHAN_B(c)) + rt = &ieee80211_11b_table; + else { + /* NB: should not get here */ + panic("%s: no rate table for channel; freq %u flags 0x%x\n", + __func__, c->ic_freq, c->ic_flags); + } + return rt; +} + +/* + * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s) + * + * Note we do no parameter checking; this routine is mainly + * used to derive an 802.11 rate for constructing radiotap + * header data for rx frames. + * + * XXX might be a candidate for inline + */ +uint8_t +ieee80211_plcp2rate(uint8_t plcp, int ofdm) +{ + if (ofdm) { + static const uint8_t ofdm_plcp2rate[16] = { + [0xb] = 12, + [0xf] = 18, + [0xa] = 24, + [0xe] = 36, + [0x9] = 48, + [0xd] = 72, + [0x8] = 96, + [0xc] = 108 + }; + return ofdm_plcp2rate[plcp & 0xf]; + } else { + static const uint8_t cck_plcp2rate[16] = { + [0xa] = 2, /* 0x0a */ + [0x4] = 4, /* 0x14 */ + [0x7] = 11, /* 0x37 */ + [0xe] = 22, /* 0x6e */ + [0xc] = 44, /* 0xdc , actually PBCC */ + }; + return cck_plcp2rate[plcp & 0xf]; + } +} + +/* + * Covert 802.11 rate to PLCP signal. + */ +uint8_t +ieee80211_rate2plcp(int rate) +{ + switch (rate) { + /* CCK rates (returned values are device-dependent) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + } + return 0xff; /* XXX unsupported/unknown rate */ +} +/* + * Compute the time to transmit a frame of length frameLen bytes + * using the specified rate, phy, and short preamble setting. + * SIFS is included. + */ +uint16_t +ieee80211_compute_duration(const struct ieee80211_rate_table *rt, + uint32_t frameLen, uint16_t rate, int isShortPreamble) +{ + uint8_t rix = rt->rateCodeToIndex[rate]; + uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime; + uint32_t kbps; + + KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate)); + kbps = rt->info[rix].rateKbps; + if (kbps == 0) /* XXX bandaid for channel changes */ + return 0; + + switch (rt->info[rix].phy) { + case IEEE80211_T_CCK: +#define CCK_SIFS_TIME 10 +#define CCK_PREAMBLE_BITS 144 +#define CCK_PLCP_BITS 48 + phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; + if (isShortPreamble && rt->info[rix].shortPreamble) + phyTime >>= 1; + numBits = frameLen << 3; + txTime = CCK_SIFS_TIME + phyTime + + ((numBits * 1000)/kbps); + break; +#undef CCK_SIFS_TIME +#undef CCK_PREAMBLE_BITS +#undef CCK_PLCP_BITS + + case IEEE80211_T_OFDM: +#define OFDM_SIFS_TIME 16 +#define OFDM_PREAMBLE_TIME 20 +#define OFDM_PLCP_BITS 22 +#define OFDM_SYMBOL_TIME 4 + +#define OFDM_SIFS_TIME_HALF 32 +#define OFDM_PREAMBLE_TIME_HALF 40 +#define OFDM_PLCP_BITS_HALF 22 +#define OFDM_SYMBOL_TIME_HALF 8 + +#define OFDM_SIFS_TIME_QUARTER 64 +#define OFDM_PREAMBLE_TIME_QUARTER 80 +#define OFDM_PLCP_BITS_QUARTER 22 +#define OFDM_SYMBOL_TIME_QUARTER 16 + if (rt == &ieee80211_half_table) { + bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000; + KASSERT(bitsPerSymbol != 0, ("1/2 rate bps")); + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = howmany(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME_QUARTER + + OFDM_PREAMBLE_TIME_QUARTER + + (numSymbols * OFDM_SYMBOL_TIME_QUARTER); + } else if (rt == &ieee80211_quarter_table) { + bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000; + KASSERT(bitsPerSymbol != 0, ("1/4 rate bps")); + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = howmany(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME_HALF + + OFDM_PREAMBLE_TIME_HALF + + (numSymbols * OFDM_SYMBOL_TIME_HALF); + } else { /* full rate channel */ + bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; + KASSERT(bitsPerSymbol != 0, ("full rate bps")); + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = howmany(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME + + OFDM_PREAMBLE_TIME + + (numSymbols * OFDM_SYMBOL_TIME); + } + break; + +#undef OFDM_SIFS_TIME +#undef OFDM_PREAMBLE_TIME +#undef OFDM_PLCP_BITS +#undef OFDM_SYMBOL_TIME + + case IEEE80211_T_TURBO: +#define TURBO_SIFS_TIME 8 +#define TURBO_PREAMBLE_TIME 14 +#define TURBO_PLCP_BITS 22 +#define TURBO_SYMBOL_TIME 4 + /* we still save OFDM rates in kbps - so double them */ + bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000; + KASSERT(bitsPerSymbol != 0, ("turbo bps")); + + numBits = TURBO_PLCP_BITS + (frameLen << 3); + numSymbols = howmany(numBits, bitsPerSymbol); + txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME + + (numSymbols * TURBO_SYMBOL_TIME); + break; +#undef TURBO_SIFS_TIME +#undef TURBO_PREAMBLE_TIME +#undef TURBO_PLCP_BITS +#undef TURBO_SYMBOL_TIME + + default: + panic("%s: unknown phy %u (rate %u)\n", __func__, + rt->info[rix].phy, rate); + break; + } + return txTime; +} diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h new file mode 100644 index 0000000..2b5adcf --- /dev/null +++ b/sys/net80211/ieee80211_phy.h @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NET80211_IEEE80211_PHY_H_ +#define _NET80211_IEEE80211_PHY_H_ + +#ifdef _KERNEL +/* + * IEEE 802.11 PHY-related definitions. + */ + +/* + * Contention window (slots). + */ +#define IEEE80211_CW_MAX 1023 /* aCWmax */ +#define IEEE80211_CW_MIN_0 31 /* DS/CCK aCWmin, ERP aCWmin(0) */ +#define IEEE80211_CW_MIN_1 15 /* OFDM aCWmin, ERP aCWmin(1) */ + +/* + * SIFS (microseconds). + */ +#define IEEE80211_DUR_SIFS 10 /* DS/CCK/ERP SIFS */ +#define IEEE80211_DUR_OFDM_SIFS 16 /* OFDM SIFS */ + +/* + * Slot time (microseconds). + */ +#define IEEE80211_DUR_SLOT 20 /* DS/CCK slottime, ERP long slottime */ +#define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */ +#define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */ + +/* + * DIFS (microseconds). + */ +#define IEEE80211_DUR_DIFS(sifs, slot) ((sifs) + 2 * (slot)) + +struct ieee80211_channel; + +struct ieee80211_rate_table { + int rateCount; /* NB: for proper padding */ + uint8_t rateCodeToIndex[256]; /* back mapping */ + struct { + uint8_t phy; /* CCK/OFDM/TURBO */ + uint32_t rateKbps; /* transfer rate in kbs */ + uint8_t shortPreamble; /* mask for enabling short + * preamble in CCK rate code */ + uint8_t dot11Rate; /* value for supported rates + * info element of MLME */ + uint8_t ctlRateIndex; /* index of next lower basic + * rate; used for dur. calcs */ + uint16_t lpAckDuration; /* long preamble ACK dur. */ + uint16_t spAckDuration; /* short preamble ACK dur. */ + } info[32]; +}; + +const struct ieee80211_rate_table *ieee80211_get_ratetable( + struct ieee80211_channel *); + +static __inline__ uint8_t +ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate) +{ + uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex; + KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate)); + return rt->info[cix].dot11Rate; +} + +static __inline__ uint8_t +ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate) +{ + uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex; + KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate)); + return rt->info[cix].dot11Rate; +} + +static __inline__ enum ieee80211_phytype +ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate) +{ + uint8_t rix = rt->rateCodeToIndex[rate]; + KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate)); + return rt->info[rix].phy; +} + +/* + * Calculate ACK field for + * o non-fragment data frames + * o management frames + * sent using rate, phy and short preamble setting. + */ +static __inline__ uint16_t +ieee80211_ack_duration(const struct ieee80211_rate_table *rt, + uint8_t rate, int isShortPreamble) +{ + uint8_t rix = rt->rateCodeToIndex[rate]; + + KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate)); + if (isShortPreamble) { + KASSERT(rt->info[rix].spAckDuration != 0, + ("shpreamble ack dur is not computed!\n")); + return rt->info[rix].spAckDuration; + } else { + KASSERT(rt->info[rix].lpAckDuration != 0, + ("lgpreamble ack dur is not computed!\n")); + return rt->info[rix].lpAckDuration; + } +} + +/* + * Compute the time to transmit a frame of length frameLen bytes + * using the specified 802.11 rate code, phy, and short preamble + * setting. + * + * NB: SIFS is included. + */ +uint16_t ieee80211_compute_duration(const struct ieee80211_rate_table *, + uint32_t frameLen, uint16_t rate, int isShortPreamble); +/* + * Convert PLCP signal/rate field to 802.11 rate code (.5Mbits/s) + */ +uint8_t ieee80211_plcp2rate(uint8_t, int); +/* + * Convert 802.11 rate code to PLCP signal. + */ +uint8_t ieee80211_rate2plcp(int); +#endif /* _KERNEL */ +#endif /* !_NET80211_IEEE80211_PHY_H_ */ diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c index e764e1f..2a7dda8 100644 --- a/sys/net80211/ieee80211_power.c +++ b/sys/net80211/ieee80211_power.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 power save support. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -43,43 +45,57 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> -static void ieee80211_set_tim(struct ieee80211_node *ni, int set); +static void ieee80211_update_ps(struct ieee80211vap *, int); +static int ieee80211_set_tim(struct ieee80211_node *, int); + +MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state"); void ieee80211_power_attach(struct ieee80211com *ic) { - if (ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_IBSS) { +} + +void +ieee80211_power_detach(struct ieee80211com *ic) +{ +} + +void +ieee80211_power_vattach(struct ieee80211vap *vap) +{ + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { /* NB: driver should override */ - ic->ic_set_tim = ieee80211_set_tim; + vap->iv_update_ps = ieee80211_update_ps; + vap->iv_set_tim = ieee80211_set_tim; } } void -ieee80211_power_lateattach(struct ieee80211com *ic) +ieee80211_power_latevattach(struct ieee80211vap *vap) { /* * Allocate these only if needed. Beware that we * know adhoc mode doesn't support ATIM yet... */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t); - MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len, - M_DEVBUF, M_NOWAIT | M_ZERO); - if (ic->ic_tim_bitmap == NULL) { + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t); + MALLOC(vap->iv_tim_bitmap, uint8_t *, vap->iv_tim_len, + M_80211_POWER, M_NOWAIT | M_ZERO); + if (vap->iv_tim_bitmap == NULL) { printf("%s: no memory for TIM bitmap!\n", __func__); /* XXX good enough to keep from crashing? */ - ic->ic_tim_len = 0; + vap->iv_tim_len = 0; } } } void -ieee80211_power_detach(struct ieee80211com *ic) +ieee80211_power_vdetach(struct ieee80211vap *vap) { - if (ic->ic_tim_bitmap != NULL) { - FREE(ic->ic_tim_bitmap, M_DEVBUF); - ic->ic_tim_bitmap = NULL; + if (vap->iv_tim_bitmap != NULL) { + FREE(vap->iv_tim_bitmap, M_80211_POWER); + vap->iv_tim_bitmap = NULL; } } @@ -116,12 +132,16 @@ ieee80211_node_saveq_age(struct ieee80211_node *ni) int discard = 0; if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { +#ifdef IEEE80211_DEBUG + struct ieee80211vap *vap = ni->ni_vap; +#endif struct mbuf *m; IEEE80211_NODE_SAVEQ_LOCK(ni); while (IF_POLL(&ni->ni_savedq, m) != NULL && M_AGE_GET(m) < IEEE80211_INACT_WAIT) { -IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/ + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "discard frame, age %u", M_AGE_GET(m)); _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); m_freem(m); discard++; @@ -130,7 +150,7 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n" M_AGE_SUB(m, IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(ni); - IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard %u frames for age", discard); IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); } @@ -138,35 +158,52 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n" } /* - * Indicate whether there are frames queued for a station in power-save mode. + * Handle a change in the PS station occupancy. */ static void +ieee80211_update_ps(struct ieee80211vap *vap, int nsta) +{ + + KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS, + ("operating mode %u", vap->iv_opmode)); +} + +/* + * Indicate whether there are frames queued for a station in power-save mode. + */ +static int ieee80211_set_tim(struct ieee80211_node *ni, int set) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; uint16_t aid; + int changed; - KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || - ic->ic_opmode == IEEE80211_M_IBSS, - ("operating mode %u", ic->ic_opmode)); + KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS, + ("operating mode %u", vap->iv_opmode)); aid = IEEE80211_AID(ni->ni_associd); - KASSERT(aid < ic->ic_max_aid, - ("bogus aid %u, max %u", aid, ic->ic_max_aid)); + KASSERT(aid < vap->iv_max_aid, + ("bogus aid %u, max %u", aid, vap->iv_max_aid)); - IEEE80211_BEACON_LOCK(ic); - if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { + IEEE80211_LOCK(ic); + changed = (set != (isset(vap->iv_tim_bitmap, aid) != 0)); + if (changed) { if (set) { - setbit(ic->ic_tim_bitmap, aid); - ic->ic_ps_pending++; + setbit(vap->iv_tim_bitmap, aid); + vap->iv_ps_pending++; } else { - clrbit(ic->ic_tim_bitmap, aid); - ic->ic_ps_pending--; + clrbit(vap->iv_tim_bitmap, aid); + vap->iv_ps_pending--; } - /* NB: we know ic is in RUN state so no need to check */ - ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM); + /* NB: we know vap is in RUN state so no need to check */ + vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM); } - IEEE80211_BEACON_UNLOCK(ic); + IEEE80211_UNLOCK(ic); + + return changed; } /* @@ -177,6 +214,7 @@ ieee80211_set_tim(struct ieee80211_node *ni, int set) void ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int qlen, age; @@ -184,13 +222,13 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) if (_IF_QFULL(&ni->ni_savedq)) { _IF_DROP(&ni->ni_savedq); IEEE80211_NODE_SAVEQ_UNLOCK(ni); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] pwr save q overflow, drops %d (size %d)\n", - ether_sprintf(ni->ni_macaddr), - ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); + IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, + "pwr save q overflow, drops %d (size %d)", + ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); #ifdef IEEE80211_DEBUG - if (ieee80211_msg_dumppkts(ic)) - ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1); + if (ieee80211_msg_dumppkts(vap)) + ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t), + m->m_len, -1, -1); #endif m_freem(m); return; @@ -207,57 +245,26 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); IEEE80211_NODE_SAVEQ_UNLOCK(ni); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, - "[%s] save frame with age %d, %u now queued\n", - ether_sprintf(ni->ni_macaddr), age, qlen); + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "save frame with age %d, %u now queued", age, qlen); - if (qlen == 1 && ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 1); + if (qlen == 1 && vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 1); } /* - * Handle station power-save state change. + * Unload the frames from the ps q but don't send them + * to the driver yet. We do this in two stages to minimize + * locking but also because there's no easy way to preserve + * ordering given the existing ifnet access mechanisms. + * XXX could be optimized */ -void -ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) +static void +pwrsave_flushq(struct ieee80211_node *ni) { - struct ieee80211com *ic = ni->ni_ic; struct mbuf *m, *mhead, *mtail; int mcount; - if (enable) { - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) - ic->ic_ps_sta++; - ni->ni_flags |= IEEE80211_NODE_PWR_MGT; - IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, - "power save mode on, %u sta's in ps mode", 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_NOTE(ic, IEEE80211_MSG_POWER, ni, - "power save mode off, %u sta's in ps mode", 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_NOTE(ic, IEEE80211_MSG_POWER, ni, - "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni)); - /* - * Unload the frames from the ps q but don't send them - * to the driver yet. We do this in two stages to minimize - * locking but also because there's no easy way to preserve - * ordering given the existing ifnet access mechanisms. - * XXX could be optimized - */ IEEE80211_NODE_SAVEQ_LOCK(ni); mcount = IEEE80211_NODE_SAVEQ_QLEN(ni); mhead = mtail = NULL; @@ -275,26 +282,67 @@ ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) IEEE80211_NODE_SAVEQ_UNLOCK(ni); if (mhead != NULL) { /* XXX need different driver interface */ - /* XXX bypasses q max */ - IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount); + /* XXX bypasses q max and OACTIVE */ + struct ifnet *ifp = ni->ni_vap->iv_ifp; + IF_PREPEND_LIST(&ifp->if_snd, mhead, mtail, mcount); + if_start(ifp); + } +} + +/* + * Handle station power-save state change. + */ +void +ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) +{ + struct ieee80211vap *vap = ni->ni_vap; + int update; + + update = 0; + if (enable) { + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { + vap->iv_ps_sta++; + update = 1; + } + ni->ni_flags |= IEEE80211_NODE_PWR_MGT; + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "power save mode on, %u sta's in ps mode", vap->iv_ps_sta); + + if (update) + vap->iv_update_ps(vap, vap->iv_ps_sta); + } else { + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { + vap->iv_ps_sta--; + update = 1; + } + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "power save mode off, %u sta's in ps mode", vap->iv_ps_sta); + + /* NB: order here is intentional so TIM is clear before flush */ + if (vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); + if (update) { + /* NB if no sta's in ps, driver should flush mc q */ + vap->iv_update_ps(vap, vap->iv_ps_sta); + } + pwrsave_flushq(ni); } - if (ic->ic_set_tim != NULL) - ic->ic_set_tim(ni, 0); } /* * Handle power-save state change in station mode. */ void -ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable) +ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable) { - struct ieee80211_node *ni = ic->ic_bss; + struct ieee80211_node *ni = vap->iv_bss; int qlen; if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0))) return; - IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "sta power save mode %s", enable ? "on" : "off"); if (!enable) { ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; @@ -307,20 +355,9 @@ ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable) */ qlen = IEEE80211_NODE_SAVEQ_QLEN(ni); if (qlen != 0) { - IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni, + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "flush ps queue, %u packets queued", qlen); - for (;;) { - struct mbuf *m; - - IEEE80211_NODE_SAVEQ_LOCK(ni); - _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); - IEEE80211_NODE_SAVEQ_UNLOCK(ni); - if (m == NULL) - break; - /* XXX need different driver interface */ - /* XXX bypasses q max */ - IF_ENQUEUE(&ic->ic_ifp->if_snd, m); - } + pwrsave_flushq(ni); } } else { ni->ni_flags |= IEEE80211_NODE_PWR_MGT; diff --git a/sys/net80211/ieee80211_power.h b/sys/net80211/ieee80211_power.h index c8461f6..27f5d5a 100644 --- a/sys/net80211/ieee80211_power.h +++ b/sys/net80211/ieee80211_power.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,14 +30,16 @@ struct ieee80211com; void ieee80211_power_attach(struct ieee80211com *); -void ieee80211_power_lateattach(struct ieee80211com *); void ieee80211_power_detach(struct ieee80211com *); +void ieee80211_power_vattach(struct ieee80211vap *); +void ieee80211_power_vdetach(struct ieee80211vap *); +void ieee80211_power_latevattach(struct ieee80211vap *); int ieee80211_node_saveq_drain(struct ieee80211_node *); int ieee80211_node_saveq_age(struct ieee80211_node *); void ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *); void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); -void ieee80211_sta_pwrsave(struct ieee80211com *, int enable); +void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable); void ieee80211_power_poll(struct ieee80211com *); #endif /* _NET80211_IEEE80211_POWER_H_ */ diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index e67d40c..fe03b15 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,25 +32,32 @@ __FBSDID("$FreeBSD$"); */ #include "opt_inet.h" +#include "opt_wlan.h" #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> +#include <sys/taskqueue.h> #include <sys/socket.h> +#include <sys/sockio.h> #include <net/if.h> #include <net/if_media.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_adhoc.h> +#include <net80211/ieee80211_sta.h> +#include <net80211/ieee80211_hostap.h> +#include <net80211/ieee80211_wds.h> +#include <net80211/ieee80211_monitor.h> +#include <net80211/ieee80211_input.h> /* 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) - const char *ieee80211_mgt_subtype_name[] = { "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", "probe_req", "probe_resp", "reserved#6", "reserved#7", @@ -66,11 +73,9 @@ const char *ieee80211_ctl_subtype_name[] = { const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { "IBSS", /* IEEE80211_M_IBSS */ "STA", /* IEEE80211_M_STA */ - "#2", + "WDS", /* IEEE80211_M_WDS */ "AHDEMO", /* IEEE80211_M_AHDEMO */ - "#4", "#5", "HOSTAP", /* IEEE80211_M_HOSTAP */ - "#7", "MONITOR" /* IEEE80211_M_MONITOR */ }; const char *ieee80211_state_name[IEEE80211_S_MAX] = { @@ -91,11 +96,19 @@ const char *ieee80211_wme_acnames[] = { "WME_UPSD", }; -static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); +static void parent_updown(void *, int); +static int ieee80211_new_state_locked(struct ieee80211vap *, + enum ieee80211_state, int); -static void -null_update_beacon(struct ieee80211com *ic, int item) +static int +null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) { + struct ifnet *ifp = ni->ni_ic->ic_ifp; + + if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); + m_freem(m); + return ENETDOWN; } void @@ -103,54 +116,131 @@ ieee80211_proto_attach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; - /* XXX room for crypto */ - ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); - - ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; - 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, CALLOUT_MPSAFE); - callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE); - ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; + /* override the 802.3 setting */ + ifp->if_hdrlen = ic->ic_headroom + + sizeof(struct ieee80211_qosframe_addr4) + + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN; + /* XXX no way to recalculate on ifdetach */ + if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { + /* XXX sanity check... */ + max_linkhdr = ALIGN(ifp->if_hdrlen); + max_hdr = max_linkhdr + max_protohdr; + max_datalen = MHLEN - max_hdr; + } ic->ic_protmode = IEEE80211_PROT_CTSONLY; - ic->ic_roaming = IEEE80211_ROAMING_AUTO; + + TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp); ic->ic_wme.wme_hipri_switch_hysteresis = AGGRESSIVE_MODE_SWITCH_HYSTERESIS; - mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); - - /* protocol state change handler */ - ic->ic_newstate = ieee80211_newstate; - ic->ic_update_beacon = null_update_beacon; - /* initialize management frame handlers */ - ic->ic_recv_mgmt = ieee80211_recv_mgmt; ic->ic_send_mgmt = ieee80211_send_mgmt; - ic->ic_raw_xmit = ieee80211_raw_xmit; + ic->ic_raw_xmit = null_raw_xmit; + + ieee80211_adhoc_attach(ic); + ieee80211_sta_attach(ic); + ieee80211_wds_attach(ic); + ieee80211_hostap_attach(ic); + ieee80211_monitor_attach(ic); } void ieee80211_proto_detach(struct ieee80211com *ic) { + ieee80211_monitor_detach(ic); + ieee80211_hostap_detach(ic); + ieee80211_wds_detach(ic); + ieee80211_adhoc_detach(ic); + ieee80211_sta_detach(ic); +} + +static void +null_update_beacon(struct ieee80211vap *vap, int item) +{ +} + +void +ieee80211_proto_vattach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + int i; + + /* override the 802.3 setting */ + ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; + + vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; + vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; + vap->iv_bmiss_max = IEEE80211_BMISS_MAX; + callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE); + callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE); + /* + * Install default tx rate handling: no fixed rate, lowest + * supported rate for mgmt and multicast frames. Default + * max retry count. These settings can be changed by the + * driver and/or user applications. + */ + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) { + const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; + + vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; + /* NB: we default to min supported rate for channel */ + vap->iv_txparms[i].mgmtrate = + rs->rs_rates[0] & IEEE80211_RATE_VAL; + vap->iv_txparms[i].mcastrate = + rs->rs_rates[0] & IEEE80211_RATE_VAL; + vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; + } + for (; i < IEEE80211_MODE_MAX; i++) { + vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; + /* NB: default to MCS 0 */ + vap->iv_txparms[i].mgmtrate = 0 | 0x80; + vap->iv_txparms[i].mcastrate = 0 | 0x80; + vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; + } + vap->iv_roaming = IEEE80211_ROAMING_AUTO; + + vap->iv_update_beacon = null_update_beacon; + vap->iv_deliver_data = ieee80211_deliver_data; + + /* attach support for operating mode */ + ic->ic_vattach[vap->iv_opmode](vap); +} +void +ieee80211_proto_vdetach(struct ieee80211vap *vap) +{ +#define FREEAPPIE(ie) do { \ + if (ie != NULL) \ + FREE(ie, M_80211_NODE_IE); \ +} while (0) + /* + * Detach operating mode module. + */ + if (vap->iv_opdetach != NULL) + vap->iv_opdetach(vap); /* * 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); - - ieee80211_drain_ifq(&ic->ic_mgtq); - mtx_destroy(&ic->ic_mgtq.ifq_mtx); - + if (vap->iv_auth->ia_detach != NULL) + vap->iv_auth->ia_detach(vap); /* * Detach any ACL'ator. */ - if (ic->ic_acl != NULL) - ic->ic_acl->iac_detach(ic); + if (vap->iv_acl != NULL) + vap->iv_acl->iac_detach(vap); + + FREEAPPIE(vap->iv_appie_beacon); + FREEAPPIE(vap->iv_appie_probereq); + FREEAPPIE(vap->iv_appie_proberesp); + FREEAPPIE(vap->iv_appie_assocreq); + FREEAPPIE(vap->iv_appie_assocresp); + FREEAPPIE(vap->iv_appie_wpa); +#undef FREEAPPIE } /* @@ -363,16 +453,53 @@ ieee80211_fix_rate(struct ieee80211_node *ni, struct ieee80211_rateset *nrs, int flags) { #define RV(v) ((v) & IEEE80211_RATE_VAL) + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int i, j, rix, error; - int okrate, badrate, fixedrate; + int okrate, badrate, fixedrate, ucastrate; const struct ieee80211_rateset *srs; uint8_t r; error = 0; okrate = badrate = 0; + ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; + if (ucastrate != IEEE80211_FIXED_RATE_NONE) { + /* + * Workaround awkwardness with fixed rate. We are called + * to check both the legacy rate set and the HT rate set + * but we must apply any legacy fixed rate check only to the + * legacy rate set and vice versa. We cannot tell what type + * of rate set we've been given (legacy or HT) but we can + * distinguish the fixed rate type (MCS have 0x80 set). + * So to deal with this the caller communicates whether to + * check MCS or legacy rate using the flags and we use the + * type of any fixed rate to avoid applying an MCS to a + * legacy rate and vice versa. + */ + if (ucastrate & 0x80) { + if (flags & IEEE80211_F_DOFRATE) + flags &= ~IEEE80211_F_DOFRATE; + } else if ((ucastrate & 0x80) == 0) { + if (flags & IEEE80211_F_DOFMCS) + flags &= ~IEEE80211_F_DOFMCS; + } + /* NB: required to make MCS match below work */ + ucastrate &= IEEE80211_RATE_VAL; + } fixedrate = IEEE80211_FIXED_RATE_NONE; - srs = ieee80211_get_suprates(ic, ni->ni_chan); + /* + * XXX we are called to process both MCS and legacy rates; + * we must use the appropriate basic rate set or chaos will + * ensue; for now callers that want MCS must supply + * IEEE80211_F_DOBRS; at some point we'll need to split this + * function so there are two variants, one for MCS and one + * for legacy rates. + */ + if (flags & IEEE80211_F_DOBRS) + srs = (const struct ieee80211_rateset *) + ieee80211_get_suphtrates(ic, ni->ni_chan); + else + srs = ieee80211_get_suprates(ic, ni->ni_chan); for (i = 0; i < nrs->rs_nrates; ) { if (flags & IEEE80211_F_DOSORT) { /* @@ -391,7 +518,7 @@ ieee80211_fix_rate(struct ieee80211_node *ni, /* * Check for fixed rate. */ - if (r == ic->ic_fixed_rate) + if (r == ucastrate) fixedrate = r; /* * Check against supported rates. @@ -431,9 +558,13 @@ ieee80211_fix_rate(struct ieee80211_node *ni, i++; } if (okrate == 0 || error != 0 || - ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate)) + ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && + fixedrate != ucastrate)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, + "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " + "ucastrate %x\n", __func__, fixedrate, ucastrate, flags); return badrate | IEEE80211_RATE_BASIC; - else + } else return RV(okrate); #undef RV } @@ -491,7 +622,7 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) * NB: the rate set is assumed to be sorted. */ int -ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) +ieee80211_iserp_rateset(const struct ieee80211_rateset *rs) { #define N(a) (sizeof(a) / sizeof(a[0])) static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; @@ -516,14 +647,15 @@ ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) } /* - * Mark the basic rates for the 11g rate table based on the + * Mark the basic rates for the 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 void +setbasicrates(struct ieee80211_rateset *rs, + enum ieee80211_phymode mode, int add) { static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ @@ -531,16 +663,17 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ - /* IEEE80211_MODE_PUREG (not yet) */ - { 7, { 2, 4, 11, 22, 12, 24, 48 } }, + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */ + { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */ + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_STURBO_A */ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ - /* IEEE80211_MODE_11NG (mixed b/g) */ - { 7, { 2, 4, 11, 22, 12, 24, 48 } }, + { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11NG (mixed b/g) */ }; int i, j; for (i = 0; i < rs->rs_nrates; i++) { - rs->rs_rates[i] &= IEEE80211_RATE_VAL; + if (!add) + 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; @@ -550,14 +683,40 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode } /* - * WME protocol support. The following parameters come from the spec. + * Set the basic rates in a rate set. + */ +void +ieee80211_setbasicrates(struct ieee80211_rateset *rs, + enum ieee80211_phymode mode) +{ + setbasicrates(rs, mode, 0); +} + +/* + * Add basic rates to a rate set. + */ +void +ieee80211_addbasicrates(struct ieee80211_rateset *rs, + enum ieee80211_phymode mode) +{ + setbasicrates(rs, mode, 1); +} + +/* + * WME protocol support. + * + * The default 11a/b/g/n parameters come from the WiFi Alliance WMM + * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n + * Draft 2.0 Test Plan (Appendix D). + * + * Static/Dynamic Turbo mode settings come from Atheros. */ typedef struct phyParamType { - uint8_t aifsn; - uint8_t logcwmin; - uint8_t logcwmax; - uint16_t txopLimit; - uint8_t acm; + 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] = { @@ -646,15 +805,18 @@ static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ }; -void -ieee80211_wme_initparams(struct ieee80211com *ic) +static void +ieee80211_wme_initparams_locked(struct ieee80211vap *vap) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_wme_state *wme = &ic->ic_wme; const paramType *pPhyParam, *pBssPhyParam; struct wmeParams *wmep; enum ieee80211_phymode mode; int i; + IEEE80211_LOCK_ASSERT(ic); + if ((ic->ic_caps & IEEE80211_C_WME) == 0) return; @@ -704,7 +866,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic) wmep->wmep_txopLimit = pBssPhyParam->txopLimit; } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[i] @@ -721,7 +883,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic) wmep->wmep_logcwmin = pBssPhyParam->logcwmin; wmep->wmep_logcwmax = pBssPhyParam->logcwmax; wmep->wmep_txopLimit = pBssPhyParam->txopLimit; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " "log2(cwmax) %u txpoLimit %u]\n", __func__ , ieee80211_wme_acnames[i] @@ -733,7 +895,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic) ); } /* NB: check ic_bss to avoid NULL deref on initial attach */ - if (ic->ic_bss != NULL) { + if (vap->iv_bss != NULL) { /* * Calculate agressive mode switching threshold based * on beacon interval. This doesn't need locking since @@ -741,16 +903,26 @@ ieee80211_wme_initparams(struct ieee80211com *ic) * 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); + (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; + ieee80211_wme_updateparams(vap); } } +void +ieee80211_wme_initparams(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + ieee80211_wme_initparams_locked(vap); + IEEE80211_UNLOCK(ic); +} + /* * Update WME parameters for ourself and the BSS. */ void -ieee80211_wme_updateparams_locked(struct ieee80211com *ic) +ieee80211_wme_updateparams_locked(struct ieee80211vap *vap) { static const paramType phyParam[IEEE80211_MODE_MAX] = { { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ @@ -764,6 +936,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ }; + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_wme_state *wme = &ic->ic_wme; const struct wmeParams *wmep; struct wmeParams *chanp, *bssp; @@ -806,11 +979,11 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) * BE uses agressive params to optimize performance of * legacy/non-QoS traffic. */ - if ((ic->ic_opmode == IEEE80211_M_HOSTAP && + if ((vap->iv_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) { + (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || + (vap->iv_flags & IEEE80211_F_WME) == 0) { chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; @@ -820,9 +993,9 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) chanp->wmep_logcwmax = bssp->wmep_logcwmax = phyParam[mode].logcwmax; chanp->wmep_txopLimit = bssp->wmep_txopLimit = - (ic->ic_flags & IEEE80211_F_BURST) ? + (vap->iv_flags & IEEE80211_F_BURST) ? phyParam[mode].txopLimit : 0; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, 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] @@ -834,7 +1007,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) ); } - if (ic->ic_opmode == IEEE80211_M_HOSTAP && + /* XXX multi-bss */ + if (vap->iv_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 */ @@ -852,77 +1026,238 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic) bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, 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? */ + if (vap->iv_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; - ieee80211_beacon_notify(ic, IEEE80211_BEACON_WME); + ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); } wme->wme_update(ic); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: WME params updated, cap_info 0x%x\n", __func__, - ic->ic_opmode == IEEE80211_M_STA ? + vap->iv_opmode == IEEE80211_M_STA ? wme->wme_wmeChanParams.cap_info : wme->wme_bssChanParams.cap_info); } void -ieee80211_wme_updateparams(struct ieee80211com *ic) +ieee80211_wme_updateparams(struct ieee80211vap *vap) { + struct ieee80211com *ic = vap->iv_ic; if (ic->ic_caps & IEEE80211_C_WME) { - IEEE80211_BEACON_LOCK(ic); - ieee80211_wme_updateparams_locked(ic); - IEEE80211_BEACON_UNLOCK(ic); + IEEE80211_LOCK(ic); + ieee80211_wme_updateparams_locked(vap); + IEEE80211_UNLOCK(ic); } } +static void +parent_updown(void *arg, int npending) +{ + struct ifnet *parent = arg; + + parent->if_ioctl(parent, SIOCSIFFLAGS, NULL); +} + /* - * Start a device. If this is the first vap running on the - * underlying device then we first bring it up. + * Start a vap running. If this is the first vap to be + * set running on the underlying device then we + * automatically bring the device up. */ -int -ieee80211_init(struct ieee80211com *ic, int forcescan) +void +ieee80211_start_locked(struct ieee80211vap *vap) { + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *parent = ic->ic_ifp; - IEEE80211_DPRINTF(ic, + IEEE80211_LOCK_ASSERT(ic); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, - "%s\n", "start running"); + "start running, %d vaps running\n", ic->ic_nrunning); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + /* + * Mark us running. Note that it's ok to do this first; + * if we need to bring the parent device up we defer that + * to avoid dropping the com lock. We expect the device + * to respond to being marked up by calling back into us + * through ieee80211_start_all at which point we'll come + * back in here and complete the work. + */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + /* + * We are not running; if this we are the first vap + * to be brought up auto-up the parent if necessary. + */ + if (ic->ic_nrunning++ == 0 && + (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s: up parent %s\n", __func__, parent->if_xname); + parent->if_flags |= IFF_UP; + taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); + return; + } + } /* - * Kick the 802.11 state machine as appropriate. + * If the parent is up and running, then kick the + * 802.11 state machine as appropriate. */ - if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { - if (ic->ic_opmode == IEEE80211_M_STA) { - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + if ((parent->if_drv_flags & IFF_DRV_RUNNING) && + vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { + if (vap->iv_opmode == IEEE80211_M_STA) { +#if 0 + /* XXX bypasses scan too easily; disable for now */ + /* + * Try to be intelligent about clocking the state + * machine. If we're currently in RUN state then + * we should be able to apply any new state/parameters + * simply by re-associating. Otherwise we need to + * re-scan to select an appropriate ap. + */ + if (vap->iv_state >= IEEE80211_S_RUN) + ieee80211_new_state_locked(vap, + IEEE80211_S_ASSOC, 1); + else +#endif + ieee80211_new_state_locked(vap, + IEEE80211_S_SCAN, 0); } else { /* - * For monitor+wds modes there's nothing to do but - * start running. Otherwise, if this is the first + * For monitor+wds mode there's nothing to do but + * start running. Otherwise if this is the first * vap to be brought up, start a scan which may be * preempted if the station is locked to a particular * channel. */ - if (ic->ic_opmode == IEEE80211_M_MONITOR || - ic->ic_opmode == IEEE80211_M_WDS) { - ic->ic_state = IEEE80211_S_INIT; /* XXX*/ - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - } else - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + /* XXX needed? */ + ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0); + if (vap->iv_opmode == IEEE80211_M_MONITOR || + vap->iv_opmode == IEEE80211_M_WDS) + ieee80211_new_state_locked(vap, + IEEE80211_S_RUN, -1); + else + ieee80211_new_state_locked(vap, + IEEE80211_S_SCAN, 0); } } - return 0; +} + +/* + * Start a single vap. + */ +void +ieee80211_init(void *arg) +{ + struct ieee80211vap *vap = arg; + + /* + * This routine is publicly accessible through the vap's + * if_init method so guard against calls during detach. + * ieee80211_vap_detach null's the backpointer before + * tearing down state to signal any callback should be + * rejected/ignored. + */ + if (vap != NULL) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s\n", __func__); + + IEEE80211_LOCK(vap->iv_ic); + ieee80211_start_locked(vap); + IEEE80211_UNLOCK(vap->iv_ic); + } +} + +/* + * Start all runnable vap's on a device. + */ +void +ieee80211_start_all(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ + ieee80211_start_locked(vap); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Stop a vap. We force it down using the state machine + * then mark it's ifnet not running. If this is the last + * vap running on the underlying device then we close it + * too to insure it will be properly initialized when the + * next vap is brought up. + */ +void +ieee80211_stop_locked(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ifnet *parent = ic->ic_ifp; + + IEEE80211_LOCK_ASSERT(ic); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "stop running, %d vaps running\n", ic->ic_nrunning); + + ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */ + if (--ic->ic_nrunning == 0 && + (parent->if_drv_flags & IFF_DRV_RUNNING)) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "down parent %s\n", parent->if_xname); + parent->if_flags &= ~IFF_UP; + taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); + } + } +} + +void +ieee80211_stop(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + ieee80211_stop_locked(vap); + IEEE80211_UNLOCK(ic); +} + +/* + * Stop all vap's running on a device. + */ +void +ieee80211_stop_all(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ + ieee80211_stop_locked(vap); + } + IEEE80211_UNLOCK(ic); } /* @@ -932,19 +1267,20 @@ ieee80211_init(struct ieee80211com *ic, int forcescan) * the driver to effect the change. */ void -ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags) +ieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *chan; chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); if (chan == NULL) { /* XXX should not happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no channel with freq %u flags 0x%x\n", __func__, ic->ic_bsschan->ic_freq, newflags); return; } - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], ieee80211_phymode_name[ieee80211_chan2mode(chan)], @@ -960,57 +1296,21 @@ ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags) void ieee80211_beacon_miss(struct ieee80211com *ic) { + struct ieee80211vap *vap; - if (ic->ic_flags & IEEE80211_F_SCAN) { - /* XXX check ic_curchan != ic_bsschan? */ + if (ic->ic_flags & IEEE80211_F_SCAN) 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) { + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { /* - * 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. + * We only pass events through for sta vap's in RUN state; + * may be too restrictive but for now this saves all the + * handlers duplicating these checks. */ - 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; - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { - /* - * If we receive a beacon miss interrupt when using - * dynamic turbo, attempt to switch modes before - * reassociating. - */ - if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP)) - ieee80211_dturbo_switch(ic, - ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); - /* - * Try to reassociate before scanning for a new ap. - */ - ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); - } else { - /* - * Somebody else is controlling state changes (e.g. - * a user-mode app) don't do anything that would - * confuse them; just drop into scan mode so they'll - * notified of the state change and given control. - */ - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN && + vap->iv_bmiss != NULL) + vap->iv_bmiss(vap); } } @@ -1019,377 +1319,378 @@ ieee80211_beacon_miss(struct ieee80211com *ic) * were received in the last period. If not post a * beacon miss; otherwise reset the counter. */ -static void +void ieee80211_swbmiss(void *arg) { - struct ieee80211com *ic = arg; + struct ieee80211vap *vap = arg; - if (ic->ic_swbmiss_count == 0) { - ieee80211_beacon_miss(ic); - if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ + if (vap->iv_swbmiss_count == 0) { + if (vap->iv_bmiss != NULL) + vap->iv_bmiss(vap); + if (vap->iv_bmiss_count == 0) /* don't re-arm timer */ return; } else - ic->ic_swbmiss_count = 0; - callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, - ieee80211_swbmiss, ic); + vap->iv_swbmiss_count = 0; + callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, + ieee80211_swbmiss, vap); +} + +/* + * Start an 802.11h channel switch. We record the parameters, + * mark the operation pending, notify each vap through the + * beacon update mechanism so it can update the beacon frame + * contents, and then switch vap's to CSA state to block outbound + * traffic. Devices that handle CSA directly can use the state + * switch to do the right thing so long as they call + * ieee80211_csa_completeswitch when it's time to complete the + * channel change. Devices that depend on the net80211 layer can + * use ieee80211_beacon_update to handle the countdown and the + * channel switch. + */ +void +ieee80211_csa_startswitch(struct ieee80211com *ic, + struct ieee80211_channel *c, int mode, int count) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + ic->ic_csa_newchan = c; + ic->ic_csa_count = count; + /* XXX record mode? */ + ic->ic_flags |= IEEE80211_F_CSAPENDING; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) + ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); + /* switch to CSA state to block outbound traffic */ + if (vap->iv_state == IEEE80211_S_RUN) + ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); + } + ieee80211_notify_csa(ic, c, mode, count); +} + +/* + * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. + * We clear state and move all vap's in CSA state to RUN state + * so they can again transmit. + */ +void +ieee80211_csa_completeswitch(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); + + ieee80211_setcurchan(ic, ic->ic_csa_newchan); + ic->ic_csa_newchan = NULL; + ic->ic_flags &= ~IEEE80211_F_CSAPENDING; + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state == IEEE80211_S_CSA) + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); +} + +/* + * Complete a DFS CAC started by ieee80211_dfs_cac_start. + * We clear state and move all vap's in CAC state to RUN state. + */ +void +ieee80211_cac_completeswitch(struct ieee80211vap *vap0) +{ + struct ieee80211com *ic = vap0->iv_ic; + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + /* + * Complete CAC state change for lead vap first; then + * clock all the other vap's waiting. + */ + KASSERT(vap0->iv_state == IEEE80211_S_CAC, + ("wrong state %d", vap0->iv_state)); + ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state == IEEE80211_S_CAC) + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); + IEEE80211_UNLOCK(ic); } +/* + * Force all vap's other than the specified vap to the INIT state + * and mark them as waiting for a scan to complete. These vaps + * will be brought up when the scan completes and the scanning vap + * reaches RUN state by wakeupwaiting. + * XXX if we do this in threads we can use sleep/wakeup. + */ static void -sta_disassoc(void *arg, struct ieee80211_node *ni) +markwaiting(struct ieee80211vap *vap0) { - struct ieee80211com *ic = arg; + struct ieee80211com *ic = vap0->iv_ic; + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); - if (ni->ni_associd != 0) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_ASSOC_LEAVE); - ieee80211_node_leave(ic, ni); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap == vap0) + continue; + if (vap->iv_state != IEEE80211_S_INIT) { + vap->iv_newstate(vap, IEEE80211_S_INIT, 0); + vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; + } } } +/* + * Wakeup all vap's waiting for a scan to complete. This is the + * companion to markwaiting (above) and is used to coordinate + * multiple vaps scanning. + */ static void -sta_deauth(void *arg, struct ieee80211_node *ni) +wakeupwaiting(struct ieee80211vap *vap0) { - struct ieee80211com *ic = arg; + struct ieee80211com *ic = vap0->iv_ic; + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_ASSOC_LEAVE); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap == vap0) + continue; + if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; + /* NB: sta's cannot go INIT->RUN */ + vap->iv_newstate(vap, + vap->iv_opmode == IEEE80211_M_STA ? + IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); + } + } } /* - * Handle deauth with reason. We retry only for - * the cases where we might succeed. Otherwise - * we downgrade the ap and scan. + * Handle post state change work common to all operating modes. */ static void -sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason) +ieee80211_newstate_cb(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg) { - switch (reason) { - case IEEE80211_STATUS_SUCCESS: - case IEEE80211_STATUS_TIMEOUT: - case IEEE80211_REASON_ASSOC_EXPIRE: - case IEEE80211_REASON_NOT_AUTHED: - case IEEE80211_REASON_NOT_ASSOCED: - case IEEE80211_REASON_ASSOC_LEAVE: - case IEEE80211_REASON_ASSOC_NOT_AUTHED: - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); - break; - default: - ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason); - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) - ieee80211_check_scan(ic, - IEEE80211_SCAN_ACTIVE, - IEEE80211_SCAN_FOREVER, - ic->ic_des_nssid, ic->ic_des_ssid); - break; + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK_ASSERT(ic); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg); + + if (nstate == IEEE80211_S_RUN) { + /* + * OACTIVE may be set on the vap if the upper layer + * tried to transmit (e.g. IPv6 NDP) before we reach + * RUN state. Clear it and restart xmit. + * + * Note this can also happen as a result of SLEEP->RUN + * (i.e. coming out of power save mode). + */ + vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if_start(vap->iv_ifp); + + /* bring up any vaps waiting on us */ + wakeupwaiting(vap); + } else if (nstate == IEEE80211_S_INIT) { + /* + * Flush the scan cache if we did the last scan (XXX?) + * and flush any frames on send queues from this vap. + * Note the mgt q is used only for legacy drivers and + * will go away shortly. + */ + ieee80211_scan_flush(vap); + + /* XXX NB: cast for altq */ + ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); } + vap->iv_newstate_cb = NULL; } +/* + * Public interface for initiating a state machine change. + * This routine single-threads the request and coordinates + * the scheduling of multiple vaps for the purpose of selecting + * an operating channel. Specifically the following scenarios + * are handled: + * o only one vap can be selecting a channel so on transition to + * SCAN state if another vap is already scanning then + * mark the caller for later processing and return without + * doing anything (XXX? expectations by caller of synchronous operation) + * o only one vap can be doing CAC of a channel so on transition to + * CAC state if another vap is already scanning for radar then + * mark the caller for later processing and return without + * doing anything (XXX? expectations by caller of synchronous operation) + * o if another vap is already running when a request is made + * to SCAN then an operating channel has been chosen; bypass + * the scan and just join the channel + * + * Note that the state change call is done through the iv_newstate + * method pointer so any driver routine gets invoked. The driver + * will normally call back into operating mode-specific + * ieee80211_newstate routines (below) unless it needs to completely + * bypass the state machine (e.g. because the firmware has it's + * own idea how things should work). Bypassing the net80211 layer + * is usually a mistake and indicates lack of proper integration + * with the net80211 layer. + */ static int -ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +ieee80211_new_state_locked(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg) { - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211_node *ni; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211vap *vp; enum ieee80211_state ostate; - - ostate = ic->ic_state; - 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 */ - callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */ - if (ostate != IEEE80211_S_SCAN) - ieee80211_cancel_scan(ic); /* background scan */ - 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) { - case IEEE80211_S_INIT: - break; - case IEEE80211_S_RUN: - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_ASSOC_LEAVE); - ieee80211_sta_leave(ic, ni); - break; - case IEEE80211_M_HOSTAP: - ieee80211_iterate_nodes(&ic->ic_sta, - sta_disassoc, ic); - break; - default: - break; - } - break; - case IEEE80211_S_ASSOC: - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_LEAVE); - break; - case IEEE80211_M_HOSTAP: - ieee80211_iterate_nodes(&ic->ic_sta, - sta_deauth, ic); - break; - default: - break; - } - break; - case IEEE80211_S_SCAN: - ieee80211_cancel_scan(ic); - break; - case IEEE80211_S_AUTH: - break; - default: - break; + int nrunning, nscanning, rc; + + IEEE80211_LOCK_ASSERT(ic); + + nrunning = nscanning = 0; + /* XXX can track this state instead of calculating */ + TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { + if (vp != vap) { + if (vp->iv_state >= IEEE80211_S_RUN) + nrunning++; + /* XXX doesn't handle bg scan */ + /* NB: CAC+AUTH+ASSOC treated like SCAN */ + else if (vp->iv_state > IEEE80211_S_INIT) + nscanning++; } - if (ostate != IEEE80211_S_INIT) { - /* NB: optimize INIT -> INIT case */ - ieee80211_drain_ifq(&ic->ic_mgtq); - ieee80211_reset_bss(ic); - ieee80211_scan_flush(ic); - } - if (ic->ic_auth->ia_detach != NULL) - ic->ic_auth->ia_detach(ic); - break; + } + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate], + nrunning, nscanning); + switch (nstate) { case IEEE80211_S_SCAN: - switch (ostate) { - case IEEE80211_S_INIT: - createibss: - 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) { - /* - * Already have a channel; bypass the - * scan and startup immediately. Because - * of this explicitly sync the scanner state. - */ - ieee80211_scan_update(ic); - ieee80211_create_ibss(ic, ic->ic_des_chan); - } else { - ieee80211_check_scan(ic, - IEEE80211_SCAN_ACTIVE | - IEEE80211_SCAN_FLUSH, - IEEE80211_SCAN_FOREVER, - ic->ic_des_nssid, ic->ic_des_ssid); - } - break; - case IEEE80211_S_SCAN: - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: + if (ostate == IEEE80211_S_INIT) { /* - * These can happen either because of a timeout - * on an assoc/auth response or because of a - * change in state that requires a reset. For - * the former we're called with a non-zero arg - * that is the cause for the failure; pass this - * to the scan code so it can update state. - * Otherwise trigger a new scan unless we're in - * manual roaming mode in which case an application - * must issue an explicit scan request. + * INIT -> SCAN happens on initial bringup. */ - if (arg != 0) - ieee80211_scan_assoc_fail(ic, - ic->ic_bss->ni_macaddr, arg); - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) - ieee80211_check_scan(ic, - IEEE80211_SCAN_ACTIVE, - IEEE80211_SCAN_FOREVER, - ic->ic_des_nssid, ic->ic_des_ssid); - break; - case IEEE80211_S_RUN: /* beacon miss */ - if (ic->ic_opmode == IEEE80211_M_STA) { - ieee80211_sta_leave(ic, ni); - ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) - ieee80211_check_scan(ic, - IEEE80211_SCAN_ACTIVE, - IEEE80211_SCAN_FOREVER, - ic->ic_des_nssid, - ic->ic_des_ssid); - } else { - ieee80211_iterate_nodes(&ic->ic_sta, - sta_disassoc, ic); - goto createibss; - } - break; - default: - break; - } - break; - case IEEE80211_S_AUTH: - KASSERT(ic->ic_opmode == IEEE80211_M_STA, - ("switch to %s state when operating in mode %u", - ieee80211_state_name[nstate], ic->ic_opmode)); - switch (ostate) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 1); - break; - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: - switch (arg & 0xff) { - case IEEE80211_FC0_SUBTYPE_AUTH: - /* ??? */ - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 2); - break; - case IEEE80211_FC0_SUBTYPE_DEAUTH: - sta_authretry(ic, ni, arg>>8); - break; - } - break; - case IEEE80211_S_RUN: - switch (arg & 0xff) { - 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: - 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; - default: - break; - } - break; - case IEEE80211_S_ASSOC: - KASSERT(ic->ic_opmode == IEEE80211_M_STA, - ("switch to %s state when operating in mode %u", - ieee80211_state_name[nstate], ic->ic_opmode)); - switch (ostate) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "%s: invalid transition\n", __func__); - break; - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); - break; - case IEEE80211_S_RUN: - ieee80211_sta_leave(ic, ni); - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { - IEEE80211_SEND_MGMT(ic, ni, arg ? - IEEE80211_FC0_SUBTYPE_REASSOC_REQ : - IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); - } - break; - default: - 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 || - ic->ic_opmode == IEEE80211_M_WDS || - ic->ic_opmode == IEEE80211_M_HOSTAP) { + KASSERT(!(nscanning && nrunning), + ("%d scanning and %d running", nscanning, nrunning)); + if (nscanning) { /* - * Already have a channel; bypass the - * scan and startup immediately. Because - * of this explicitly sync the scanner state. + * Someone is scanning, defer our state + * change until the work has completed. */ - ieee80211_scan_update(ic); - ieee80211_create_ibss(ic, - ieee80211_ht_adjust_channel(ic, - ic->ic_curchan, ic->ic_flags_ext)); - break; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: defer %s -> %s\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; + rc = 0; + goto done; } - /* fall thru... */ - case IEEE80211_S_AUTH: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "%s: invalid transition\n", __func__); - /* fall thru... */ - case IEEE80211_S_RUN: - 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)); -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_debug(ic)) { - if (ic->ic_opmode == IEEE80211_M_STA) - if_printf(ifp, "associated "); + if (nrunning) { + /* + * Someone is operating; just join the channel + * they have chosen. + */ + /* XXX kill arg? */ + /* XXX check each opmode, adhoc? */ + if (vap->iv_opmode == IEEE80211_M_STA) + nstate = IEEE80211_S_SCAN; else - if_printf(ifp, "synchronized "); - printf("with %s ssid ", - ether_sprintf(ni->ni_bssid)); - ieee80211_print_essid(ic->ic_bss->ni_essid, - ni->ni_esslen); - printf(" channel %d start %uMb\n", - ieee80211_chan2ieee(ic, ic->ic_curchan), - IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); - } + nstate = IEEE80211_S_RUN; +#ifdef IEEE80211_DEBUG + if (nstate != IEEE80211_S_SCAN) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE, + "%s: override, now %s -> %s\n", + __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + } #endif - if (ic->ic_opmode == IEEE80211_M_STA) { - ieee80211_scan_assoc_success(ic, - ni->ni_macaddr); - ieee80211_notify_node_join(ic, ni, - arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); } - if_start(ifp); /* XXX not authorized yet */ - break; - default: - break; + } else { + /* + * SCAN was forced; e.g. on beacon miss. Force + * other running vap's to INIT state and mark + * them as waiting for the scan to complete. This + * insures they don't interfere with our scanning. + * + * XXX not always right, assumes ap follows sta + */ + markwaiting(vap); } - if (ostate != IEEE80211_S_RUN && - ic->ic_opmode == IEEE80211_M_STA && - (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { + break; + case IEEE80211_S_RUN: + if (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && + nscanning) { /* - * Start s/w beacon miss timer for devices w/o - * hardware support. We fudge a bit here since - * we're doing this in software. + * Legacy WDS with someone else scanning; don't + * go online until that completes as we should + * follow the other vap to the channel they choose. */ - 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); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: defer %s -> %s (legacy WDS)\n", __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; + rc = 0; + goto done; } - /* - * 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); + if (vap->iv_opmode == IEEE80211_M_HOSTAP && + IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && + (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && + !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { + /* + * This is a DFS channel, transition to CAC state + * instead of RUN. This allows us to initiate + * Channel Availability Check (CAC) as specified + * by 11h/DFS. + */ + nstate = IEEE80211_S_CAC; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: override %s -> %s (DFS)\n", __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); } - /* - * 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 - */ - callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, - ieee80211_node_timeout, ic); break; + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_INIT ) { + /* XXX don't believe this */ + /* INIT -> INIT. nothing to do */ + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; + } + /* fall thru... */ default: break; } - return 0; + /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */ + if (ostate != nstate) { + /* + * Arrange for work to happen after state change completes. + * If this happens asynchronously the caller must arrange + * for the com lock to be held. + */ + vap->iv_newstate_cb = ieee80211_newstate_cb; + } + rc = vap->iv_newstate(vap, nstate, arg); + if (rc == 0 && vap->iv_newstate_cb != NULL) + vap->iv_newstate_cb(vap, nstate, arg); +done: + return rc; +} + +int +ieee80211_new_state(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + int rc; + + IEEE80211_LOCK(ic); + rc = ieee80211_new_state_locked(vap, nstate, arg); + IEEE80211_UNLOCK(ic); + return rc; } diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 9f94f1c..ec7061d 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,63 +44,65 @@ enum ieee80211_state { }; #define IEEE80211_S_MAX (IEEE80211_S_SLEEP+1) -#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \ - ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg)) - -/* - * The formation of some management frames requires guidance to - * deal with legacy clients. When the client is identified as - * "legacy 11b" this parameter can be passed in the arg param of a - * IEEE80211_SEND_MGMT call. - */ -#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */ -#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */ -#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */ +#define IEEE80211_SEND_MGMT(_ni,_type,_arg) \ + ((*(_ni)->ni_ic->ic_send_mgmt)(_ni, _type, _arg)) extern const char *ieee80211_mgt_subtype_name[]; extern const char *ieee80211_phymode_name[]; void ieee80211_proto_attach(struct ieee80211com *); void ieee80211_proto_detach(struct ieee80211com *); +void ieee80211_proto_vattach(struct ieee80211vap *); +void ieee80211_proto_vdetach(struct ieee80211vap *); + +void ieee80211_syncifflag_locked(struct ieee80211com *, int flag); +void ieee80211_syncflag(struct ieee80211vap *, int flag); +void ieee80211_syncflag_ext(struct ieee80211vap *, int flag); -struct ieee80211_node; -int ieee80211_input(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *, int, int, uint32_t); -void ieee80211_deliver_data(struct ieee80211com *, - struct ieee80211_node *, struct mbuf *); -struct mbuf *ieee80211_decap1(struct mbuf *, int *); -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_saveath(struct ieee80211_node *, uint8_t *); -void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *, int, int, int, uint32_t); -int ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *, - struct mbuf *, int type); +#define ieee80211_input(ni, m, rssi, noise, rstamp) \ + ((ni)->ni_vap->iv_input(ni, m, rssi, noise, rstamp)) +int ieee80211_input_all(struct ieee80211com *, struct mbuf *, + int, int, uint32_t); +int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int); struct ieee80211_bpf_params; int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); int ieee80211_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); +void ieee80211_start(struct ifnet *); int ieee80211_send_nulldata(struct ieee80211_node *); -int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, - int, int); +int ieee80211_classify(struct ieee80211_node *, struct mbuf *m); +struct mbuf *ieee80211_encap(struct ieee80211_node *, struct mbuf *); +int ieee80211_send_mgmt(struct ieee80211_node *, int, int); +struct ieee80211_appie; 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_classify(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *); -struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *); + const uint8_t *ssid, size_t ssidlen); +/* + * The formation of ProbeResponse frames requires guidance to + * deal with legacy clients. When the client is identified as + * "legacy 11b" ieee80211_send_proberesp is passed this token. + */ +#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */ +#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */ +#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */ +struct mbuf *ieee80211_alloc_proberesp(struct ieee80211_node *, int); +int ieee80211_send_proberesp(struct ieee80211vap *, + const uint8_t da[IEEE80211_ADDR_LEN], int); +struct mbuf *ieee80211_alloc_rts(struct ieee80211com *ic, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN], uint16_t); +struct mbuf *ieee80211_alloc_cts(struct ieee80211com *, + const uint8_t [IEEE80211_ADDR_LEN], uint16_t); 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 *, +int ieee80211_iserp_rateset(const struct ieee80211_rateset *); +void ieee80211_setbasicrates(struct ieee80211_rateset *, + enum ieee80211_phymode); +void ieee80211_addbasicrates(struct ieee80211_rateset *, enum ieee80211_phymode); /* @@ -146,16 +148,16 @@ ieee80211_anyhdrsize(const void *data) /* * Template for an in-kernel authenticator. Authenticators * register with the protocol code and are typically loaded - * as separate modules as needed. + * as separate modules as needed. One special authenticator + * is xauth; it intercepts requests so that protocols like + * WPA can be handled in user space. */ 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 *); + int (*ia_attach)(struct ieee80211vap *); + void (*ia_detach)(struct ieee80211vap *); + void (*ia_node_join)(struct ieee80211_node *); + void (*ia_node_leave)(struct ieee80211_node *); }; void ieee80211_authenticator_register(int type, const struct ieee80211_authenticator *); @@ -166,23 +168,23 @@ 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. + * address of each received auth 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 *, + int (*iac_attach)(struct ieee80211vap *); + void (*iac_detach)(struct ieee80211vap *); + int (*iac_check)(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); - int (*iac_add)(struct ieee80211com *, + int (*iac_add)(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); - int (*iac_remove)(struct ieee80211com *, + int (*iac_remove)(struct ieee80211vap *, 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 *); + int (*iac_flush)(struct ieee80211vap *); + int (*iac_setpolicy)(struct ieee80211vap *, int); + int (*iac_getpolicy)(struct ieee80211vap *); + int (*iac_setioctl)(struct ieee80211vap *, struct ieee80211req *); + int (*iac_getioctl)(struct ieee80211vap *, struct ieee80211req *); }; void ieee80211_aclator_register(const struct ieee80211_aclator *); void ieee80211_aclator_unregister(const struct ieee80211_aclator *); @@ -190,11 +192,12 @@ 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_DOFRATE 0x00000002 /* use fixed legacy rate */ #define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ #define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ #define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */ #define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */ +#define IEEE80211_F_DOFMCS 0x00000040 /* use fixed HT rate */ int ieee80211_fix_rate(struct ieee80211_node *, struct ieee80211_rateset *, int); @@ -233,15 +236,38 @@ struct ieee80211_wme_state { int (*wme_update)(struct ieee80211com *); }; -void ieee80211_wme_initparams(struct ieee80211com *); -void ieee80211_wme_updateparams(struct ieee80211com *); -void ieee80211_wme_updateparams_locked(struct ieee80211com *); +void ieee80211_wme_initparams(struct ieee80211vap *); +void ieee80211_wme_updateparams(struct ieee80211vap *); +void ieee80211_wme_updateparams_locked(struct ieee80211vap *); + +/* + * Return the WME TID from a QoS frame. If no TID + * is present return the index for the "non-QoS" entry. + */ +static __inline uint8_t +ieee80211_gettid(const struct ieee80211_frame *wh) +{ + uint8_t tid; + + if (IEEE80211_QOS_HAS_SEQ(wh)) { + tid = ((const struct ieee80211_qosframe *)wh)-> + i_qos[0] & IEEE80211_QOS_TID; + tid++; + } else + tid = IEEE80211_NONQOS_TID; + return tid; +} -#define ieee80211_new_state(_ic, _nstate, _arg) \ - (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) -int ieee80211_init(struct ieee80211com *, int forcescan); -void ieee80211_dturbo_switch(struct ieee80211com *, int newflags); +void ieee80211_start_locked(struct ieee80211vap *); +void ieee80211_init(void *); +void ieee80211_start_all(struct ieee80211com *); +void ieee80211_stop_locked(struct ieee80211vap *); +void ieee80211_stop(struct ieee80211vap *); +void ieee80211_stop_all(struct ieee80211com *); +void ieee80211_dturbo_switch(struct ieee80211vap *, int newflags); +void ieee80211_swbmiss(void *arg); void ieee80211_beacon_miss(struct ieee80211com *); +int ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int); void ieee80211_print_essid(const uint8_t *, int); void ieee80211_dump_pkt(struct ieee80211com *, const uint8_t *, int, int, int); @@ -275,7 +301,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *, struct ieee80211_beacon_offsets *); /* - * Beacon frame updates are signaled through calls to ic_update_beacon + * Beacon frame updates are signaled through calls to iv_update_beacon * with one of the IEEE80211_BEACON_* tokens defined below. For devices * that construct beacon frames on the host this can trigger a rebuild * or defer the processing. For devices that offload beacon frame @@ -283,7 +309,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *, * array in the ieee80211_beacon_offsets structure is intended to record * deferred processing requirements; ieee80211_beacon_update uses the * state to optimize work. Since this structure is owned by the driver - * and not visible to the 802.11 layer drivers must supply an ic_update_beacon + * and not visible to the 802.11 layer drivers must supply an iv_update_beacon * callback that marks the flag bits and schedules (as necessary) an update. */ enum { @@ -299,14 +325,36 @@ enum { int ieee80211_beacon_update(struct ieee80211_node *, struct ieee80211_beacon_offsets *, struct mbuf *, int mcast); +void ieee80211_csa_startswitch(struct ieee80211com *, + struct ieee80211_channel *, int mode, int count); +void ieee80211_csa_completeswitch(struct ieee80211com *); +void ieee80211_cac_completeswitch(struct ieee80211vap *); + /* * 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 *); +void ieee80211_notify_node_join(struct ieee80211_node *, int newassoc); +void ieee80211_notify_node_leave(struct ieee80211_node *); +void ieee80211_notify_scan_done(struct ieee80211vap *); +void ieee80211_notify_wds_discover(struct ieee80211_node *); +void ieee80211_notify_csa(struct ieee80211com *, + const struct ieee80211_channel *, int mode, int count); +void ieee80211_notify_radar(struct ieee80211com *, + const struct ieee80211_channel *); +enum ieee80211_notify_cac_event { + IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */ + IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */ + IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */ + IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */ +}; +void ieee80211_notify_cac(struct ieee80211com *, + const struct ieee80211_channel *, + enum ieee80211_notify_cac_event); +void ieee80211_notify_node_deauth(struct ieee80211_node *); +void ieee80211_notify_node_auth(struct ieee80211_node *); +void ieee80211_notify_country(struct ieee80211vap *, const uint8_t [], + const uint8_t cc[2]); +void ieee80211_notify_radio(struct ieee80211com *, int); #endif /* _NET80211_IEEE80211_PROTO_H_ */ diff --git a/sys/net80211/ieee80211_regdomain.c b/sys/net80211/ieee80211_regdomain.c index 7f1b3dc..4cf2dc0 100644 --- a/sys/net80211/ieee80211_regdomain.c +++ b/sys/net80211/ieee80211_regdomain.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 regdomain support. */ +#include "opt_wlan.h" #include <sys/param.h> #include <sys/systm.h> @@ -37,26 +38,60 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> -#include <net/if_arp.h> -#include <net/if_dl.h> #include <net/if_media.h> -#include <net/if_types.h> -#include <net/ethernet.h> #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> +static void +null_getradiocaps(struct ieee80211com *ic, int *n, struct ieee80211_channel *c) +{ + /* just feed back the current channel list */ + *n = ic->ic_nchans; + memcpy(c, ic->ic_channels, + ic->ic_nchans*sizeof(struct ieee80211_channel)); +} + +static int +null_setregdomain(struct ieee80211com *ic, + struct ieee80211_regdomain *rd, + int nchans, struct ieee80211_channel chans[]) +{ + return 0; /* accept anything */ +} + void ieee80211_regdomain_attach(struct ieee80211com *ic) { - ic->ic_regdomain = 0; /* XXX */ - ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */ - ic->ic_location = 1+2; /* both */ + if (ic->ic_regdomain.regdomain == 0 && + ic->ic_regdomain.country == CTRY_DEFAULT) { + ic->ic_regdomain.country = CTRY_UNITED_STATES; /* XXX */ + ic->ic_regdomain.location = ' '; /* both */ + ic->ic_regdomain.isocc[0] = 'U'; /* XXX */ + ic->ic_regdomain.isocc[1] = 'S'; /* XXX */ + /* XXX? too late to setup default channel list */ + } + ic->ic_getradiocaps = null_getradiocaps; + ic->ic_setregdomain = null_setregdomain; } void ieee80211_regdomain_detach(struct ieee80211com *ic) { + if (ic->ic_countryie != NULL) { + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = NULL; + } +} + +void +ieee80211_regdomain_vattach(struct ieee80211vap *vap) +{ +} + +void +ieee80211_regdomain_vdetach(struct ieee80211vap *vap) +{ } static void @@ -68,6 +103,7 @@ addchan(struct ieee80211com *ic, int ieee, int flags) c->ic_freq = ieee80211_ieee2mhz(ieee, flags); c->ic_ieee = ieee; c->ic_flags = flags; + c->ic_extieee = 0; } /* @@ -76,24 +112,27 @@ addchan(struct ieee80211com *ic, int ieee, int flags) * when a driver does not obtain the channel list from another * source (such as firmware). */ -void +int ieee80211_init_channels(struct ieee80211com *ic, - int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm) + const struct ieee80211_regdomain *rd, const uint8_t bands[]) { int i; /* XXX just do something for now */ ic->ic_nchans = 0; - if (isset(&bands, IEEE80211_MODE_11B) || - isset(&bands, IEEE80211_MODE_11G)) { - for (i = 1; i <= (ecm ? 14 : 11); i++) { - if (isset(&bands, IEEE80211_MODE_11B)) + if (isset(bands, IEEE80211_MODE_11B) || + isset(bands, IEEE80211_MODE_11G)) { + int maxchan = 11; + if (rd != NULL && rd->ecm) + maxchan = 14; + for (i = 1; i <= maxchan; i++) { + if (isset(bands, IEEE80211_MODE_11B)) addchan(ic, i, IEEE80211_CHAN_B); - if (isset(&bands, IEEE80211_MODE_11G)) + if (isset(bands, IEEE80211_MODE_11G)) addchan(ic, i, IEEE80211_CHAN_G); } } - if (isset(&bands, IEEE80211_MODE_11A)) { + if (isset(bands, IEEE80211_MODE_11A)) { for (i = 36; i <= 64; i += 4) addchan(ic, i, IEEE80211_CHAN_A); for (i = 100; i <= 140; i += 4) @@ -101,17 +140,73 @@ ieee80211_init_channels(struct ieee80211com *ic, for (i = 149; i <= 161; i += 4) addchan(ic, i, IEEE80211_CHAN_A); } - ic->ic_regdomain = rd; - ic->ic_countrycode = cc; - ic->ic_location = outdoor; + if (rd != NULL) + ic->ic_regdomain = *rd; + + return 0; +} + +static __inline int +chancompar(const void *a, const void *b) +{ + const struct ieee80211_channel *ca = a; + const struct ieee80211_channel *cb = b; + + return (ca->ic_freq == cb->ic_freq) ? + (ca->ic_flags & IEEE80211_CHAN_ALL) - + (cb->ic_flags & IEEE80211_CHAN_ALL) : + ca->ic_freq - cb->ic_freq; +} + +/* + * Insertion sort. + */ +#define swap(_a, _b, _size) { \ + uint8_t *s = _b; \ + int i = _size; \ + do { \ + uint8_t tmp = *_a; \ + *_a++ = *s; \ + *s++ = tmp; \ + } while (--i); \ + _a -= _size; \ +} + +static void +sort_channels(void *a, size_t n, size_t size) +{ + uint8_t *aa = a; + uint8_t *ai, *t; + + KASSERT(n > 0, ("no channels")); + for (ai = aa+size; --n >= 1; ai += size) + for (t = ai; t > aa; t -= size) { + uint8_t *u = t - size; + if (chancompar(u, t) <= 0) + break; + swap(u, t, size); + } } +#undef swap /* - * Add Country Information IE. + * Order channels w/ the same frequency so that + * b < g < htg and a < hta. This is used to optimize + * channel table lookups and some user applications + * may also depend on it (though they should not). */ -uint8_t * -ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic, - enum ISOCountryCode cc, int location) +void +ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans) +{ + if (nchans > 0) + sort_channels(chans, nchans, sizeof(struct ieee80211_channel)); +} + +/* + * Allocate and construct a Country Information IE. + */ +struct ieee80211_appie * +ieee80211_alloc_countryie(struct ieee80211com *ic) { #define CHAN_UNINTERESTING \ (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \ @@ -131,35 +226,46 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic, CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */ CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */ }; - struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm; - const char *iso_name; - uint8_t nextchan, chans[IEEE80211_CHAN_BYTES]; - int i, skip; + const struct ieee80211_regdomain *rd = &ic->ic_regdomain; + uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm; + struct ieee80211_appie *aie; + struct ieee80211_country_ie *ie; + int i, skip, nruns; + aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE, + M_NOWAIT | M_ZERO); + if (aie == NULL) { + if_printf(ic->ic_ifp, + "%s: unable to allocate memory for country ie\n", __func__); + /* XXX stat */ + return NULL; + } + ie = (struct ieee80211_country_ie *) aie->ie_data; ie->ie = IEEE80211_ELEMID_COUNTRY; - iso_name = ieee80211_cctoiso(cc); - if (iso_name == NULL) { - if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc); - iso_name = " "; + if (rd->isocc[0] == '\0') { + if_printf(ic->ic_ifp, "no ISO country string for cc %d; " + "using blanks\n", rd->country); + ie->cc[0] = ie->cc[1] = ' '; + } else { + ie->cc[0] = rd->isocc[0]; + ie->cc[1] = rd->isocc[1]; } - ie->cc[0] = iso_name[0]; - ie->cc[1] = iso_name[1]; /* - * Indoor/Outdoor portion of country string. - * NB: this is not quite right, since we should have one of: + * Indoor/Outdoor portion of country string: * 'I' indoor only * 'O' outdoor only * ' ' all enviroments */ - ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O'); - + ie->cc[2] = (rd->location == 'I' ? 'I' : + rd->location == 'O' ? 'O' : ' '); /* * Run-length encoded channel+max tx power info. */ frm = (uint8_t *)&ie->band[0]; nextchan = 0; /* NB: impossible channel # */ + nruns = 0; memset(chans, 0, sizeof(chans)); - skip = skipflags[ic->ic_curmode]; + skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)]; for (i = 0; i < ic->ic_nchans; i++) { const struct ieee80211_channel *c = &ic->ic_channels[i]; @@ -170,12 +276,19 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic, setbit(chans, c->ic_ieee); if (c->ic_ieee != nextchan || c->ic_maxregpower != frm[-1]) { /* new run */ - /* XXX max of 83 runs */ + if (nruns == IEEE80211_COUNTRY_MAX_BANDS) { + if_printf(ic->ic_ifp, "%s: country ie too big, " + "runs > max %d, truncating\n", + __func__, IEEE80211_COUNTRY_MAX_BANDS); + /* XXX stat? fail? */ + break; + } frm[0] = c->ic_ieee; /* starting channel # */ frm[1] = 1; /* # channels in run */ frm[2] = c->ic_maxregpower; /* tx power cap */ frm += 3; nextchan = c->ic_ieee + 1; /* overflow? */ + nruns++; } else { /* extend run */ frm[-2]++; nextchan++; @@ -186,154 +299,114 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic, ie->len++; *frm++ = 0; } - return frm; + aie->ie_len = frm - aie->ie_data; + + return aie; #undef CHAN_UNINTERESTING } -/* - * Country Code Table for code-to-string conversion. - */ -static const struct { - enum ISOCountryCode iso_code; - const char* iso_name; -} country_strings[] = { - { CTRY_DEBUG, "DB" }, /* NB: nonstandard */ - { CTRY_DEFAULT, "NA" }, /* NB: nonstandard */ - { CTRY_ALBANIA, "AL" }, - { CTRY_ALGERIA, "DZ" }, - { CTRY_ARGENTINA, "AR" }, - { CTRY_ARMENIA, "AM" }, - { CTRY_AUSTRALIA, "AU" }, - { CTRY_AUSTRIA, "AT" }, - { CTRY_AZERBAIJAN, "AZ" }, - { CTRY_BAHRAIN, "BH" }, - { CTRY_BELARUS, "BY" }, - { CTRY_BELGIUM, "BE" }, - { CTRY_BELIZE, "BZ" }, - { CTRY_BOLIVIA, "BO" }, - { CTRY_BRAZIL, "BR" }, - { CTRY_BRUNEI_DARUSSALAM, "BN" }, - { CTRY_BULGARIA, "BG" }, - { CTRY_CANADA, "CA" }, - { CTRY_CHILE, "CL" }, - { CTRY_CHINA, "CN" }, - { CTRY_COLOMBIA, "CO" }, - { CTRY_COSTA_RICA, "CR" }, - { CTRY_CROATIA, "HR" }, - { CTRY_CYPRUS, "CY" }, - { CTRY_CZECH, "CZ" }, - { CTRY_DENMARK, "DK" }, - { CTRY_DOMINICAN_REPUBLIC, "DO" }, - { CTRY_ECUADOR, "EC" }, - { CTRY_EGYPT, "EG" }, - { CTRY_EL_SALVADOR, "SV" }, - { CTRY_ESTONIA, "EE" }, - { CTRY_FINLAND, "FI" }, - { CTRY_FRANCE, "FR" }, - { CTRY_FRANCE2, "F2" }, - { CTRY_GEORGIA, "GE" }, - { CTRY_GERMANY, "DE" }, - { CTRY_GREECE, "GR" }, - { CTRY_GUATEMALA, "GT" }, - { CTRY_HONDURAS, "HN" }, - { CTRY_HONG_KONG, "HK" }, - { CTRY_HUNGARY, "HU" }, - { CTRY_ICELAND, "IS" }, - { CTRY_INDIA, "IN" }, - { CTRY_INDONESIA, "ID" }, - { CTRY_IRAN, "IR" }, - { CTRY_IRELAND, "IE" }, - { CTRY_ISRAEL, "IL" }, - { CTRY_ITALY, "IT" }, - { CTRY_JAMAICA, "JM" }, - { CTRY_JAPAN, "JP" }, - { CTRY_JAPAN1, "J1" }, - { CTRY_JAPAN2, "J2" }, - { CTRY_JAPAN3, "J3" }, - { CTRY_JAPAN4, "J4" }, - { CTRY_JAPAN5, "J5" }, - { CTRY_JORDAN, "JO" }, - { CTRY_KAZAKHSTAN, "KZ" }, - { CTRY_KOREA_NORTH, "KP" }, - { CTRY_KOREA_ROC, "KR" }, - { CTRY_KOREA_ROC2, "K2" }, - { CTRY_KUWAIT, "KW" }, - { CTRY_LATVIA, "LV" }, - { CTRY_LEBANON, "LB" }, - { CTRY_LIECHTENSTEIN, "LI" }, - { CTRY_LITHUANIA, "LT" }, - { CTRY_LUXEMBOURG, "LU" }, - { CTRY_MACAU, "MO" }, - { CTRY_MACEDONIA, "MK" }, - { CTRY_MALAYSIA, "MY" }, - { CTRY_MEXICO, "MX" }, - { CTRY_MONACO, "MC" }, - { CTRY_MOROCCO, "MA" }, - { CTRY_NETHERLANDS, "NL" }, - { CTRY_NEW_ZEALAND, "NZ" }, - { CTRY_NORWAY, "NO" }, - { CTRY_OMAN, "OM" }, - { CTRY_PAKISTAN, "PK" }, - { CTRY_PANAMA, "PA" }, - { CTRY_PERU, "PE" }, - { CTRY_PHILIPPINES, "PH" }, - { CTRY_POLAND, "PL" }, - { CTRY_PORTUGAL, "PT" }, - { CTRY_PUERTO_RICO, "PR" }, - { CTRY_QATAR, "QA" }, - { CTRY_ROMANIA, "RO" }, - { CTRY_RUSSIA, "RU" }, - { CTRY_SAUDI_ARABIA, "SA" }, - { CTRY_SINGAPORE, "SG" }, - { CTRY_SLOVAKIA, "SK" }, - { CTRY_SLOVENIA, "SI" }, - { CTRY_SOUTH_AFRICA, "ZA" }, - { CTRY_SPAIN, "ES" }, - { CTRY_SWEDEN, "SE" }, - { CTRY_SWITZERLAND, "CH" }, - { CTRY_SYRIA, "SY" }, - { CTRY_TAIWAN, "TW" }, - { CTRY_THAILAND, "TH" }, - { CTRY_TRINIDAD_Y_TOBAGO, "TT" }, - { CTRY_TUNISIA, "TN" }, - { CTRY_TURKEY, "TR" }, - { CTRY_UKRAINE, "UA" }, - { CTRY_UAE, "AE" }, - { CTRY_UNITED_KINGDOM, "GB" }, - { CTRY_UNITED_STATES, "US" }, - { CTRY_URUGUAY, "UY" }, - { CTRY_UZBEKISTAN, "UZ" }, - { CTRY_VENEZUELA, "VE" }, - { CTRY_VIET_NAM, "VN" }, - { CTRY_YEMEN, "YE" }, - { CTRY_ZIMBABWE, "ZW" } -}; - -const char * -ieee80211_cctoiso(enum ISOCountryCode cc) +static int +allvapsdown(struct ieee80211com *ic) { -#define N(a) (sizeof(a) / sizeof(a[0])) - int i; + struct ieee80211vap *vap; - for (i = 0; i < N(country_strings); i++) { - if (country_strings[i].iso_code == cc) - return country_strings[i].iso_name; - } - return NULL; -#undef N + IEEE80211_LOCK_ASSERT(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state != IEEE80211_S_INIT) + return 0; + return 1; } int -ieee80211_isotocc(const char iso[2]) +ieee80211_setregdomain(struct ieee80211vap *vap, + struct ieee80211_regdomain_req *reg) { -#define N(a) (sizeof(a) / sizeof(a[0])) - int i; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; + int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */ + int error, i; - for (i = 0; i < N(country_strings); i++) { - if (country_strings[i].iso_name[0] == iso[0] && - country_strings[i].iso_name[1] == iso[1]) - return country_strings[i].iso_code; + if (reg->rd.location != 'I' && reg->rd.location != 'O' && + reg->rd.location != ' ') + return EINVAL; + if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0') + return EINVAL; + if (reg->chaninfo.ic_nchans >= IEEE80211_CHAN_MAX) + return EINVAL; + /* + * Calculate freq<->IEEE mapping and default max tx power + * for channels not setup. The driver can override these + * setting to reflect device properties/requirements. + */ + for (i = 0; i < reg->chaninfo.ic_nchans; i++) { + c = ®->chaninfo.ic_chans[i]; + if (c->ic_freq == 0 || c->ic_flags == 0) + return EINVAL; + if (c->ic_maxregpower == 0) + return EINVAL; + if (c->ic_ieee == 0) + c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); + if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) + c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + + (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), + c->ic_flags); + if (c->ic_maxpower == 0) + c->ic_maxpower = 2*c->ic_maxregpower; + } + IEEE80211_LOCK(ic); + error = ic->ic_setregdomain(ic, ®->rd, + reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans); + if (error != 0) { + IEEE80211_UNLOCK(ic); + return error; } - return -1; -#undef N + /* XXX bandaid; a running vap will likely crash */ + if (!allvapsdown(ic)) { + IEEE80211_UNLOCK(ic); + return EBUSY; + } + /* + * Commit: copy in new channel table and reset media state. + * On return the state machines will be clocked so all vaps + * will reset their state. + * + * XXX ic_bsschan is marked undefined, must have vap's in + * INIT state or we blow up forcing stations off + */ + /* + * Save any desired channel for restore below. Note this + * needs to be done for all vaps but for now we only do + * the one where the ioctl is issued. + */ + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { + desfreq = vap->iv_des_chan->ic_freq; + desflags = vap->iv_des_chan->ic_flags; + } + /* regdomain parameters */ + memcpy(&ic->ic_regdomain, ®->rd, sizeof(reg->rd)); + /* channel table */ + memcpy(ic->ic_channels, reg->chaninfo.ic_chans, + reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel)); + ic->ic_nchans = reg->chaninfo.ic_nchans; + memset(&ic->ic_channels[ic->ic_nchans], 0, + (IEEE80211_CHAN_MAX - ic->ic_nchans) * + sizeof(struct ieee80211_channel)); + ieee80211_media_init(ic); + + /* + * Invalidate channel-related state. + */ + if (ic->ic_countryie != NULL) { + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = NULL; + } + ieee80211_scan_flush(vap); + ieee80211_dfs_reset(ic); + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { + /* NB: may be NULL if not present in new channel list */ + vap->iv_des_chan = ieee80211_find_channel(ic, desfreq, desflags); + } + IEEE80211_UNLOCK(ic); + + return 0; } diff --git a/sys/net80211/ieee80211_regdomain.h b/sys/net80211/ieee80211_regdomain.h index 9c1345e..c9c0823 100644 --- a/sys/net80211/ieee80211_regdomain.h +++ b/sys/net80211/ieee80211_regdomain.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,42 +43,92 @@ enum ISOCountryCode { CTRY_ANDORRA = 20, CTRY_ANGOLA = 24, CTRY_ANGUILLA = 660, - /* XXX correct remainder */ + CTRY_ANTARTICA = 10, + CTRY_ANTIGUA = 28, /* Antigua and Barbuda */ CTRY_ARGENTINA = 32, /* Argentina */ CTRY_ARMENIA = 51, /* Armenia */ + CTRY_ARUBA = 533, /* Aruba */ CTRY_AUSTRALIA = 36, /* Australia */ CTRY_AUSTRIA = 40, /* Austria */ CTRY_AZERBAIJAN = 31, /* Azerbaijan */ + CTRY_BAHAMAS = 44, /* Bahamas */ CTRY_BAHRAIN = 48, /* Bahrain */ + CTRY_BANGLADESH = 50, /* Bangladesh */ + CTRY_BARBADOS = 52, CTRY_BELARUS = 112, /* Belarus */ CTRY_BELGIUM = 56, /* Belgium */ - CTRY_BELIZE = 84, /* Belize */ + CTRY_BELIZE = 84, + CTRY_BENIN = 204, + CTRY_BERMUDA = 60, + CTRY_BHUTAN = 64, CTRY_BOLIVIA = 68, /* Bolivia */ + CTRY_BOSNIA_AND_HERZEGOWINA = 70, + CTRY_BOTSWANA = 72, + CTRY_BOUVET_ISLAND = 74, CTRY_BRAZIL = 76, /* Brazil */ + CTRY_BRITISH_INDIAN_OCEAN_TERRITORY = 86, CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */ CTRY_BULGARIA = 100, /* Bulgaria */ + CTRY_BURKINA_FASO = 854, + CTRY_BURUNDI = 108, + CTRY_CAMBODIA = 116, + CTRY_CAMEROON = 120, CTRY_CANADA = 124, /* Canada */ + CTRY_CAPE_VERDE = 132, + CTRY_CAYMAN_ISLANDS = 136, + CTRY_CENTRAL_AFRICAN_REPUBLIC = 140, + CTRY_CHAD = 148, CTRY_CHILE = 152, /* Chile */ CTRY_CHINA = 156, /* People's Republic of China */ + CTRY_CHRISTMAS_ISLAND = 162, + CTRY_COCOS_ISLANDS = 166, CTRY_COLOMBIA = 170, /* Colombia */ + CTRY_COMOROS = 174, + CTRY_CONGO = 178, + CTRY_COOK_ISLANDS = 184, CTRY_COSTA_RICA = 188, /* Costa Rica */ - CTRY_CROATIA = 191, /* Croatia */ + CTRY_COTE_DIVOIRE = 384, + CTRY_CROATIA = 191, /* Croatia (local name: Hrvatska) */ CTRY_CYPRUS = 196, /* Cyprus */ CTRY_CZECH = 203, /* Czech Republic */ CTRY_DENMARK = 208, /* Denmark */ + CTRY_DJIBOUTI = 262, + CTRY_DOMINICA = 212, CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */ + CTRY_EAST_TIMOR = 626, CTRY_ECUADOR = 218, /* Ecuador */ CTRY_EGYPT = 818, /* Egypt */ CTRY_EL_SALVADOR = 222, /* El Salvador */ + CTRY_EQUATORIAL_GUINEA = 226, + CTRY_ERITREA = 232, CTRY_ESTONIA = 233, /* Estonia */ + CTRY_ETHIOPIA = 210, + CTRY_FALKLAND_ISLANDS = 238, /* (Malvinas) */ CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */ + CTRY_FIJI = 242, CTRY_FINLAND = 246, /* Finland */ CTRY_FRANCE = 250, /* France */ - CTRY_FRANCE2 = 255, /* France2 */ + CTRY_FRANCE2 = 255, /* France (Metropolitan) */ + CTRY_FRENCH_GUIANA = 254, + CTRY_FRENCH_POLYNESIA = 258, + CTRY_FRENCH_SOUTHERN_TERRITORIES = 260, + CTRY_GABON = 266, + CTRY_GAMBIA = 270, CTRY_GEORGIA = 268, /* Georgia */ CTRY_GERMANY = 276, /* Germany */ + CTRY_GHANA = 288, + CTRY_GIBRALTAR = 292, CTRY_GREECE = 300, /* Greece */ + CTRY_GREENLAND = 304, + CTRY_GRENADA = 308, + CTRY_GUADELOUPE = 312, + CTRY_GUAM = 316, CTRY_GUATEMALA = 320, /* Guatemala */ + CTRY_GUINEA = 324, + CTRY_GUINEA_BISSAU = 624, + CTRY_GUYANA = 328, + /* XXX correct remainder */ + CTRY_HAITI = 332, CTRY_HONDURAS = 340, /* Honduras */ CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */ CTRY_HUNGARY = 348, /* Hungary */ @@ -113,9 +163,11 @@ enum ISOCountryCode { CTRY_MACAU = 446, /* Macau */ CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */ CTRY_MALAYSIA = 458, /* Malaysia */ + CTRY_MALTA = 470, /* Malta */ CTRY_MEXICO = 484, /* Mexico */ CTRY_MONACO = 492, /* Principality of Monaco */ CTRY_MOROCCO = 504, /* Morocco */ + CTRY_NEPAL = 524, /* Nepal */ CTRY_NETHERLANDS = 528, /* Netherlands */ CTRY_NEW_ZEALAND = 554, /* New Zealand */ CTRY_NICARAGUA = 558, /* Nicaragua */ @@ -138,6 +190,7 @@ enum ISOCountryCode { CTRY_SLOVENIA = 705, /* Slovenia */ CTRY_SOUTH_AFRICA = 710, /* South Africa */ CTRY_SPAIN = 724, /* Spain */ + CTRY_SRILANKA = 144, /* Sri Lanka */ CTRY_SWEDEN = 752, /* Sweden */ CTRY_SWITZERLAND = 756, /* Switzerland */ CTRY_SYRIA = 760, /* Syria */ @@ -158,18 +211,39 @@ enum ISOCountryCode { CTRY_ZIMBABWE = 716, /* Zimbabwe */ }; +enum RegdomainCode { + SKU_FCC = 0x10, /* FCC, aka United States */ + SKU_CA = 0x20, /* North America, aka Canada */ + SKU_ETSI = 0x30, /* Europe */ + SKU_ETSI2 = 0x32, /* Europe w/o HT40 in 5GHz */ + SKU_ETSI3 = 0x33, /* Europe - channel 36 */ + SKU_FCC3 = 0x3a, /* FCC w/5470 band, 11h, DFS */ + SKU_JAPAN = 0x40, + SKU_KOREA = 0x45, + SKU_APAC = 0x50, /* Asia Pacific */ + SKU_APAC2 = 0x51, /* Asia Pacific w/ DFS on mid-band */ + SKU_APAC3 = 0x5d, /* Asia Pacific w/o ISM band */ + SKU_ROW = 0x81, /* China/Taiwan/Rest of World */ + SKU_NONE = 0xf0, /* "Region Free" */ + SKU_DEBUG = 0x1ff +}; + #if defined(__KERNEL__) || defined(_KERNEL) #define CTRY_DEBUG 0x1ff /* debug */ #define CTRY_DEFAULT 0 /* default */ void ieee80211_regdomain_attach(struct ieee80211com *); void ieee80211_regdomain_detach(struct ieee80211com *); +void ieee80211_regdomain_vattach(struct ieee80211vap *); +void ieee80211_regdomain_vdetach(struct ieee80211vap *); -void ieee80211_init_channels(struct ieee80211com *ic, - int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm); -uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *, - enum ISOCountryCode cc, int location); -const char *ieee80211_cctoiso(enum ISOCountryCode); -int ieee80211_isotocc(const char iso[2]); +int ieee80211_init_channels(struct ieee80211com *, + const struct ieee80211_regdomain *, const uint8_t bands[]); +void ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans); +struct ieee80211_appie; +struct ieee80211_appie *ieee80211_alloc_countryie(struct ieee80211com *); +struct ieee80211_regdomain_req; +int ieee80211_setregdomain(struct ieee80211vap *, + struct ieee80211_regdomain_req *); #endif /* defined(__KERNEL__) || defined(_KERNEL) */ #endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */ diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c new file mode 100644 index 0000000..f1fc409 --- /dev/null +++ b/sys/net80211/ieee80211_rssadapt.c @@ -0,0 +1,273 @@ +/* $FreeBSD$ */ +/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */ +/*- + * Copyright (c) 2003, 2004 David Young. 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 David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``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 David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_rssadapt.h> + +struct rssadapt_expavgctl { + /* RSS threshold decay. */ + u_int rc_decay_denom; + u_int rc_decay_old; + /* RSS threshold update. */ + u_int rc_thresh_denom; + u_int rc_thresh_old; + /* RSS average update. */ + u_int rc_avgrssi_denom; + u_int rc_avgrssi_old; +}; + +static struct rssadapt_expavgctl master_expavgctl = { + rc_decay_denom : 16, + rc_decay_old : 15, + rc_thresh_denom : 8, + rc_thresh_old : 4, + rc_avgrssi_denom : 8, + rc_avgrssi_old : 4 +}; + +#ifdef interpolate +#undef interpolate +#endif +#define interpolate(parm, old, new) ((parm##_old * (old) + \ + (parm##_denom - parm##_old) * (new)) / \ + parm##_denom) + +static void rssadapt_sysctlattach(struct ieee80211_rssadapt *rs, + struct sysctl_ctx_list *ctx, struct sysctl_oid *tree); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +void +ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *rs, int msecs) +{ + int t; + + if (msecs < 100) + msecs = 100; + t = msecs_to_ticks(msecs); + rs->interval = (t < 1) ? 1 : t; +} + +void +ieee80211_rssadapt_init(struct ieee80211_rssadapt *rs, struct ieee80211vap *vap, int interval) +{ + rs->vap = vap; + ieee80211_rssadapt_setinterval(rs, interval); + + rssadapt_sysctlattach(rs, vap->iv_sysctl, vap->iv_oid); +} + +void +ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *rs) +{ +} + +static void +rssadapt_updatestats(struct ieee80211_rssadapt_node *ra) +{ + long interval; + + ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2; + ra->ra_nfail = ra->ra_nok = 0; + + /* + * A node is eligible for its rate to be raised every 1/10 to 10 + * seconds, more eligible in proportion to recent packet rates. + */ + interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate)); + ra->ra_raise_interval = msecs_to_ticks(interval); +} + +void +ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *rsa, + struct ieee80211_rssadapt_node *ra, struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs = &ni->ni_rates; + + ra->ra_rs = rsa; + ra->ra_rates = *rs; + rssadapt_updatestats(ra); + + /* pick initial rate */ + for (ra->ra_rix = rs->rs_nrates - 1; + ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72; + ra->ra_rix--) + ; + ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL; + ra->ra_ticks = ticks; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "RSSADAPT initial rate %d", ni->ni_txrate); +} + +static __inline int +bucket(int pktlen) +{ + int i, top, thridx; + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thridx = i; + if (pktlen <= top) + break; + } + return thridx; +} + +int +ieee80211_rssadapt_choose(struct ieee80211_node *ni, + struct ieee80211_rssadapt_node *ra, u_int pktlen) +{ + const struct ieee80211_rateset *rs = &ra->ra_rates; + uint16_t (*thrs)[IEEE80211_RATE_SIZE]; + int rix, rssi; + + if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) { + rssadapt_updatestats(ra); + ra->ra_ticks = ticks; + } + + thrs = &ra->ra_rate_thresh[bucket(pktlen)]; + + /* XXX this is average rssi, should be using last value */ + rssi = ni->ni_ic->ic_node_getrssi(ni); + for (rix = rs->rs_nrates-1; rix >= 0; rix--) + if ((*thrs)[rix] < (rssi << 8)) + break; + if (rix != ra->ra_rix) { + /* update public rate */ + ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL; + ra->ra_rix = rix; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "RSSADAPT new rate %d (pktlen %d rssi %d)", + ni->ni_txrate, pktlen, rssi); + } + return rix; +} + +/* + * Adapt the data rate to suit the conditions. When a transmitted + * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions, + * raise the RSS threshold for transmitting packets of similar length at + * the same data rate. + */ +void +ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra, + int pktlen, int rssi) +{ + uint16_t last_thr; + uint16_t (*thrs)[IEEE80211_RATE_SIZE]; + u_int rix; + + thrs = &ra->ra_rate_thresh[bucket(pktlen)]; + + rix = ra->ra_rix; + last_thr = (*thrs)[rix]; + (*thrs)[rix] = interpolate(master_expavgctl.rc_thresh, + last_thr, (rssi << 8)); + + IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL, + "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n", + ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL, + last_thr, (*thrs)[rix], rssi); +} + +void +ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra, + int pktlen, int rssi) +{ + uint16_t (*thrs)[IEEE80211_RATE_SIZE]; + uint16_t newthr, oldthr; + int rix; + + thrs = &ra->ra_rate_thresh[bucket(pktlen)]; + + rix = ra->ra_rix; + if ((*thrs)[rix + 1] > (*thrs)[rix]) { + oldthr = (*thrs)[rix + 1]; + if ((*thrs)[rix] == 0) + newthr = (rssi << 8); + else + newthr = (*thrs)[rix]; + (*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay, + oldthr, newthr); + + IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL, + "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n", + ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL, + oldthr, newthr, rssi); + + ra->ra_last_raise = ticks; + } +} + +static int +rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211_rssadapt *rs = arg1; + int msecs = ticks_to_msecs(rs->interval); + int error; + + error = sysctl_handle_int(oidp, &msecs, 0, req); + if (error || !req->newptr) + return error; + ieee80211_rssadapt_setinterval(rs, msecs); + return 0; +} + +static void +rssadapt_sysctlattach(struct ieee80211_rssadapt *rs, + struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) +{ + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, rs, + 0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)"); +} + +/* + * Module glue. + */ +IEEE80211_RATE_MODULE(rssadapt, 1); diff --git a/sys/net80211/ieee80211_rssadapt.h b/sys/net80211/ieee80211_rssadapt.h new file mode 100644 index 0000000..b454f43 --- /dev/null +++ b/sys/net80211/ieee80211_rssadapt.h @@ -0,0 +1,101 @@ +/* $FreeBSD$ */ +/* $NetBSD: ieee80211_rssadapt.h,v 1.4 2005/02/26 22:45:09 perry Exp $ */ +/*- + * Copyright (c) 2003, 2004 David Young. 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 David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``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 David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#ifndef _NET80211_IEEE80211_RSSADAPT_H_ +#define _NET80211_IEEE80211_RSSADAPT_H_ + +/* Data-rate adaptation loosely based on "Link Adaptation Strategy + * for IEEE 802.11 WLAN via Received Signal Strength Measurement" + * by Javier del Prado Pavon and Sunghyun Choi. + */ + +/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */ +#define IEEE80211_RSSADAPT_BKTS 3 +#define IEEE80211_RSSADAPT_BKT0 128 +#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */ + +struct ieee80211_rssadapt { + struct ieee80211vap *vap; + int interval; /* update interval (ticks) */ +}; + +struct ieee80211_rssadapt_node { + struct ieee80211_rssadapt *ra_rs; /* backpointer */ + struct ieee80211_rateset ra_rates; /* negotiated rates */ + int ra_rix; /* current rate index */ + int ra_ticks; /* time of last update */ + int ra_last_raise; /* time of last rate raise */ + int ra_raise_interval; /* rate raise time threshold */ + + /* Tx failures in this update interval */ + uint32_t ra_nfail; + /* Tx successes in this update interval */ + uint32_t ra_nok; + /* exponential average packets/second */ + uint32_t ra_pktrate; + /* RSSI threshold for each Tx rate */ + uint16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS] + [IEEE80211_RATE_SIZE]; +}; + +void ieee80211_rssadapt_init(struct ieee80211_rssadapt *, + struct ieee80211vap *, int); +void ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *); +void ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *, int); +void ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *, + struct ieee80211_rssadapt_node *, struct ieee80211_node *); +int ieee80211_rssadapt_choose(struct ieee80211_node *, + struct ieee80211_rssadapt_node *, u_int); + +/* NB: these are public only for the inline below */ +void ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *, + int pktlen, int rssi); +void ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *, + int pktlen, int rssi); + +#define IEEE80211_RSSADAPT_SUCCESS 1 +#define IEEE80211_RSSADAPT_FAILURE 0 + +static __inline void +ieee80211_rssadapt_tx_complete(struct ieee80211_rssadapt_node *ra, + int success, int pktlen, int rssi) +{ + if (success) { + ra->ra_nok++; + if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates && + (ticks - ra->ra_last_raise) >= ra->ra_raise_interval) + ieee80211_rssadapt_raise_rate(ra, pktlen, rssi); + } else { + ra->ra_nfail++; + ieee80211_rssadapt_lower_rate(ra, pktlen, rssi); + } +} +#endif /* _NET80211_IEEE80211_RSSADAPT_H_ */ diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c index 6f6b9a6..cec9673 100644 --- a/sys/net80211/ieee80211_scan.c +++ b/sys/net80211/ieee80211_scan.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 scanning support. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -72,7 +74,7 @@ struct scan_state { /* * Roaming-related defaults. RSSI thresholds are as returned by the * driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e - * .5M units). + * .5M units) or MCS. */ #define ROAM_RSSI_11A_DEFAULT 14 /* rssi threshold for 11a bss */ #define ROAM_RSSI_11B_DEFAULT 14 /* rssi threshold for 11b bss */ @@ -80,10 +82,11 @@ struct scan_state { #define ROAM_RATE_11A_DEFAULT 2*12 /* tx rate thresh for 11a bss */ #define ROAM_RATE_11B_DEFAULT 2*5 /* tx rate thresh for 11b bss */ #define ROAM_RATE_11BONLY_DEFAULT 2*1 /* tx rate thresh for 11b-only bss */ +#define ROAM_MCS_11N_DEFAULT 1 /* tx MCS thresh for 11n bss*/ static void scan_restart_pwrsav(void *); -static void scan_curchan(struct ieee80211com *, unsigned long); -static void scan_mindwell(struct ieee80211com *); +static void scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void scan_mindwell(struct ieee80211_scan_state *); static void scan_next(void *); MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); @@ -93,29 +96,17 @@ ieee80211_scan_attach(struct ieee80211com *ic) { struct scan_state *ss; - ic->ic_roaming = IEEE80211_ROAMING_AUTO; - MALLOC(ss, struct scan_state *, sizeof(struct scan_state), M_80211_SCAN, M_NOWAIT | M_ZERO); if (ss == NULL) { ic->ic_scan = NULL; return; } - callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE); + callout_init_mtx(&ss->ss_scan_timer, &ic->ic_comlock, 0); ic->ic_scan = &ss->base; ic->ic_scan_curchan = scan_curchan; ic->ic_scan_mindwell = scan_mindwell; - - ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz; - ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz; - ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz; - ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT; - ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT; - ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT; - ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT; - ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT; - ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT; } void @@ -135,31 +126,94 @@ ieee80211_scan_detach(struct ieee80211com *ic) } } +static __inline void +setparams(struct ieee80211_roamparam *rp, int8_t rssi, uint8_t txrate) +{ + rp->rssi = rssi; + rp->rate = txrate; +} + +void +ieee80211_scan_vattach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz; + vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz; + vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz; + + vap->iv_roaming = IEEE80211_ROAMING_AUTO; + + /* NB: only set supported modes so user apps can identify */ + if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) + setparams(&vap->iv_roamparms[IEEE80211_MODE_11A], + ROAM_RSSI_11A_DEFAULT, ROAM_RATE_11A_DEFAULT); + if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) + setparams(&vap->iv_roamparms[IEEE80211_MODE_11G], + ROAM_RSSI_11B_DEFAULT, ROAM_RATE_11B_DEFAULT); + if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) + setparams(&vap->iv_roamparms[IEEE80211_MODE_11B], + ROAM_RSSI_11BONLY_DEFAULT, ROAM_RATE_11BONLY_DEFAULT); + /* NB: default turbo controls to be the same as !turbo */ + if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_A)) + vap->iv_roamparms[IEEE80211_MODE_TURBO_A] = + vap->iv_roamparms[IEEE80211_MODE_11A]; + if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_G)) + vap->iv_roamparms[IEEE80211_MODE_TURBO_G] = + vap->iv_roamparms[IEEE80211_MODE_11G]; + if (isset(ic->ic_modecaps, IEEE80211_MODE_STURBO_A)) + vap->iv_roamparms[IEEE80211_MODE_STURBO_A] = + vap->iv_roamparms[IEEE80211_MODE_11A]; + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) + setparams(&vap->iv_roamparms[IEEE80211_MODE_11NA], + ROAM_RSSI_11A_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80); + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) + setparams(&vap->iv_roamparms[IEEE80211_MODE_11NG], + ROAM_RSSI_11B_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80); +} + +void +ieee80211_scan_vdetach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss; + + IEEE80211_LOCK(ic); + ss = ic->ic_scan; + if (ss != NULL && ss->ss_vap == vap) { + if (ic->ic_flags & IEEE80211_F_SCAN) { + /* XXX callout_drain */ + callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer); + ic->ic_flags &= ~IEEE80211_F_SCAN; + } + if (ss->ss_ops != NULL) { + ss->ss_ops->scan_detach(ss); + ss->ss_ops = NULL; + } + ss->ss_vap = NULL; + } + IEEE80211_UNLOCK(ic); +} + /* * Simple-minded scanner module support. */ -#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1) - -static const char *scan_modnames[IEEE80211_SCANNER_MAX] = { +static const char *scan_modnames[IEEE80211_OPMODE_MAX] = { "wlan_scan_sta", /* IEEE80211_M_IBSS */ "wlan_scan_sta", /* IEEE80211_M_STA */ "wlan_scan_wds", /* IEEE80211_M_WDS */ "wlan_scan_sta", /* IEEE80211_M_AHDEMO */ - "wlan_scan_4", /* n/a */ - "wlan_scan_5", /* n/a */ "wlan_scan_ap", /* IEEE80211_M_HOSTAP */ - "wlan_scan_7", /* n/a */ "wlan_scan_monitor", /* IEEE80211_M_MONITOR */ }; -static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX]; +static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX]; const struct ieee80211_scanner * ieee80211_scanner_get(enum ieee80211_opmode mode) { - if (mode >= IEEE80211_SCANNER_MAX) + if (mode >= IEEE80211_OPMODE_MAX) return NULL; - /* NB: avoid monitor mode; there is no scan support */ - if (mode != IEEE80211_M_MONITOR && scanners[mode] == NULL) + if (scanners[mode] == NULL) ieee80211_load_module(scan_modnames[mode]); return scanners[mode]; } @@ -168,7 +222,7 @@ void ieee80211_scanner_register(enum ieee80211_opmode mode, const struct ieee80211_scanner *scan) { - if (mode >= IEEE80211_SCANNER_MAX) + if (mode >= IEEE80211_OPMODE_MAX) return; scanners[mode] = scan; } @@ -177,7 +231,7 @@ void ieee80211_scanner_unregister(enum ieee80211_opmode mode, const struct ieee80211_scanner *scan) { - if (mode >= IEEE80211_SCANNER_MAX) + if (mode >= IEEE80211_OPMODE_MAX) return; if (scanners[mode] == scan) scanners[mode] = NULL; @@ -188,7 +242,7 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) { int m; - for (m = 0; m < IEEE80211_SCANNER_MAX; m++) + for (m = 0; m < IEEE80211_OPMODE_MAX; m++) if (scanners[m] == scan) scanners[m] = NULL; } @@ -201,35 +255,50 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) * ensure later callbacks find ss_ops set to properly * reflect current operating mode. */ -int -ieee80211_scan_update(struct ieee80211com *ic) +static void +scan_update_locked(struct ieee80211vap *vap, + const struct ieee80211_scanner *scan) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; - const struct ieee80211_scanner *scan; - scan = ieee80211_scanner_get(ic->ic_opmode); - IEEE80211_LOCK(ic); - if (scan == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: no scanner support for mode %u\n", - __func__, ic->ic_opmode); - /* XXX stat */ + IEEE80211_LOCK_ASSERT(ic); + +#ifdef IEEE80211_DEBUG + if (ss->ss_vap != vap || ss->ss_ops != scan) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: current scanner is <%s:%s>, switch to <%s:%s>\n", + __func__, + ss->ss_vap != NULL ? + ss->ss_vap->iv_ifp->if_xname : "none", + ss->ss_vap != NULL ? + ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none", + vap->iv_ifp->if_xname, + ieee80211_opmode_name[vap->iv_opmode]); } - ss->ss_ic = ic; +#endif + ss->ss_vap = vap; if (ss->ss_ops != scan) { - /* switch scanners; detach old, attach new */ - if (ss->ss_ops != NULL) - ss->ss_ops->scan_detach(ss); - if (scan != NULL && !scan->scan_attach(ss)) { - /* XXX attach failure */ - /* XXX stat+msg */ - ss->ss_ops = NULL; - } else - ss->ss_ops = scan; + /* + * Switch scanners; detach old, attach new. Special + * case where a single scan module implements multiple + * policies by using different scan ops but a common + * core. We assume if the old and new attach methods + * are identical then it's ok to just change ss_ops + * and not flush the internal state of the module. + */ + if (scan == NULL || ss->ss_ops == NULL || + ss->ss_ops->scan_attach != scan->scan_attach) { + if (ss->ss_ops != NULL) + ss->ss_ops->scan_detach(ss); + if (scan != NULL && !scan->scan_attach(ss)) { + /* XXX attach failure */ + /* XXX stat+msg */ + scan = NULL; + } + } + ss->ss_ops = scan; } - IEEE80211_UNLOCK(ic); - - return (scan != NULL); } static void @@ -263,7 +332,7 @@ channel_type(const struct ieee80211_channel *c) void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) { - struct ieee80211com *ic = ss->ss_ic; + struct ieee80211com *ic = ss->ss_vap->iv_ic; const char *sep; int i; @@ -277,6 +346,18 @@ ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) } } +#ifdef IEEE80211_DEBUG +static void +scan_dump(struct ieee80211_scan_state *ss) +{ + struct ieee80211vap *vap = ss->ss_vap; + + if_printf(vap->iv_ifp, "scan set "); + ieee80211_scan_dump_channels(ss); + printf(" dwell min %lu max %lu\n", ss->ss_mindwell, ss->ss_maxdwell); +} +#endif /* IEEE80211_DEBUG */ + /* * Enable station power save mode and start/restart the scanning thread. */ @@ -284,23 +365,24 @@ static void scan_restart_pwrsav(void *arg) { struct scan_state *ss = (struct scan_state *) arg; - struct ieee80211com *ic = ss->base.ss_ic; - int delay; + struct ieee80211vap *vap = ss->base.ss_vap; + struct ieee80211com *ic = vap->iv_ic; + int ticksdelay; - ieee80211_sta_pwrsave(ic, 1); + ieee80211_sta_pwrsave(vap, 1); /* - * Use an initial 1ms delay to insure the null + * Use an initial 1ms delay so the null * data frame has a chance to go out. * XXX 1ms is a lot, better to trigger scan * on tx complete. */ - delay = hz/1000; - if (delay < 1) - delay = 1; + ticksdelay = msecs_to_ticks(1); + if (ticksdelay < 1) + ticksdelay = 1; ic->ic_scan_start(ic); /* notify driver */ - ss->ss_scanend = ticks + delay + ss->ss_duration; + ss->ss_scanend = ticks + ticksdelay + ss->ss_duration; ss->ss_iflags |= ISCAN_START; - callout_reset(&ss->ss_scan_timer, delay, scan_next, ss); + callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss); } /* @@ -313,17 +395,18 @@ scan_restart_pwrsav(void *arg) static int scan_restart(struct scan_state *ss, u_int duration) { - struct ieee80211com *ic = ss->base.ss_ic; + struct ieee80211vap *vap = ss->base.ss_vap; + struct ieee80211com *ic = vap->iv_ic; int defer = 0; if (ss->base.ss_next == ss->base.ss_last) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no channels to scan\n", __func__); return 0; } - if (ic->ic_opmode == IEEE80211_M_STA && - ic->ic_state == IEEE80211_S_RUN) { - if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) { + if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { /* * Initiate power save before going off-channel. * Note that we cannot do this directly because @@ -346,12 +429,12 @@ scan_restart(struct scan_state *ss, u_int duration) } static void -copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss, +copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, int nssid, const struct ieee80211_scan_ssid ssids[]) { if (nssid > IEEE80211_SCAN_MAX_SSID) { /* XXX printf */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: too many ssid %d, ignoring all of them\n", __func__, nssid); return; @@ -363,76 +446,97 @@ copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss, /* * Start a scan unless one is already going. */ -int -ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration, +static int +start_scan_locked(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, u_int duration, + u_int mindwell, u_int maxdwell, u_int nssid, const struct ieee80211_scan_ssid ssids[]) { - const struct ieee80211_scanner *scan; + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; - scan = ieee80211_scanner_get(ic->ic_opmode); - if (scan == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: no scanner support for mode %u\n", - __func__, ic->ic_opmode); - /* XXX stat */ - return 0; - } + IEEE80211_LOCK_ASSERT(ic); - IEEE80211_LOCK(ic); - if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n" + if (ic->ic_flags & IEEE80211_F_CSAPENDING) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: scan inhibited by pending channel change\n", __func__); + } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" , __func__ , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" - , duration - , ieee80211_phymode_name[ic->ic_des_mode] + , duration, mindwell, maxdwell + , ieee80211_phymode_name[vap->iv_des_mode] , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" + , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" , flags & IEEE80211_SCAN_ONCE ? ", once" : "" ); - ss->ss_ic = ic; - if (ss->ss_ops != scan) { - /* switch scanners; detach old, attach new */ - if (ss->ss_ops != NULL) - ss->ss_ops->scan_detach(ss); - if (!scan->scan_attach(ss)) { - /* XXX attach failure */ - /* XXX stat+msg */ - ss->ss_ops = NULL; - } else - ss->ss_ops = scan; - } + scan_update_locked(vap, scan); if (ss->ss_ops != NULL) { if ((flags & IEEE80211_SCAN_NOSSID) == 0) - copy_ssid(ic, ss, nssid, ssids); + copy_ssid(vap, ss, nssid, ssids); /* NB: top 4 bits for internal use */ ss->ss_flags = flags & 0xfff; if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - ic->ic_stats.is_scan_active++; + vap->iv_stats.is_scan_active++; else - ic->ic_stats.is_scan_passive++; + vap->iv_stats.is_scan_passive++; if (flags & IEEE80211_SCAN_FLUSH) ss->ss_ops->scan_flush(ss); /* NB: flush frames rx'd before 1st channel change */ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; - ss->ss_ops->scan_start(ss, ic); + ss->ss_next = 0; + ss->ss_mindwell = mindwell; + ss->ss_maxdwell = maxdwell; + ss->ss_ops->scan_start(ss, vap); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap)) + scan_dump(ss); +#endif /* IEEE80211_DEBUG */ if (scan_restart(SCAN_PRIVATE(ss), duration)) ic->ic_flags |= IEEE80211_F_SCAN; } } else { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan already in progress\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); } + return (ic->ic_flags & IEEE80211_F_SCAN); +} + +/* + * Start a scan unless one is already going. + */ +int +ieee80211_start_scan(struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_scanner *scan; + int result; + + scan = ieee80211_scanner_get(vap->iv_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scanner support for %s mode\n", + __func__, ieee80211_opmode_name[vap->iv_opmode]); + /* XXX stat */ + return 0; + } + + IEEE80211_LOCK(ic); + result = start_scan_locked(scan, vap, flags, duration, + mindwell, maxdwell, nssid, ssids); IEEE80211_UNLOCK(ic); - /* NB: racey, does it matter? */ - return (ic->ic_flags & IEEE80211_F_SCAN); + return result; } /* @@ -440,11 +544,23 @@ ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration, * fails then kick off a new scan. */ int -ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration, +ieee80211_check_scan(struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, u_int nssid, const struct ieee80211_scan_ssid ssids[]) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; - int checkscanlist = 0; + const struct ieee80211_scanner *scan; + int checkscanlist = 0, result; + + scan = ieee80211_scanner_get(vap->iv_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scanner support for %s mode\n", + __func__, vap->iv_opmode); + /* XXX stat */ + return 0; + } /* * Check if there's a list of scan candidates already. @@ -452,30 +568,35 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration, */ IEEE80211_LOCK(ic); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n" + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, %s%s%s%s%s\n" , __func__ , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" - , duration - , ieee80211_phymode_name[ic->ic_des_mode] , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" , flags & IEEE80211_SCAN_ONCE ? ", once" : "" ); + if (ss->ss_ops != scan) { + /* XXX re-use cache contents? e.g. adhoc<->sta */ + flags |= IEEE80211_SCAN_FLUSH; + } + scan_update_locked(vap, scan); if (ss->ss_ops != NULL) { - /* XXX verify ss_ops matches ic->ic_opmode */ + /* XXX verify ss_ops matches vap->iv_opmode */ if ((flags & IEEE80211_SCAN_NOSSID) == 0) { /* * Update the ssid list and mark flags so if * we call start_scan it doesn't duplicate work. */ - copy_ssid(ic, ss, nssid, ssids); + copy_ssid(vap, ss, nssid, ssids); flags |= IEEE80211_SCAN_NOSSID; } if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && - time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) { + (flags & IEEE80211_SCAN_FLUSH) == 0 && + time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { /* * We're not currently scanning and the cache is * deemed hot enough to consult. Lock out others @@ -486,30 +607,40 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration, */ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; ic->ic_flags |= IEEE80211_F_SCAN; + /* NB: need to use supplied flags in check below */ + ss->ss_flags = flags & 0xff; checkscanlist = 1; } } - IEEE80211_UNLOCK(ic); if (checkscanlist) { - const struct ieee80211_scanner *scan; - - scan = ieee80211_scanner_get(ic->ic_opmode); - if (scan == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: no scanner support for mode %u\n", - __func__, ic->ic_opmode); - /* XXX stat */ - return 0; - } - if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) { + if (ss->ss_ops->scan_end(ss, vap)) { /* found an ap, just clear the flag */ ic->ic_flags &= ~IEEE80211_F_SCAN; + ieee80211_notify_scan_done(vap); + IEEE80211_UNLOCK(ic); return 1; } /* no ap, clear the flag before starting a scan */ ic->ic_flags &= ~IEEE80211_F_SCAN; } - return ieee80211_start_scan(ic, flags, duration, nssid, ssids); + result = start_scan_locked(scan, vap, flags, duration, + mindwell, maxdwell, nssid, ssids); + IEEE80211_UNLOCK(ic); + + return result; +} + +/* + * Check the scan cache for an ap/channel to use; if that fails + * then kick off a scan using the current settings. + */ +int +ieee80211_check_scan_current(struct ieee80211vap *vap) +{ + return ieee80211_check_scan(vap, + IEEE80211_SCAN_ACTIVE, + IEEE80211_SCAN_FOREVER, 0, 0, + vap->iv_des_nssid, vap->iv_des_ssid); } /* @@ -517,9 +648,20 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration, * then we start again using the existing channel list. */ int -ieee80211_bg_scan(struct ieee80211com *ic) +ieee80211_bg_scan(struct ieee80211vap *vap, int flags) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; + const struct ieee80211_scanner *scan; + + scan = ieee80211_scanner_get(vap->iv_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scanner support for %s mode\n", + __func__, vap->iv_opmode); + /* XXX stat */ + return 0; + } IEEE80211_LOCK(ic); if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { @@ -532,13 +674,14 @@ ieee80211_bg_scan(struct ieee80211com *ic) */ duration = IEEE80211_SCAN_OFFCHANNEL; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan, ticks %u duration %lu\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", ticks, duration); + scan_update_locked(vap, scan); if (ss->ss_ops != NULL) { - ss->ss_ic = ic; + ss->ss_vap = vap; /* * A background scan does not select a new sta; it * just refreshes the scan cache. Also, indicate @@ -554,15 +697,30 @@ ieee80211_bg_scan(struct ieee80211com *ic) * usual sta power save logic. */ ss->ss_flags |= IEEE80211_SCAN_NOPICK - | IEEE80211_SCAN_BGSCAN; + | IEEE80211_SCAN_BGSCAN + | flags + ; /* if previous scan completed, restart */ if (ss->ss_next >= ss->ss_last) { - ss->ss_next = 0; if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - ic->ic_stats.is_scan_active++; + vap->iv_stats.is_scan_active++; else - ic->ic_stats.is_scan_passive++; - ss->ss_ops->scan_restart(ss, ic); + vap->iv_stats.is_scan_passive++; + /* + * NB: beware of the scan cache being flushed; + * if the channel list is empty use the + * scan_start method to populate it. + */ + ss->ss_next = 0; + if (ss->ss_last != 0) + ss->ss_ops->scan_restart(ss, vap); + else { + ss->ss_ops->scan_start(ss, vap); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap)) + scan_dump(ss); +#endif /* IEEE80211_DEBUG */ + } } /* NB: flush frames rx'd before 1st channel change */ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; @@ -575,7 +733,7 @@ ieee80211_bg_scan(struct ieee80211com *ic) /* XXX msg+stat */ } } else { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan already in progress\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); } @@ -586,17 +744,46 @@ ieee80211_bg_scan(struct ieee80211com *ic) } /* + * Cancel any scan currently going on for the specified vap. + */ +void +ieee80211_cancel_scan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) && + ss->ss_vap == vap && + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel %s scan\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? + "active" : "passive"); + + /* clear bg scan NOPICK and mark cancel request */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; + /* force it to fire asap */ + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, + 0, scan_next, ss); + } + IEEE80211_UNLOCK(ic); +} + +/* * Cancel any scan currently going on. */ void -ieee80211_cancel_scan(struct ieee80211com *ic) +ieee80211_cancel_anyscan(struct ieee80211vap *vap) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; IEEE80211_LOCK(ic); if ((ic->ic_flags & IEEE80211_F_SCAN) && (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: cancel %s scan\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); @@ -616,15 +803,12 @@ ieee80211_cancel_scan(struct ieee80211com *ic) * scanning themselves (e.g. for firmware-based devices). */ void -ieee80211_scan_next(struct ieee80211com *ic) +ieee80211_scan_next(struct ieee80211vap *vap) { - /* - * XXX: We might need/want to decouple context here by either: - * callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); - * or using a taskqueue. Let's see what kind of problems direct - * dispatch has for now. - */ - scan_next(ic->ic_scan); + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); } /* @@ -632,12 +816,52 @@ ieee80211_scan_next(struct ieee80211com *ic) * channels (e.g. for firmware-based devices). */ void -ieee80211_scan_done(struct ieee80211com *ic) +ieee80211_scan_done(struct ieee80211vap *vap) { - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss; + IEEE80211_LOCK(ic); + ss = ic->ic_scan; ss->ss_next = ss->ss_last; /* all channels are complete */ scan_next(ss); + IEEE80211_UNLOCK(ic); +} + +/* + * 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 ieee80211vap *vap, int force) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + struct ifnet *ifp = vap->iv_ifp; + int i; + + if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) { + ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; + return; + } + /* + * Send directed probe requests followed by any + * broadcast probe request. + * XXX remove dependence on ic/vap->iv_bss + */ + for (i = 0; i < ss->ss_nssid; i++) + ieee80211_send_probereq(vap->iv_bss, + vap->iv_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); + if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) + ieee80211_send_probereq(vap->iv_bss, + vap->iv_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + "", 0); } /* @@ -646,37 +870,16 @@ ieee80211_scan_done(struct ieee80211com *ic) * Arrange for the channel change after maxdwell ticks. */ static void -scan_curchan(struct ieee80211com *ic, unsigned long maxdwell) +scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211vap *vap = ss->ss_vap; - if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && - (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { - struct ifnet *ifp = ic->ic_ifp; - int i; + IEEE80211_LOCK_ASSERT(vap->iv_ic); - /* - * Send a broadcast probe request followed by - * any specified directed probe requests. - * XXX suppress broadcast probe req? - * XXX remove dependence on ic/ic->ic_bss - * XXX move to policy code? - */ - ieee80211_send_probereq(ic->ic_bss, - ic->ic_myaddr, ifp->if_broadcastaddr, - ifp->if_broadcastaddr, - "", 0, - ic->ic_opt_ie, ic->ic_opt_ie_len); - for (i = 0; i < ss->ss_nssid; i++) - ieee80211_send_probereq(ic->ic_bss, - ic->ic_myaddr, ifp->if_broadcastaddr, - ifp->if_broadcastaddr, - ss->ss_ssid[i].ssid, - ss->ss_ssid[i].len, - ic->ic_opt_ie, ic->ic_opt_ie_len); - } + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + ieee80211_probe_curchan(vap, 0); callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, - maxdwell, scan_next, ss); + maxdwell, scan_next, ss); } /* @@ -684,10 +887,8 @@ scan_curchan(struct ieee80211com *ic, unsigned long maxdwell) * change to the next channel asap. */ static void -scan_mindwell(struct ieee80211com *ic) +scan_mindwell(struct ieee80211_scan_state *ss) { - struct ieee80211_scan_state *ss = ic->ic_scan; - callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); } @@ -699,17 +900,16 @@ scan_next(void *arg) { #define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD) struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; - struct ieee80211com *ic = ss->ss_ic; + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *chan; unsigned long maxdwell, scanend; - int scanning, scandone; + int scandone; - IEEE80211_LOCK(ic); - scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0; - IEEE80211_UNLOCK(ic); - if (!scanning) /* canceled */ - return; + IEEE80211_LOCK_ASSERT(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) + return; again: scandone = (ss->ss_next >= ss->ss_last) || (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; @@ -728,7 +928,7 @@ again: else maxdwell = ss->ss_maxdwell; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n", __func__, ieee80211_chan2ieee(ic, ic->ic_curchan), @@ -751,7 +951,7 @@ again: * sending a probe request (as needed), and arming the * timeout to switch channels after maxdwell ticks. */ - ic->ic_scan_curchan(ic, maxdwell); + ic->ic_scan_curchan(ss, maxdwell); SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; /* clear mindwell lock and initial channel change flush */ @@ -768,7 +968,7 @@ again: /* return to the bss channel */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && ic->ic_curchan != ic->ic_bsschan) - change_channel(ic, ic->ic_bsschan); + ieee80211_setcurchan(ic, ic->ic_bsschan); /* clear internal flags and any indication of a pick */ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; @@ -781,20 +981,21 @@ again: * rx frames alter the scan candidate list. */ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && - !ss->ss_ops->scan_end(ss, ic) && + !ss->ss_ops->scan_end(ss, vap) && (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && time_before(ticks + ss->ss_mindwell, scanend)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: done, restart " "[ticks %u, dwell min %lu scanend %lu]\n", __func__, ticks, ss->ss_mindwell, scanend); ss->ss_next = 0; /* reset to begining */ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - ic->ic_stats.is_scan_active++; + vap->iv_stats.is_scan_active++; else - ic->ic_stats.is_scan_passive++; + vap->iv_stats.is_scan_passive++; + ss->ss_ops->scan_restart(ss, vap); /* XXX? */ ic->ic_scan_start(ic); /* notify driver */ goto again; } else { @@ -802,7 +1003,7 @@ again: if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) scandone = 1; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s, " "[ticks %u, dwell min %lu scanend %lu]\n", __func__, scandone ? "done" : "stopped", @@ -825,9 +1026,9 @@ again: * waiting for us. */ if (scandone) { - ieee80211_sta_pwrsave(ic, 0); + ieee80211_sta_pwrsave(vap, 0); if (ss->ss_next >= ss->ss_last) { - ieee80211_notify_scan_done(ic); + ieee80211_notify_scan_done(vap); ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; } } @@ -841,32 +1042,48 @@ again: #ifdef IEEE80211_DEBUG static void +dump_country(const uint8_t *ie) +{ + const struct ieee80211_country_ie *cie = + (const struct ieee80211_country_ie *) ie; + int i, nbands, schan, nchan; + + if (cie->len < 3) { + printf(" <bogus country ie, len %d>", cie->len); + return; + } + printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]); + nbands = (cie->len - 3) / sizeof(cie->band[0]); + for (i = 0; i < nbands; i++) { + schan = cie->band[i].schan; + nchan = cie->band[i].nchan; + if (nchan != 1) + printf(" %u-%u,%u", schan, schan + nchan-1, + cie->band[i].maxtxpwr); + else + printf(" %u,%u", schan, cie->band[i].maxtxpwr); + } + printf("]"); +} + +static void dump_probe_beacon(uint8_t subtype, int isnew, const uint8_t mac[IEEE80211_ADDR_LEN], - const struct ieee80211_scanparams *sp) + const struct ieee80211_scanparams *sp, int rssi) { printf("[%s] %s%s on chan %u (bss chan %u) ", ether_sprintf(mac), isnew ? "new " : "", ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], - IEEE80211_CHAN2IEEE(sp->curchan), sp->bchan); + sp->chan, sp->bchan); ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); - printf("\n"); + printf(" rssi %d\n", rssi); if (isnew) { printf("[%s] caps 0x%x bintval %u erp 0x%x", ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp); - if (sp->country != NULL) { -#ifdef __FreeBSD__ - 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 - } + if (sp->country != NULL) + dump_country(sp->country); printf("\n"); } } @@ -876,26 +1093,27 @@ dump_probe_beacon(uint8_t subtype, int isnew, * Process a beacon or probe response frame. */ void -ieee80211_add_scan(struct ieee80211com *ic, +ieee80211_add_scan(struct ieee80211vap *vap, const struct ieee80211_scanparams *sp, const struct ieee80211_frame *wh, int subtype, int rssi, int noise, int rstamp) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; + /* XXX locking */ /* * Frames received during startup are discarded to avoid * using scan state setup on the initial entry to the timer * callback. This can occur because the device may enable * rx prior to our doing the initial channel change in the - * timer routine (we defer the channel change to the timer - * code to simplify locking on linux). + * timer routine. */ if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) return; #ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN)) - dump_probe_beacon(subtype, 1, wh->i_addr2, sp); + if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) + dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); #endif if (ss->ss_ops != NULL && ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) { @@ -905,7 +1123,7 @@ ieee80211_add_scan(struct ieee80211com *ic, */ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: chan %3d%c min dwell met (%u > %lu)\n", __func__, ieee80211_chan2ieee(ic, ic->ic_curchan), @@ -914,9 +1132,9 @@ ieee80211_add_scan(struct ieee80211com *ic, SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; /* * NB: trigger at next clock tick or wait for the - * hardware + * hardware. */ - ic->ic_scan_mindwell(ic); + ic->ic_scan_mindwell(ss); } } } @@ -938,12 +1156,12 @@ ieee80211_scan_timeout(struct ieee80211com *ic) * Mark a scan cache entry after a successful associate. */ void -ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[]) +ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[]) { - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; if (ss->ss_ops != NULL) { - IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac, "%s", __func__); ss->ss_ops->scan_assoc_success(ss, mac); } @@ -953,13 +1171,13 @@ ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[]) * Demerit a scan cache entry after failing to associate. */ void -ieee80211_scan_assoc_fail(struct ieee80211com *ic, +ieee80211_scan_assoc_fail(struct ieee80211vap *vap, const uint8_t mac[], int reason) { - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; if (ss->ss_ops != NULL) { - IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac, + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac, "%s: reason %u", __func__, reason); ss->ss_ops->scan_assoc_fail(ss, mac, reason); } @@ -969,10 +1187,10 @@ ieee80211_scan_assoc_fail(struct ieee80211com *ic, * Iterate over the contents of the scan cache. */ void -ieee80211_scan_iterate(struct ieee80211com *ic, +ieee80211_scan_iterate(struct ieee80211vap *vap, ieee80211_scan_iter_func *f, void *arg) { - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; if (ss->ss_ops != NULL) ss->ss_ops->scan_iterate(ss, f, arg); @@ -982,13 +1200,36 @@ ieee80211_scan_iterate(struct ieee80211com *ic, * Flush the contents of the scan cache. */ void -ieee80211_scan_flush(struct ieee80211com *ic) +ieee80211_scan_flush(struct ieee80211vap *vap) { - struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; - if (ss->ss_ops != NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s\n", __func__); + if (ss->ss_ops != NULL && ss->ss_vap == vap) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__); ss->ss_ops->scan_flush(ss); } } + +/* + * Check the scan cache for an ap/channel to use; if that + * fails then kick off a new scan. + */ +struct ieee80211_channel * +ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + + if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) { + /* XXX printf? */ + return NULL; + } + if (ss->ss_ops->scan_pickchan == NULL) { + IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, + "%s: scan module does not support picking a channel, " + "opmode %s\n", __func__, ss->ss_vap->iv_opmode); + return NULL; + } + return ss->ss_ops->scan_pickchan(ss, flags); +} diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h index fed5e0b..df6ce1f7 100644 --- a/sys/net80211/ieee80211_scan.h +++ b/sys/net80211/ieee80211_scan.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,18 +27,69 @@ #ifndef _NET80211_IEEE80211_SCAN_H_ #define _NET80211_IEEE80211_SCAN_H_ +/* + * 802.11 scanning support. + * + * Scanning is the procedure by which a station locates a bss to join + * (infrastructure/ibss mode), or a channel to use (when operating as + * an ap or ibss master). Scans are either "active" or "passive". An + * active scan causes one or more probe request frames to be sent on + * visiting each channel. A passive request causes each channel in the + * scan set to be visited but no frames to be transmitted; the station + * only listens for traffic. Note that active scanning may still need + * to listen for traffic before sending probe request frames depending + * on regulatory constraints; the 802.11 layer handles this by generating + * a callback when scanning on a ``passive channel'' when the + * IEEE80211_FEXT_PROBECHAN flag is set. + * + * A scan operation involves constructing a set of channels to inspec + * (the scan set), visiting each channel and collecting information + * (e.g. what bss are present), and then analyzing the results to make + * decisions like which bss to join. This process needs to be as fast + * as possible so we do things like intelligently construct scan sets + * and dwell on a channel only as long as necessary. The scan code also + * maintains a cache of recent scan results and uses it to bypass scanning + * whenever possible. The scan cache is also used to enable roaming + * between access points when operating in infrastructure mode. + * + * Scanning is handled with pluggable modules that implement "policy" + * per-operating mode. The core scanning support provides an + * instrastructure to support these modules and exports a common api + * to the rest of the 802.11 layer. Policy modules decide what + * channels to visit, what state to record to make decisions (e.g. ap + * mode scanning for auto channel selection keeps significantly less + * state than sta mode scanning for an ap to associate to), and selects + * the final station/channel to return as the result of a scan. + * + * Scanning is done synchronously when initially bringing a vap to an + * operational state and optionally in the background to maintain the + * scan cache for doing roaming and rogue ap monitoring. Scanning is + * not tied to the 802.11 state machine that governs vaps though there + * is linkage to the IEEE80211_SCAN state. Only one vap at a time may + * be scanning; this scheduling policy is handled in ieee80211_new_state + * and is invisible to the scanning code. +*/ #define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX -struct ieee80211_scanner; +struct ieee80211_scanner; /* scan policy state */ struct ieee80211_scan_ssid { - int len; /* length in bytes */ - uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */ + int len; /* length in bytes */ + uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */ }; -#define IEEE80211_SCAN_MAX_SSID 1 +#define IEEE80211_SCAN_MAX_SSID 1 /* max # ssid's to probe */ +/* + * Scan state visible to the 802.11 layer. Scan parameters and + * results are stored in this data structure. The ieee80211_scan_state + * structure is extended with space that is maintained private to + * the core scanning support. We allocate one instance and link it + * to the ieee80211com structure; then share it between all associated + * vaps. We could allocate multiple of these, e.g. to hold multiple + * scan results, but this is sufficient for current needs. + */ struct ieee80211_scan_state { - struct ieee80211com *ss_ic; + struct ieee80211vap *ss_vap; const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */ void *ss_priv; /* scanner private state */ uint16_t ss_flags; @@ -47,6 +98,8 @@ struct ieee80211_scan_state { #define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */ #define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */ #define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */ +#define IEEE80211_SCAN_NOBCAST 0x0020 /* no broadcast probe req */ +#define IEEE80211_SCAN_NOJOIN 0x0040 /* no auto-sequencing */ #define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */ uint8_t ss_nssid; /* # ssid's to probe/match */ struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID]; @@ -65,48 +118,64 @@ struct ieee80211_scan_state { * ss_flags. It might be better to split this stuff out into * a separate variable to avoid confusion. */ -#define IEEE80211_SCAN_FLUSH 0x10000 /* flush candidate table */ -#define IEEE80211_SCAN_NOSSID 0x20000 /* don't update ssid list */ +#define IEEE80211_SCAN_FLUSH 0x00010000 /* flush candidate table */ +#define IEEE80211_SCAN_NOSSID 0x80000000 /* don't update ssid list */ struct ieee80211com; void ieee80211_scan_attach(struct ieee80211com *); void ieee80211_scan_detach(struct ieee80211com *); +void ieee80211_scan_vattach(struct ieee80211vap *); +void ieee80211_scan_vdetach(struct ieee80211vap *); void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *); -int ieee80211_scan_update(struct ieee80211com *); #define IEEE80211_SCAN_FOREVER 0x7fffffff -int ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration, +int ieee80211_start_scan(struct ieee80211vap *, int flags, + u_int duration, u_int mindwell, u_int maxdwell, u_int nssid, const struct ieee80211_scan_ssid ssids[]); -int ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration, +int ieee80211_check_scan(struct ieee80211vap *, int flags, + u_int duration, u_int mindwell, u_int maxdwell, u_int nssid, const struct ieee80211_scan_ssid ssids[]); -int ieee80211_bg_scan(struct ieee80211com *); -void ieee80211_cancel_scan(struct ieee80211com *); -void ieee80211_scan_next(struct ieee80211com *); -void ieee80211_scan_done(struct ieee80211com *); +int ieee80211_check_scan_current(struct ieee80211vap *); +int ieee80211_bg_scan(struct ieee80211vap *, int); +void ieee80211_cancel_scan(struct ieee80211vap *); +void ieee80211_cancel_anyscan(struct ieee80211vap *); +void ieee80211_scan_next(struct ieee80211vap *); +void ieee80211_scan_done(struct ieee80211vap *); +void ieee80211_probe_curchan(struct ieee80211vap *, int); +struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int); struct ieee80211_scanparams; -void ieee80211_add_scan(struct ieee80211com *, +void ieee80211_add_scan(struct ieee80211vap *, const struct ieee80211_scanparams *, const struct ieee80211_frame *, int subtype, int rssi, int noise, int rstamp); void ieee80211_scan_timeout(struct ieee80211com *); -void ieee80211_scan_assoc_success(struct ieee80211com *, +void ieee80211_scan_assoc_success(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); enum { IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */ IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */ }; -void ieee80211_scan_assoc_fail(struct ieee80211com *, +void ieee80211_scan_assoc_fail(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN], int reason); -void ieee80211_scan_flush(struct ieee80211com *); +void ieee80211_scan_flush(struct ieee80211vap *); struct ieee80211_scan_entry; typedef void ieee80211_scan_iter_func(void *, const struct ieee80211_scan_entry *); -void ieee80211_scan_iterate(struct ieee80211com *, +void ieee80211_scan_iterate(struct ieee80211vap *, ieee80211_scan_iter_func, void *); +enum { + IEEE80211_BPARSE_BADIELEN = 0x01, /* ie len past end of frame */ + IEEE80211_BPARSE_RATES_INVALID = 0x02, /* invalid RATES ie */ + IEEE80211_BPARSE_XRATES_INVALID = 0x04, /* invalid XRATES ie */ + IEEE80211_BPARSE_SSID_INVALID = 0x08, /* invalid SSID ie */ + IEEE80211_BPARSE_CHAN_INVALID = 0x10, /* invalid FH/DSPARMS chan */ + IEEE80211_BPARSE_OFFCHAN = 0x20, /* DSPARMS chan != curchan */ + IEEE80211_BPARSE_BINTVAL_INVALID= 0x40, /* invalid beacon interval */ +}; /* * Parameters supplied when adding/updating an entry in a @@ -116,14 +185,17 @@ void ieee80211_scan_iterate(struct ieee80211com *, * 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 */ - struct ieee80211_channel *curchan; - uint8_t bchan; /* chan# advertised inside beacon */ + uint8_t status; /* bitmask of IEEE80211_BPARSE_* */ + uint8_t chan; /* channel # from FH/DSPARMS */ + uint8_t bchan; /* curchan's channel # */ uint8_t fhindex; - uint8_t erp; + uint16_t fhdwell; /* FHSS dwell interval */ + uint16_t capinfo; /* 802.11 capabilities */ + uint16_t erp; /* NB: 0x100 indicates ie present */ uint16_t bintval; uint8_t timoff; + uint8_t *ies; /* all captured ies */ + size_t ies_len; /* length of all captured ies */ uint8_t *tim; uint8_t *tstamp; uint8_t *country; @@ -146,13 +218,14 @@ struct ieee80211_scanparams { struct ieee80211_scan_entry { uint8_t se_macaddr[IEEE80211_ADDR_LEN]; uint8_t se_bssid[IEEE80211_ADDR_LEN]; + /* XXX can point inside se_ies */ uint8_t se_ssid[2+IEEE80211_NWID_LEN]; uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE]; uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE]; uint32_t se_rstamp; /* recv timestamp */ union { uint8_t data[8]; - uint64_t tsf; + u_int64_t tsf; } se_tstamp; /* from last rcv'd beacon */ uint16_t se_intval; /* beacon interval (host byte order) */ uint16_t se_capinfo; /* capabilities (host byte order) */ @@ -160,16 +233,12 @@ struct ieee80211_scan_entry { uint16_t se_timoff; /* byte offset to TIM ie */ uint16_t se_fhdwell; /* FH only (host byte order) */ uint8_t se_fhindex; /* FH only */ - uint8_t se_erp; /* ERP from beacon/probe resp */ + uint8_t se_dtimperiod; /* DTIM period */ + uint16_t se_erp; /* ERP from beacon/probe resp */ int8_t se_rssi; /* avg'd recv ssi */ int8_t se_noise; /* noise floor */ - uint8_t se_dtimperiod; /* DTIM period */ - uint8_t *se_wpa_ie; /* captured WPA ie */ - uint8_t *se_rsn_ie; /* captured RSN ie */ - uint8_t *se_wme_ie; /* captured WME ie */ - uint8_t *se_htcap_ie; /* captured HTP cap ie */ - uint8_t *se_htinfo_ie; /* captured HTP info ie */ - uint8_t *se_ath_ie; /* captured Atheros ie */ + uint8_t se_cc[2]; /* captured country code */ + struct ieee80211_ies se_ies; /* captured ie's */ u_int se_age; /* age of entry (0 on create) */ }; MALLOC_DECLARE(M_80211_SCAN); @@ -184,14 +253,16 @@ struct ieee80211_scanner { int (*scan_attach)(struct ieee80211_scan_state *); int (*scan_detach)(struct ieee80211_scan_state *); int (*scan_start)(struct ieee80211_scan_state *, - struct ieee80211com *); + struct ieee80211vap *); int (*scan_restart)(struct ieee80211_scan_state *, - struct ieee80211com *); + struct ieee80211vap *); int (*scan_cancel)(struct ieee80211_scan_state *, - struct ieee80211com *); + struct ieee80211vap *); int (*scan_end)(struct ieee80211_scan_state *, - struct ieee80211com *); + struct ieee80211vap *); int (*scan_flush)(struct ieee80211_scan_state *); + struct ieee80211_channel *(*scan_pickchan)( + struct ieee80211_scan_state *, int); /* add an entry to the cache */ int (*scan_add)(struct ieee80211_scan_state *, const struct ieee80211_scanparams *, diff --git a/sys/net80211/ieee80211_scan_ap.c b/sys/net80211/ieee80211_scan_ap.c deleted file mode 100644 index 300f440..0000000 --- a/sys/net80211/ieee80211_scan_ap.c +++ /dev/null @@ -1,408 +0,0 @@ -/*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/* - * IEEE 802.11 ap scanning support. - */ -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/module.h> - -#include <sys/socket.h> - -#include <net/if.h> -#include <net/if_media.h> -#include <net/ethernet.h> - -#include <net80211/ieee80211_var.h> - -#include <net/bpf.h> - -struct ap_state { - int as_maxrssi[IEEE80211_CHAN_MAX]; -}; - -static int ap_flush(struct ieee80211_scan_state *); - -/* number of references from net80211 layer */ -static int nrefs = 0; - -/* - * Attach prior to any scanning work. - */ -static int -ap_attach(struct ieee80211_scan_state *ss) -{ - struct ap_state *as; - - MALLOC(as, struct ap_state *, sizeof(struct ap_state), - M_80211_SCAN, M_NOWAIT); - ss->ss_priv = as; - ap_flush(ss); - nrefs++; /* NB: we assume caller locking */ - return 1; -} - -/* - * Cleanup any private state. - */ -static int -ap_detach(struct ieee80211_scan_state *ss) -{ - struct ap_state *as = ss->ss_priv; - - if (as != NULL) { - KASSERT(nrefs > 0, ("imbalanced attach/detach")); - nrefs--; /* NB: we assume caller locking */ - FREE(as, M_80211_SCAN); - } - return 1; -} - -/* - * Flush all per-scan state. - */ -static int -ap_flush(struct ieee80211_scan_state *ss) -{ - struct ap_state *as = ss->ss_priv; - - memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi)); - ss->ss_last = 0; /* insure no channel will be picked */ - return 0; -} - -static int -find11gchannel(struct ieee80211com *ic, int i, int freq) -{ - const struct ieee80211_channel *c; - int j; - - /* - * The normal ordering in the channel list is b channel - * immediately followed by g so optimize the search for - * this. We'll still do a full search just in case. - */ - for (j = i+1; j < ic->ic_nchans; j++) { - c = &ic->ic_channels[j]; - if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) - return 1; - } - for (j = 0; j < i; j++) { - c = &ic->ic_channels[j]; - if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) - return 1; - } - return 0; -} - -/* - * Start an ap scan by populating the channel list. - */ -static int -ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) -{ - struct ieee80211_channel *c; - int i; - - ss->ss_last = 0; - if (ic->ic_des_mode == IEEE80211_MODE_AUTO) { - for (i = 0; i < ic->ic_nchans; i++) { - c = &ic->ic_channels[i]; - if (IEEE80211_IS_CHAN_TURBO(c)) { -#ifdef IEEE80211_F_XR - /* XR is not supported on turbo channels */ - if (ic->ic_flags & IEEE80211_F_XR) - continue; -#endif - /* dynamic channels are scanned in base mode */ - if (!IEEE80211_IS_CHAN_ST(c)) - continue; - } else if (IEEE80211_IS_CHAN_HT(c)) { - /* HT channels are scanned in legacy */ - continue; - } else { - /* - * Use any 11g channel instead of 11b one. - */ - if (IEEE80211_IS_CHAN_B(c) && - find11gchannel(ic, i, c->ic_freq)) - continue; - } - if (ss->ss_last >= IEEE80211_SCAN_MAX) - break; - ss->ss_chans[ss->ss_last++] = c; - } - } else { - static const u_int chanflags[IEEE80211_MODE_MAX] = { - 0, /* IEEE80211_MODE_AUTO */ - IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ - IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ - IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */ - IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ - IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */ - IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ - IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */ - IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */ - IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */ - }; - u_int modeflags; - - modeflags = chanflags[ic->ic_des_mode]; - if ((ic->ic_flags & IEEE80211_F_TURBOP) && - modeflags != IEEE80211_CHAN_ST) { - if (ic->ic_des_mode == IEEE80211_MODE_11G) - modeflags = IEEE80211_CHAN_108G; - else - modeflags = IEEE80211_CHAN_108A; - } - for (i = 0; i < ic->ic_nchans; i++) { - c = &ic->ic_channels[i]; - if ((c->ic_flags & modeflags) != modeflags) - continue; -#ifdef IEEE80211_F_XR - /* XR is not supported on turbo channels */ - if (IEEE80211_IS_CHAN_TURBO(c) && - (ic->ic_flags & IEEE80211_F_XR)) - continue; -#endif - if (ss->ss_last >= IEEE80211_SCAN_MAX) - break; - /* - * Do not select static turbo channels if - * the mode is not static turbo. - */ - if (IEEE80211_IS_CHAN_STURBO(c) && - ic->ic_des_mode != IEEE80211_MODE_STURBO_A) - continue; - ss->ss_chans[ss->ss_last++] = c; - } - } - ss->ss_next = 0; - /* XXX tunables */ - ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ - ss->ss_maxdwell = msecs_to_ticks(300); /* 300ms */ - -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic)) { - if_printf(ic->ic_ifp, "scan set "); - ieee80211_scan_dump_channels(ss); - printf(" dwell min %ld max %ld\n", - ss->ss_mindwell, ss->ss_maxdwell); - } -#endif /* IEEE80211_DEBUG */ - - return 0; -} - -/* - * Restart a bg scan. - */ -static int -ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic) -{ - return 0; -} - -/* - * Cancel an ongoing scan. - */ -static int -ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic) -{ - return 0; -} - -/* - * Record max rssi on channel. - */ -static int -ap_add(struct ieee80211_scan_state *ss, - const struct ieee80211_scanparams *sp, - const struct ieee80211_frame *wh, - int subtype, int rssi, int noise, int rstamp) -{ - struct ap_state *as = ss->ss_priv; - struct ieee80211com *ic = ss->ss_ic; - int chan; - - chan = ieee80211_chan2ieee(ic, ic->ic_curchan); - /* XXX better quantification of channel use? */ - /* XXX count bss's? */ - if (rssi > as->as_maxrssi[chan]) - as->as_maxrssi[chan] = rssi; - /* XXX interference, turbo requirements */ - return 1; -} - -/* - * Pick a quiet channel to use for ap operation. - */ -static int -ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic) -{ - struct ap_state *as = ss->ss_priv; - int i, chan, bestchan, bestchanix; - - KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP, - ("wrong opmode %u", ic->ic_opmode)); - /* XXX select channel more intelligently, e.g. channel spread, power */ - bestchan = -1; - bestchanix = 0; /* NB: silence compiler */ - /* NB: use scan list order to preserve channel preference */ - for (i = 0; i < ss->ss_last; 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. - */ - /* XXX channel have interference? */ - chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]); - - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n", - __func__, chan, as->as_maxrssi[chan], - bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0); - - if (as->as_maxrssi[chan] == 0) { - bestchan = chan; - bestchanix = i; - /* XXX use other considerations */ - break; - } - if (bestchan == -1 || - as->as_maxrssi[chan] < as->as_maxrssi[bestchan]) - bestchan = chan; - } - if (bestchan == -1) { - /* no suitable channel, should not happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, - "%s: no suitable channel! (should not happen)\n", __func__); - /* XXX print something? */ - return 0; /* restart scan */ - } else { - struct ieee80211_channel *c; - - /* XXX notify all vap's? */ - /* - * If this is a dynamic turbo frequency, - * start with normal mode first. - */ - c = ss->ss_chans[bestchanix]; - if (IEEE80211_IS_CHAN_TURBO(c) && - !IEEE80211_IS_CHAN_STURBO(c)) { - c = ieee80211_find_channel(ic, c->ic_freq, - c->ic_flags & ~IEEE80211_CHAN_TURBO); - if (c == NULL) { - /* should never happen ?? */ - return 0; - } - } - ieee80211_create_ibss(ic, - ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext)); - return 1; - } -} - -static void -ap_age(struct ieee80211_scan_state *ss) -{ - /* XXX is there anything meaningful to do? */ -} - -static void -ap_iterate(struct ieee80211_scan_state *ss, - ieee80211_scan_iter_func *f, void *arg) -{ - /* NB: nothing meaningful we can do */ -} - -static void -ap_assoc_success(struct ieee80211_scan_state *ss, - const uint8_t macaddr[IEEE80211_ADDR_LEN]) -{ - /* should not be called */ -} - -static void -ap_assoc_fail(struct ieee80211_scan_state *ss, - const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason) -{ - /* should not be called */ -} - -static const struct ieee80211_scanner ap_default = { - .scan_name = "default", - .scan_attach = ap_attach, - .scan_detach = ap_detach, - .scan_start = ap_start, - .scan_restart = ap_restart, - .scan_cancel = ap_cancel, - .scan_end = ap_end, - .scan_flush = ap_flush, - .scan_add = ap_add, - .scan_age = ap_age, - .scan_iterate = ap_iterate, - .scan_assoc_success = ap_assoc_success, - .scan_assoc_fail = ap_assoc_fail, -}; - -/* - * Module glue. - */ -static int -wlan_modevent(module_t mod, int type, void *unused) -{ - switch (type) { - case MOD_LOAD: - ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default); - return 0; - case MOD_UNLOAD: - case MOD_QUIESCE: - if (nrefs) { - printf("wlan_scan_ap: still in use (%u dynamic refs)\n", - nrefs); - return EBUSY; - } - if (type == MOD_UNLOAD) - ieee80211_scanner_unregister_all(&ap_default); - return 0; - } - return EINVAL; -} - -static moduledata_t wlan_mod = { - "wlan_scan_ap", - wlan_modevent, - 0 -}; -DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan_scan_ap, 1); -MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1); diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c index d152605..d1dc060 100644 --- a/sys/net80211/ieee80211_scan_sta.c +++ b/sys/net80211/ieee80211_scan_sta.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); /* * IEEE 802.11 station scanning support. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -41,6 +43,8 @@ __FBSDID("$FreeBSD$"); #include <net/ethernet.h> #include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_input.h> +#include <net80211/ieee80211_regdomain.h> #include <net/bpf.h> @@ -61,20 +65,6 @@ __FBSDID("$FreeBSD$"); #define STA_RSSI_MIN 8 /* min acceptable rssi */ #define STA_RSSI_MAX 40 /* max rssi for comparison */ -#define RSSI_LPF_LEN 10 -#define RSSI_DUMMY_MARKER 0x127 -#define RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ -#define RSSI_IN(x) ((x) * RSSI_EP_MULTIPLIER) -#define LPF_RSSI(x, y, len) \ - ((x != RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) -#define RSSI_LPF(x, y) do { \ - if ((y) >= -20) \ - x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN); \ -} while (0) -#define EP_RND(x, mul) \ - ((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul)) -#define RSSI_GET(x) EP_RND(x, RSSI_EP_MULTIPLIER) - struct sta_entry { struct ieee80211_scan_entry base; TAILQ_ENTRY(sta_entry) se_list; @@ -83,11 +73,14 @@ struct sta_entry { uint8_t se_seen; /* seen during current scan */ uint8_t se_notseen; /* not seen in previous scans */ uint8_t se_flags; +#define STA_SSID_MATCH 0x01 +#define STA_BSSID_MATCH 0x02 uint32_t se_avgrssi; /* LPF rssi state */ unsigned long se_lastupdate; /* time of last update */ unsigned long se_lastfail; /* time of last failure */ unsigned long se_lastassoc; /* time of last association */ u_int se_scangen; /* iterator scan gen# */ + u_int se_countrygen; /* gen# of last cc notify */ }; #define STA_HASHSIZE 32 @@ -99,9 +92,12 @@ struct sta_table { struct mtx st_lock; /* on scan table */ TAILQ_HEAD(, sta_entry) st_entry; /* all entries */ LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE]; - struct mtx st_scanlock; /* on st_scangen */ - u_int st_scangen; /* gen# for iterator */ + struct mtx st_scanlock; /* on st_scaniter */ + u_int st_scaniter; /* gen# for iterator */ + u_int st_scangen; /* scan generation # */ int st_newscan; + /* ap-related state */ + int st_maxrssi[IEEE80211_CHAN_MAX]; }; static void sta_flush_table(struct sta_table *); @@ -120,8 +116,16 @@ static void sta_flush_table(struct sta_table *); #define MATCH_FAILS 0x040 /* too many failed auth attempts */ #define MATCH_NOTSEEN 0x080 /* not seen in recent scans */ #define MATCH_RSSI 0x100 /* rssi deemed too low to use */ -static int match_bss(struct ieee80211com *, +#define MATCH_CC 0x200 /* country code mismatch */ +static int match_bss(struct ieee80211vap *, const struct ieee80211_scan_state *, struct sta_entry *, int); +static void adhoc_age(struct ieee80211_scan_state *); + +static __inline int +isocmp(const uint8_t cc1[], const uint8_t cc2[]) +{ + return (cc1[0] == cc2[0] && cc1[1] == cc2[1]); +} /* number of references from net80211 layer */ static int nrefs = 0; @@ -191,18 +195,10 @@ sta_flush_table(struct sta_table *st) TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { TAILQ_REMOVE(&st->st_entry, se, se_list); LIST_REMOVE(se, se_hash); + ieee80211_ies_cleanup(&se->base.se_ies); FREE(se, M_80211_SCAN); } -} - -static void -saveie(uint8_t **iep, const uint8_t *ie) -{ - - if (ie == NULL) - *iep = NULL; - else - ieee80211_saveie(iep, ie); + memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi)); } /* @@ -221,10 +217,11 @@ sta_add(struct ieee80211_scan_state *ss, IEEE80211_SCAN_PICK1ST) struct sta_table *st = ss->ss_priv; const uint8_t *macaddr = wh->i_addr2; - struct ieee80211com *ic = ss->ss_ic; + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = vap->iv_ic; struct sta_entry *se; struct ieee80211_scan_entry *ise; - int hash, offchan; + int hash; hash = STA_HASH(macaddr); @@ -238,8 +235,8 @@ sta_add(struct ieee80211_scan_state *ss, mtx_unlock(&st->st_lock); return 0; } - se->se_scangen = st->st_scangen-1; - se->se_avgrssi = RSSI_DUMMY_MARKER; + se->se_scangen = st->st_scaniter-1; + se->se_avgrssi = IEEE80211_RSSI_DUMMY_MARKER; IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr); TAILQ_INSERT_TAIL(&st->st_entry, se, se_list); LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash); @@ -254,23 +251,21 @@ found: memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]); if (sp->xrates != NULL) { /* XXX validate xrates[1] */ - KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE, + KASSERT(sp->xrates[1] <= IEEE80211_RATE_MAXSIZE, ("xrate set too large: %u", sp->xrates[1])); memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]); } else ise->se_xrates[1] = 0; IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3); - offchan = (IEEE80211_CHAN2IEEE(sp->curchan) != sp->bchan && - ic->ic_phytype != IEEE80211_T_FH); - if (!offchan) { + if ((sp->status & IEEE80211_BPARSE_OFFCHAN) == 0) { /* * Record rssi data using extended precision LPF filter. * * NB: use only on-channel data to insure we get a good * estimate of the signal we'll see when associated. */ - RSSI_LPF(se->se_avgrssi, rssi); - ise->se_rssi = RSSI_GET(se->se_avgrssi); + IEEE80211_RSSI_LPF(se->se_avgrssi, rssi); + ise->se_rssi = IEEE80211_RSSI_GET(se->se_avgrssi); ise->se_noise = noise; } ise->se_rstamp = rstamp; @@ -280,24 +275,26 @@ found: /* * Beware of overriding se_chan for frames seen * off-channel; this can cause us to attempt an - * assocation on the wrong channel. + * association on the wrong channel. */ - if (offchan) { + if (sp->status & IEEE80211_BPARSE_OFFCHAN) { struct ieee80211_channel *c; /* * Off-channel, locate the home/bss channel for the sta - * using the value broadcast in the DSPARMS ie. + * using the value broadcast in the DSPARMS ie. We know + * sp->chan has this value because it's used to calculate + * IEEE80211_BPARSE_OFFCHAN. */ - c = ieee80211_find_channel_byieee(ic, sp->bchan, - sp->curchan->ic_flags); + c = ieee80211_find_channel_byieee(ic, sp->chan, + ic->ic_curchan->ic_flags); if (c != NULL) { ise->se_chan = c; } else if (ise->se_chan == NULL) { /* should not happen, pick something */ - ise->se_chan = sp->curchan; + ise->se_chan = ic->ic_curchan; } } else - ise->se_chan = sp->curchan; + ise->se_chan = ic->ic_curchan; ise->se_fhdwell = sp->fhdwell; ise->se_fhindex = sp->fhindex; ise->se_erp = sp->erp; @@ -307,17 +304,39 @@ found: (const struct ieee80211_tim_ie *) sp->tim; ise->se_dtimperiod = tim->tim_period; } - saveie(&ise->se_wme_ie, sp->wme); - saveie(&ise->se_wpa_ie, sp->wpa); - saveie(&ise->se_rsn_ie, sp->rsn); - saveie(&ise->se_ath_ie, sp->ath); - saveie(&ise->se_htcap_ie, sp->htcap); - saveie(&ise->se_htinfo_ie, sp->htinfo); + if (sp->country != NULL) { + const struct ieee80211_country_ie *cie = + (const struct ieee80211_country_ie *) sp->country; + /* + * If 11d is enabled and we're attempting to join a bss + * that advertises it's country code then compare our + * current settings to what we fetched from the country ie. + * If our country code is unspecified or different then + * dispatch an event to user space that identifies the + * country code so our regdomain config can be changed. + */ + /* XXX only for STA mode? */ + if ((IEEE80211_IS_CHAN_11D(ise->se_chan) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) && + (ic->ic_regdomain.country == CTRY_DEFAULT || + !isocmp(cie->cc, ic->ic_regdomain.isocc))) { + /* only issue one notify event per scan */ + if (se->se_countrygen != st->st_scangen) { + ieee80211_notify_country(vap, ise->se_bssid, + cie->cc); + se->se_countrygen = st->st_scangen; + } + } + ise->se_cc[0] = cie->cc[0]; + ise->se_cc[1] = cie->cc[1]; + } + /* NB: no need to setup ie ptrs; they are not (currently) used */ + (void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len); /* clear failure count after STA_FAIL_AGE passes */ if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) { se->se_fails = 0; - IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr, + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, macaddr, "%s: fails %u", __func__, se->se_fails); } @@ -325,13 +344,16 @@ found: se->se_seen = 1; se->se_notseen = 0; + if (rssi > st->st_maxrssi[sp->bchan]) + st->st_maxrssi[sp->bchan] = rssi; + mtx_unlock(&st->st_lock); /* * If looking for a quick choice and nothing's * been found check here. */ - if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0) + if (PICK1ST(ss) && match_bss(vap, ss, se, IEEE80211_MSG_SCAN) == 0) ss->ss_flags |= IEEE80211_SCAN_GOTPICK; return 1; @@ -343,11 +365,11 @@ found: * Check if a channel is excluded by user request. */ static int -isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c) +isexcluded(struct ieee80211vap *vap, const struct ieee80211_channel *c) { - return (isclr(ic->ic_chan_active, c->ic_ieee) || - (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - c->ic_freq != ic->ic_des_chan->ic_freq)); + return (isclr(vap->iv_ic->ic_chan_active, c->ic_ieee) || + (vap->iv_des_chan != IEEE80211_CHAN_ANYC && + c->ic_freq != vap->iv_des_chan->ic_freq)); } static struct ieee80211_channel * @@ -387,11 +409,12 @@ static const u_int chanflags[IEEE80211_MODE_MAX] = { }; static void -add_channels(struct ieee80211com *ic, +add_channels(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, enum ieee80211_phymode mode, const uint16_t freq[], int nfreq) { #define N(a) (sizeof(a) / sizeof(a[0])) + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *c, *cg; u_int modeflags; int i; @@ -403,89 +426,28 @@ add_channels(struct ieee80211com *ic, break; c = ieee80211_find_channel(ic, freq[i], modeflags); - if (c != NULL && isexcluded(ic, c)) + if (c == NULL || isexcluded(vap, c)) continue; if (mode == IEEE80211_MODE_AUTO) { /* * XXX special-case 11b/g channels so we select - * the g channel if both are present or there - * are only g channels. + * the g channel if both are present. */ - if (c == NULL || IEEE80211_IS_CHAN_B(c)) { - cg = find11gchannel(ic, i, freq[i]); - if (cg != NULL) - c = cg; - } + if (IEEE80211_IS_CHAN_B(c) && + (cg = find11gchannel(ic, i, c->ic_freq)) != NULL) + c = cg; } - if (c == NULL) - continue; - ss->ss_chans[ss->ss_last++] = c; } #undef N } -static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */ -{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 }; -static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */ -{ 5170, 5190, 5210, 5230 }; -static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */ -{ 2412, 2437, 2462, 2442, 2472 }; -static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */ -{ 5745, 5765, 5785, 5805, 5825 }; -static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */ -{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 }; -static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */ -{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 }; -static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */ -{ 2484 }; -static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */ -{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 }; -static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */ -{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 }; -#ifdef ATH_TURBO_SCAN -static const uint16_t rcl5[] = /* 3 static turbo channels */ -{ 5210, 5250, 5290 }; -static const uint16_t rcl6[] = /* 2 static turbo channels */ -{ 5760, 5800 }; -static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */ -{ 5540, 5580, 5620, 5660 }; -static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */ -{ 2437 }; -static const uint16_t rcl13[] = /* dynamic Turbo channels */ -{ 5200, 5240, 5280, 5765, 5805 }; -#endif /* ATH_TURBO_SCAN */ - struct scanlist { uint16_t mode; uint16_t count; const uint16_t *list; }; -#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a - -static const struct scanlist staScanTable[] = { - { IEEE80211_MODE_11B, X(rcl3) }, - { IEEE80211_MODE_11A, X(rcl1) }, - { IEEE80211_MODE_11A, X(rcl2) }, - { IEEE80211_MODE_11B, X(rcl8) }, - { IEEE80211_MODE_11B, X(rcl9) }, - { IEEE80211_MODE_11A, X(rcl4) }, -#ifdef ATH_TURBO_SCAN - { IEEE80211_MODE_STURBO_A, X(rcl5) }, - { IEEE80211_MODE_STURBO_A, X(rcl6) }, - { IEEE80211_MODE_TURBO_A, X(rcl6x) }, - { IEEE80211_MODE_TURBO_A, X(rcl13) }, -#endif /* ATH_TURBO_SCAN */ - { IEEE80211_MODE_11A, X(rcl7) }, - { IEEE80211_MODE_11B, X(rcl10) }, - { IEEE80211_MODE_11A, X(rcl11) }, -#ifdef ATH_TURBO_SCAN - { IEEE80211_MODE_TURBO_G, X(rcl12) }, -#endif /* ATH_TURBO_SCAN */ - { .list = NULL } -}; - static int checktable(const struct scanlist *scan, const struct ieee80211_channel *c) { @@ -500,16 +462,13 @@ checktable(const struct scanlist *scan, const struct ieee80211_channel *c) } static void -sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic, +sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, const struct scanlist table[]) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_channel *c; int i; - /* - * Add the channels from the ic (from HAL) that are not present - * in the staScanTable. - */ for (i = 0; i < ic->ic_nchans; i++) { if (ss->ss_last >= IEEE80211_SCAN_MAX) break; @@ -528,14 +487,14 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic, * If a desired mode was specified, scan only * channels that satisfy that constraint. */ - if (ic->ic_des_mode != IEEE80211_MODE_AUTO && - ic->ic_des_mode != ieee80211_chan2mode(c)) + if (vap->iv_des_mode != IEEE80211_MODE_AUTO && + vap->iv_des_mode != ieee80211_chan2mode(c)) continue; /* * Skip channels excluded by user request. */ - if (isexcluded(ic, c)) + if (isexcluded(vap, c)) continue; /* @@ -552,14 +511,10 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic, } } -/* - * Start a station-mode scan by populating the channel list. - */ -static int -sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +static void +makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, + const struct scanlist table[]) { -#define N(a) (sizeof(a)/sizeof(a[0])) - struct sta_table *st = ss->ss_priv; const struct scanlist *scan; enum ieee80211_phymode mode; @@ -569,20 +524,20 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) * of channels for scanning. Any channels in the ordered * list not in the master list will be discarded. */ - for (scan = staScanTable; scan->list != NULL; scan++) { + for (scan = table; scan->list != NULL; scan++) { mode = scan->mode; - if (ic->ic_des_mode != IEEE80211_MODE_AUTO) { + if (vap->iv_des_mode != IEEE80211_MODE_AUTO) { /* * If a desired mode was specified, scan only * channels that satisfy that constraint. */ - if (ic->ic_des_mode != mode) { + if (vap->iv_des_mode != mode) { /* * The scan table marks 2.4Ghz channels as b * so if the desired mode is 11g, then use * the 11b channel list but upgrade the mode. */ - if (ic->ic_des_mode != IEEE80211_MODE_11G || + if (vap->iv_des_mode != IEEE80211_MODE_11G || mode != IEEE80211_MODE_11B) continue; mode = IEEE80211_MODE_11G; /* upgrade */ @@ -597,7 +552,7 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) } #ifdef IEEE80211_F_XR /* XR does not operate on turbo channels */ - if ((ic->ic_flags & IEEE80211_F_XR) && + if ((vap->iv_flags & IEEE80211_F_XR) && (mode == IEEE80211_MODE_TURBO_A || mode == IEEE80211_MODE_TURBO_G || mode == IEEE80211_MODE_STURBO_A)) @@ -607,40 +562,98 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) * Add the list of the channels; any that are not * in the master channel list will be discarded. */ - add_channels(ic, ss, mode, scan->list, scan->count); + add_channels(vap, ss, mode, scan->list, scan->count); } /* - * Add the channels from the ic (from HAL) that are not present - * in the staScanTable. + * Add the channels from the ic that are not present + * in the table. */ - sweepchannels(ss, ic, staScanTable); + sweepchannels(ss, vap, table); +} - ss->ss_next = 0; - /* XXX tunables */ - ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */ - ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ +static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */ +{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 }; +static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */ +{ 5170, 5190, 5210, 5230 }; +static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */ +{ 2412, 2437, 2462, 2442, 2472 }; +static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */ +{ 5745, 5765, 5785, 5805, 5825 }; +static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */ +{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 }; +static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */ +{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 }; +static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */ +{ 2484 }; +static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */ +{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 }; +static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */ +{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 }; +#ifdef ATH_TURBO_SCAN +static const uint16_t rcl5[] = /* 3 static turbo channels */ +{ 5210, 5250, 5290 }; +static const uint16_t rcl6[] = /* 2 static turbo channels */ +{ 5760, 5800 }; +static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */ +{ 5540, 5580, 5620, 5660 }; +static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */ +{ 2437 }; +static const uint16_t rcl13[] = /* dynamic Turbo channels */ +{ 5200, 5240, 5280, 5765, 5805 }; +#endif /* ATH_TURBO_SCAN */ -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic)) { - if_printf(ic->ic_ifp, "scan set "); - ieee80211_scan_dump_channels(ss); - printf(" dwell min %ld max %ld\n", - ss->ss_mindwell, ss->ss_maxdwell); - } -#endif /* IEEE80211_DEBUG */ +#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a +static const struct scanlist staScanTable[] = { + { IEEE80211_MODE_11B, X(rcl3) }, + { IEEE80211_MODE_11A, X(rcl1) }, + { IEEE80211_MODE_11A, X(rcl2) }, + { IEEE80211_MODE_11B, X(rcl8) }, + { IEEE80211_MODE_11B, X(rcl9) }, + { IEEE80211_MODE_11A, X(rcl4) }, +#ifdef ATH_TURBO_SCAN + { IEEE80211_MODE_STURBO_A, X(rcl5) }, + { IEEE80211_MODE_STURBO_A, X(rcl6) }, + { IEEE80211_MODE_TURBO_A, X(rcl6x) }, + { IEEE80211_MODE_TURBO_A, X(rcl13) }, +#endif /* ATH_TURBO_SCAN */ + { IEEE80211_MODE_11A, X(rcl7) }, + { IEEE80211_MODE_11B, X(rcl10) }, + { IEEE80211_MODE_11A, X(rcl11) }, +#ifdef ATH_TURBO_SCAN + { IEEE80211_MODE_TURBO_G, X(rcl12) }, +#endif /* ATH_TURBO_SCAN */ + { .list = NULL } +}; + +/* + * Start a station-mode scan by populating the channel list. + */ +static int +sta_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + + makescanlist(ss, vap, staScanTable); + + if (ss->ss_mindwell == 0) + ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */ + if (ss->ss_maxdwell == 0) + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + + st->st_scangen++; st->st_newscan = 1; return 0; -#undef N } /* - * Restart a bg scan. + * Restart a scan, typically a bg scan but can + * also be a fg scan that came up empty. */ static int -sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +sta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { struct sta_table *st = ss->ss_priv; @@ -652,18 +665,44 @@ sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic) * Cancel an ongoing scan. */ static int -sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { return 0; } -static uint8_t +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) + +static int maxrate(const struct ieee80211_scan_entry *se) { - uint8_t rmax, r; - int i; + const struct ieee80211_ie_htcap *htcap = + (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie; + int rmax, r, i; + uint16_t caps; rmax = 0; + if (htcap != NULL) { + /* + * HT station; inspect supported MCS and then adjust + * rate by channel width. Could also include short GI + * in this if we want to be extra accurate. + */ + /* XXX assumes MCS15 is max */ + for (i = 15; i >= 0 && isclr(htcap->hc_mcsset, i); i--) + ; + if (i >= 0) { + caps = LE_READ_2(&htcap->hc_cap); + /* XXX short/long GI */ + if (caps & IEEE80211_HTCAP_CHWIDTH40) + rmax = ieee80211_htrates[i].ht40_rate_400ns; + else + rmax = ieee80211_htrates[i].ht40_rate_800ns; + } + } for (i = 0; i < se->se_rates[1]; i++) { r = se->se_rates[2+i] & IEEE80211_RATE_VAL; if (r > rmax) @@ -691,7 +730,7 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b) if (((_a) ^ (_b)) & (_what)) \ return ((_a) & (_what)) ? 1 : -1; \ } while (0) - uint8_t maxa, maxb; + int maxa, maxb; int8_t rssia, rssib; int weight; @@ -722,12 +761,8 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b) 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->base.se_chan) && - !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan)) - return 1; - if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) && - IEEE80211_IS_CHAN_5GHZ(b->base.se_chan)) - return -1; + PREFER(IEEE80211_IS_CHAN_5GHZ(a->base.se_chan), + IEEE80211_IS_CHAN_5GHZ(b->base.se_chan), 1); } /* all things being equal, use signal level */ return a->base.se_rssi - b->base.se_rssi; @@ -736,20 +771,23 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b) /* * Check rate set suitability and return the best supported rate. + * XXX inspect MCS for HT */ static int -check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se) +check_rate(struct ieee80211vap *vap, const struct ieee80211_scan_entry *se) { #define RV(v) ((v) & IEEE80211_RATE_VAL) const struct ieee80211_rateset *srs; - int i, j, nrs, r, okrate, badrate, fixedrate; + int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate; const uint8_t *rs; - okrate = badrate = fixedrate = 0; + okrate = badrate = 0; - srs = ieee80211_get_suprates(ic, se->se_chan); + srs = ieee80211_get_suprates(vap->iv_ic, se->se_chan); nrs = se->se_rates[1]; rs = se->se_rates+2; + /* XXX MCS */ + ucastrate = vap->iv_txparms[ieee80211_chan2mode(se->se_chan)].ucastrate; fixedrate = IEEE80211_FIXED_RATE_NONE; again: for (i = 0; i < nrs; i++) { @@ -758,7 +796,7 @@ again: /* * Check any fixed rate is included. */ - if (r == ic->ic_fixed_rate) + if (r == ucastrate) fixedrate = r; /* * Check against our supported rates. @@ -787,7 +825,7 @@ again: } back: - if (okrate == 0 || ic->ic_fixed_rate != fixedrate) + if (okrate == 0 || ucastrate != fixedrate) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); @@ -812,13 +850,14 @@ match_ssid(const uint8_t *ie, * Test a scan candidate for suitability/compatibility. */ static int -match_bss(struct ieee80211com *ic, +match_bss(struct ieee80211vap *vap, const struct ieee80211_scan_state *ss, struct sta_entry *se0, int debug) { + struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_entry *se = &se0->base; - uint8_t rate; - int fail; + uint8_t rate; + int fail; fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan))) @@ -830,18 +869,33 @@ match_bss(struct ieee80211com *ic, * list so we check the desired mode here to weed them * out. */ - if (ic->ic_des_mode != IEEE80211_MODE_AUTO && + if (vap->iv_des_mode != IEEE80211_MODE_AUTO && (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) != - chanflags[ic->ic_des_mode]) + chanflags[vap->iv_des_mode]) fail |= MATCH_CHANNEL; - if (ic->ic_opmode == IEEE80211_M_IBSS) { + if (vap->iv_opmode == IEEE80211_M_IBSS) { if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0) fail |= MATCH_CAPINFO; } else { if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= MATCH_CAPINFO; + /* + * If 11d is enabled and we're attempting to join a bss + * that advertises it's country code then compare our + * current settings to what we fetched from the country ie. + * If our country code is unspecified or different then do + * not attempt to join the bss. We should have already + * dispatched an event to user space that identifies the + * new country code so our regdomain config should match. + */ + if ((IEEE80211_IS_CHAN_11D(se->se_chan) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) && + se->se_cc[0] != 0 && + (ic->ic_regdomain.country == CTRY_DEFAULT || + !isocmp(se->se_cc, ic->ic_regdomain.isocc))) + fail |= MATCH_CC; } - if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_flags & IEEE80211_F_PRIVACY) { if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= MATCH_PRIVACY; } else { @@ -849,27 +903,27 @@ match_bss(struct ieee80211com *ic, if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= MATCH_PRIVACY; } - rate = check_rate(ic, se); + rate = check_rate(vap, se); if (rate & IEEE80211_RATE_BASIC) fail |= MATCH_RATE; if (ss->ss_nssid != 0 && !match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid)) fail |= MATCH_SSID; - if ((ic->ic_flags & IEEE80211_F_DESBSSID) && - !IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid)) - fail |= MATCH_BSSID; + if ((vap->iv_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid)) + fail |= MATCH_BSSID; if (se0->se_fails >= STA_FAILS_MAX) fail |= MATCH_FAILS; - /* NB: entries may be present awaiting purge, skip */ if (se0->se_notseen >= STA_PURGE_SCANS) fail |= MATCH_NOTSEEN; if (se->se_rssi < STA_RSSI_MIN) fail |= MATCH_RSSI; #ifdef IEEE80211_DEBUG - if (ieee80211_msg(ic, debug)) { + if (ieee80211_msg(vap, debug)) { printf(" %c %s", fail & MATCH_FAILS ? '=' : fail & MATCH_NOTSEEN ? '^' : + fail & MATCH_CC ? '$' : fail ? '-' : '+', ether_sprintf(se->se_macaddr)); printf(" %s%c", ether_sprintf(se->se_bssid), fail & MATCH_BSSID ? '!' : ' '); @@ -929,16 +983,17 @@ sta_dec_fails(struct sta_table *st) } static struct sta_entry * -select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug) +select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int debug) { struct sta_table *st = ss->ss_priv; struct sta_entry *se, *selbs = NULL; - IEEE80211_DPRINTF(ic, debug, " %s\n", + IEEE80211_DPRINTF(vap, debug, " %s\n", "macaddr bssid chan rssi rate flag wep essid"); mtx_lock(&st->st_lock); TAILQ_FOREACH(se, &st->st_entry, se_list) { - if (match_bss(ic, ss, se, debug) == 0) { + if (match_bss(vap, ss, se, debug) == 0) { + ieee80211_ies_expand(&se->base.se_ies); if (selbs == NULL) selbs = se; else if (sta_compare(se, selbs) > 0) @@ -955,13 +1010,13 @@ select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug) * to use to start an ibss network. */ static int -sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { struct sta_table *st = ss->ss_priv; struct sta_entry *selbs; - KASSERT(ic->ic_opmode == IEEE80211_M_STA, - ("wrong mode %u", ic->ic_opmode)); + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); if (st->st_newscan) { sta_update_notseen(st); @@ -982,8 +1037,10 @@ sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic) */ /* NB: unlocked read should be ok */ if (TAILQ_FIRST(&st->st_entry) == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no scan candidate\n", __func__); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return 0; notfound: /* * If nothing suitable was found decrement @@ -996,8 +1053,10 @@ notfound: st->st_newscan = 1; return 0; /* restart scan */ } - selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN); - if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base)) + selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return (selbs != NULL); + if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base)) goto notfound; return 1; /* terminate scan */ } @@ -1024,12 +1083,14 @@ sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN]) } static void -sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { - struct ieee80211_node *ni = ic->ic_bss; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; struct sta_table *st = ss->ss_priv; + enum ieee80211_phymode mode; struct sta_entry *se, *selbs; - uint8_t roamRate, curRate; + uint8_t roamRate, curRate, ucastRate; int8_t roamRssi, curRssi; se = sta_lookup(st, ni->ni_macaddr); @@ -1038,27 +1099,21 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic) return; } - /* XXX do we need 11g too? */ - if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { - roamRate = ic->ic_roam.rate11b; - roamRssi = ic->ic_roam.rssi11b; - } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) { - roamRate = ic->ic_roam.rate11bOnly; - roamRssi = ic->ic_roam.rssi11bOnly; - } else { - roamRate = ic->ic_roam.rate11a; - roamRssi = ic->ic_roam.rssi11a; - } + mode = ieee80211_chan2mode(ic->ic_bsschan); + roamRate = vap->iv_roamparms[mode].rate; + roamRssi = vap->iv_roamparms[mode].rssi; + ucastRate = vap->iv_txparms[mode].ucastrate; /* NB: the most up to date rssi is in the node, not the scan cache */ curRssi = ic->ic_node_getrssi(ni); - if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { - curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM, + if (ucastRate == IEEE80211_FIXED_RATE_NONE) { + curRate = ni->ni_txrate; + roamRate &= IEEE80211_RATE_VAL; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, "%s: currssi %d currate %u roamrssi %d roamrate %u\n", __func__, curRssi, curRate, roamRssi, roamRate); } else { curRate = roamRate; /* NB: insure compare below fails */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi); } /* @@ -1066,7 +1121,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic) * XXX deauth current ap */ if (curRate < roamRate || curRssi < roamRssi) { - if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) { + if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { /* * Scan cache contents are too old; force a scan now * if possible so we have current state to make a @@ -1076,19 +1131,19 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic) * XXX force immediate switch on scan complete */ if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && - time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle)) - ieee80211_bg_scan(ic); + time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)) + ieee80211_bg_scan(vap, 0); return; } se->base.se_rssi = curRssi; - selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM); + selbs = select_bss(ss, vap, IEEE80211_MSG_ROAM); if (selbs != NULL && selbs != se) { - IEEE80211_DPRINTF(ic, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG, "%s: ROAM: curRate %u, roamRate %u, " "curRssi %d, roamRssi %d\n", __func__, curRate, roamRate, curRssi, roamRssi); - ieee80211_sta_join(ic, &selbs->base); + ieee80211_sta_join(vap, &selbs->base); } } } @@ -1100,19 +1155,9 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic) static void sta_age(struct ieee80211_scan_state *ss) { - struct ieee80211com *ic = ss->ss_ic; - struct sta_table *st = ss->ss_priv; - struct sta_entry *se, *next; + struct ieee80211vap *vap = ss->ss_vap; - mtx_lock(&st->st_lock); - TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { - if (se->se_notseen > STA_PURGE_SCANS) { - TAILQ_REMOVE(&st->st_entry, se, se_list); - LIST_REMOVE(se, se_hash); - FREE(se, M_80211_SCAN); - } - } - mtx_unlock(&st->st_lock); + adhoc_age(ss); /* * If rate control is enabled check periodically to see if * we should roam from our current connection to one that @@ -1122,13 +1167,13 @@ sta_age(struct ieee80211_scan_state *ss) * XXX repeater station * XXX do when !bgscan? */ - KASSERT(ic->ic_opmode == IEEE80211_M_STA, - ("wrong mode %u", ic->ic_opmode)); - if (ic->ic_roaming == IEEE80211_ROAMING_AUTO && - (ic->ic_flags & IEEE80211_F_BGSCAN) && - ic->ic_state >= IEEE80211_S_RUN) + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO && + (vap->iv_ic->ic_flags & IEEE80211_F_BGSCAN) && + vap->iv_state >= IEEE80211_S_RUN) /* XXX vap is implicit */ - sta_roam_check(ss, ic); + sta_roam_check(ss, vap); } /* @@ -1144,7 +1189,7 @@ sta_iterate(struct ieee80211_scan_state *ss, u_int gen; mtx_lock(&st->st_scanlock); - gen = st->st_scangen++; + gen = st->st_scaniter++; restart: mtx_lock(&st->st_lock); TAILQ_FOREACH(se, &st->st_entry, se_list) { @@ -1173,7 +1218,7 @@ sta_assoc_fail(struct ieee80211_scan_state *ss, if (se != NULL) { se->se_fails++; se->se_lastfail = ticks; - IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN, + IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN, macaddr, "%s: reason %u fails %u", __func__, reason, se->se_fails); } @@ -1190,7 +1235,7 @@ sta_assoc_success(struct ieee80211_scan_state *ss, if (se != NULL) { #if 0 se->se_fails = 0; - IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN, + IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN, macaddr, "%s: fails %u", __func__, se->se_fails); #endif @@ -1240,93 +1285,30 @@ static const struct scanlist adhocScanTable[] = { * Start an adhoc-mode scan by populating the channel list. */ static int -adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { -#define N(a) (sizeof(a)/sizeof(a[0])) struct sta_table *st = ss->ss_priv; - const struct scanlist *scan; - enum ieee80211_phymode mode; - ss->ss_last = 0; - /* - * Use the table of ordered channels to construct the list - * of channels for scanning. Any channels in the ordered - * list not in the master list will be discarded. - */ - for (scan = adhocScanTable; scan->list != NULL; scan++) { - mode = scan->mode; - if (ic->ic_des_mode != IEEE80211_MODE_AUTO) { - /* - * If a desired mode was specified, scan only - * channels that satisfy that constraint. - */ - if (ic->ic_des_mode != mode) { - /* - * The scan table marks 2.4Ghz channels as b - * so if the desired mode is 11g, then use - * the 11b channel list but upgrade the mode. - */ - if (ic->ic_des_mode != IEEE80211_MODE_11G || - mode != IEEE80211_MODE_11B) - continue; - mode = IEEE80211_MODE_11G; /* upgrade */ - } - } else { - /* - * This lets add_channels upgrade an 11b channel - * to 11g if available. - */ - if (mode == IEEE80211_MODE_11B) - mode = IEEE80211_MODE_AUTO; - } -#ifdef IEEE80211_F_XR - /* XR does not operate on turbo channels */ - if ((ic->ic_flags & IEEE80211_F_XR) && - (mode == IEEE80211_MODE_TURBO_A || - mode == IEEE80211_MODE_TURBO_G)) - continue; -#endif - /* - * Add the list of the channels; any that are not - * in the master channel list will be discarded. - */ - add_channels(ic, ss, mode, scan->list, scan->count); - } - - /* - * Add the channels from the ic (from HAL) that are not present - * in the staScanTable. - */ - sweepchannels(ss, ic, adhocScanTable); - - ss->ss_next = 0; - /* XXX tunables */ - ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ - ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + makescanlist(ss, vap, adhocScanTable); -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(ic)) { - if_printf(ic->ic_ifp, "scan set "); - ieee80211_scan_dump_channels(ss); - printf(" dwell min %ld max %ld\n", - ss->ss_mindwell, ss->ss_maxdwell); - } -#endif /* IEEE80211_DEBUG */ + if (ss->ss_mindwell == 0) + ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ + if (ss->ss_maxdwell == 0) + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + st->st_scangen++; st->st_newscan = 1; return 0; -#undef N } /* * Select a channel to start an adhoc network on. * The channel list was populated with appropriate * channels so select one that looks least occupied. - * XXX need regulatory domain constraints */ static struct ieee80211_channel * -adhoc_pick_channel(struct ieee80211_scan_state *ss) +adhoc_pick_channel(struct ieee80211_scan_state *ss, int flags) { struct sta_table *st = ss->ss_priv; struct sta_entry *se; @@ -1339,7 +1321,14 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss) mtx_lock(&st->st_lock); for (i = 0; i < ss->ss_last; i++) { c = ss->ss_chans[i]; - if (!checktable(adhocScanTable, c)) + /* never consider a channel with radar */ + if (IEEE80211_IS_CHAN_RADAR(c)) + continue; + /* skip channels disallowed by regulatory settings */ + if (IEEE80211_IS_CHAN_NOADHOC(c)) + continue; + /* check channel attributes for band compatibility */ + if (flags != 0 && (c->ic_flags & flags) != flags) continue; maxrssi = 0; TAILQ_FOREACH(se, &st->st_entry, se_list) { @@ -1361,15 +1350,15 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss) * to use to start an ibss network. */ static int -adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic) +adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { struct sta_table *st = ss->ss_priv; struct sta_entry *selbs; struct ieee80211_channel *chan; - KASSERT(ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO, - ("wrong opmode %u", ic->ic_opmode)); + KASSERT(vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO, + ("wrong opmode %u", vap->iv_opmode)); if (st->st_newscan) { sta_update_notseen(st); @@ -1390,22 +1379,26 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic) */ /* NB: unlocked read should be ok */ if (TAILQ_FIRST(&st->st_entry) == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no scan candidate\n", __func__); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return 0; notfound: - if (ic->ic_des_nssid) { + if (vap->iv_des_nssid) { /* * No existing adhoc network to join and we have * an ssid; start one up. If no channel was * specified, try to select a channel. */ - if (ic->ic_des_chan == IEEE80211_CHAN_ANYC) - chan = ieee80211_ht_adjust_channel(ic, - adhoc_pick_channel(ss), ic->ic_flags_ext); - else - chan = ic->ic_des_chan; + if (vap->iv_des_chan == IEEE80211_CHAN_ANYC || + IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { + chan = ieee80211_ht_adjust_channel(vap->iv_ic, + adhoc_pick_channel(ss, 0), + vap->iv_flags_ext); + } else + chan = vap->iv_des_chan; if (chan != NULL) { - ieee80211_create_ibss(ic, chan); + ieee80211_create_ibss(vap, chan); return 1; } } @@ -1420,8 +1413,10 @@ notfound: st->st_newscan = 1; return 0; /* restart scan */ } - selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN); - if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base)) + selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return (selbs != NULL); + if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base)) goto notfound; return 1; /* terminate scan */ } @@ -1440,6 +1435,7 @@ adhoc_age(struct ieee80211_scan_state *ss) if (se->se_notseen > STA_PURGE_SCANS) { TAILQ_REMOVE(&st->st_entry, se, se_list); LIST_REMOVE(se, se_hash); + ieee80211_ies_cleanup(&se->base.se_ies); FREE(se, M_80211_SCAN); } } @@ -1455,6 +1451,7 @@ static const struct ieee80211_scanner adhoc_default = { .scan_cancel = sta_cancel, .scan_end = adhoc_pick_bss, .scan_flush = sta_flush, + .scan_pickchan = adhoc_pick_channel, .scan_add = sta_add, .scan_age = adhoc_age, .scan_iterate = sta_iterate, @@ -1462,39 +1459,161 @@ static const struct ieee80211_scanner adhoc_default = { .scan_assoc_success = sta_assoc_success, }; +static void +ap_force_promisc(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_LOCK(ic); + /* set interface into promiscuous mode */ + ifp->if_flags |= IFF_PROMISC; + ic->ic_update_promisc(ifp); + IEEE80211_UNLOCK(ic); +} + +static void +ap_reset_promisc(struct ieee80211com *ic) +{ + IEEE80211_LOCK(ic); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + IEEE80211_UNLOCK(ic); +} + +static int +ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + + makescanlist(ss, vap, staScanTable); + + if (ss->ss_mindwell == 0) + ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ + if (ss->ss_maxdwell == 0) + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + + st->st_scangen++; + st->st_newscan = 1; + + ap_force_promisc(vap->iv_ic); + return 0; +} + /* - * Module glue. + * Cancel an ongoing scan. */ static int -wlan_modevent(module_t mod, int type, void *unused) +ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { - switch (type) { - case MOD_LOAD: - ieee80211_scanner_register(IEEE80211_M_STA, &sta_default); - ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default); - ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default); - return 0; - case MOD_UNLOAD: - case MOD_QUIESCE: - if (nrefs) { - printf("wlan_scan_sta: still in use (%u dynamic refs)\n", - nrefs); - return EBUSY; + ap_reset_promisc(vap->iv_ic); + return 0; +} + +/* + * Pick a quiet channel to use for ap operation. + */ +static struct ieee80211_channel * +ap_pick_channel(struct ieee80211_scan_state *ss, int flags) +{ + struct sta_table *st = ss->ss_priv; + struct ieee80211_channel *bestchan = NULL; + int i; + + /* XXX select channel more intelligently, e.g. channel spread, power */ + /* NB: use scan list order to preserve channel preference */ + for (i = 0; i < ss->ss_last; i++) { + struct ieee80211_channel *chan = ss->ss_chans[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 (IEEE80211_IS_CHAN_RADAR(chan)) + continue; + if (IEEE80211_IS_CHAN_NOHOSTAP(chan)) + continue; + /* check channel attributes for band compatibility */ + if (flags != 0 && (chan->ic_flags & flags) != flags) + continue; + /* XXX channel have interference */ + if (st->st_maxrssi[chan->ic_ieee] == 0) { + /* XXX use other considerations */ + return chan; } - if (type == MOD_UNLOAD) { - ieee80211_scanner_unregister_all(&sta_default); - ieee80211_scanner_unregister_all(&adhoc_default); + if (bestchan == NULL || + st->st_maxrssi[chan->ic_ieee] < st->st_maxrssi[bestchan->ic_ieee]) + bestchan = chan; + } + return bestchan; +} + +/* + * Pick a quiet channel to use for ap operation. + */ +static int +ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *bestchan; + + KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, + ("wrong opmode %u", vap->iv_opmode)); + bestchan = ap_pick_channel(ss, 0); + if (bestchan == NULL) { + /* no suitable channel, should not happen */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no suitable channel! (should not happen)\n", __func__); + /* XXX print something? */ + return 0; /* restart scan */ + } + /* + * If this is a dynamic turbo channel, start with the unboosted one. + */ + if (IEEE80211_IS_CHAN_TURBO(bestchan)) { + bestchan = ieee80211_find_channel(ic, bestchan->ic_freq, + bestchan->ic_flags & ~IEEE80211_CHAN_TURBO); + if (bestchan == NULL) { + /* should never happen ?? */ + return 0; } - return 0; } - return EINVAL; + ap_reset_promisc(ic); + if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + ieee80211_create_ibss(vap, + ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ext)); + return 1; } -static moduledata_t wlan_mod = { - "wlan_scan_sta", - wlan_modevent, - 0 +static const struct ieee80211_scanner ap_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = ap_start, + .scan_restart = sta_restart, + .scan_cancel = ap_cancel, + .scan_end = ap_end, + .scan_flush = sta_flush, + .scan_pickchan = ap_pick_channel, + .scan_add = sta_add, + .scan_age = adhoc_age, + .scan_iterate = sta_iterate, + .scan_assoc_success = sta_assoc_success, + .scan_assoc_fail = sta_assoc_fail, }; -DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan_scan_sta, 1); -MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1); + +/* + * Module glue. + */ +IEEE80211_SCANNER_MODULE(sta, 1); +IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default); +IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default); +IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default); +IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default); diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c new file mode 100644 index 0000000..e3c57ad --- /dev/null +++ b/sys/net80211/ieee80211_sta.c @@ -0,0 +1,1647 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 Station mode support. + */ +#include "opt_inet.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_llc.h> +#include <net/ethernet.h> + +#include <net/bpf.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_sta.h> +#include <net80211/ieee80211_input.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +static void sta_vattach(struct ieee80211vap *); +static void sta_beacon_miss(struct ieee80211vap *); +static int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int sta_input(struct ieee80211_node *, struct mbuf *, + int rssi, int noise, uint32_t rstamp); +static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *, + int subtype, int rssi, int noise, uint32_t rstamp); + +void +ieee80211_sta_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_STA] = sta_vattach; +} + +void +ieee80211_sta_detach(struct ieee80211com *ic) +{ +} + +static void +sta_vdetach(struct ieee80211vap *vap) +{ +} + +static void +sta_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = sta_newstate; + vap->iv_input = sta_input; + vap->iv_recv_mgmt = sta_recv_mgmt; + vap->iv_opdetach = sta_vdetach; + vap->iv_bmiss = sta_beacon_miss; +} + +/* + * Handle a beacon miss event. The common code filters out + * spurious events that can happen when scanning and/or before + * reaching RUN state. + */ +static void +sta_beacon_miss(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); + KASSERT(vap->iv_state == IEEE80211_S_RUN, + ("wrong state %d", vap->iv_state)); + + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "beacon miss, mode %u state %s\n", + vap->iv_opmode, ieee80211_state_name[vap->iv_state]); + + if (++vap->iv_bmiss_count < vap->iv_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(vap->iv_bss, vap->iv_myaddr, + vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid, + vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen); + return; + } + vap->iv_bmiss_count = 0; + vap->iv_stats.is_beacon_miss++; + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { + /* + * If we receive a beacon miss interrupt when using + * dynamic turbo, attempt to switch modes before + * reassociating. + */ + if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP)) + ieee80211_dturbo_switch(vap, + ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); + /* + * Try to reassociate before scanning for a new ap. + */ + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); + } else { + /* + * Somebody else is controlling state changes (e.g. + * a user-mode app) don't do anything that would + * confuse them; just drop into scan mode so they'll + * notified of the state change and given control. + */ + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } +} + +/* + * Handle deauth with reason. We retry only for + * the cases where we might succeed. Otherwise + * we downgrade the ap and scan. + */ +static void +sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason) +{ + switch (reason) { + case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */ + case IEEE80211_STATUS_TIMEOUT: + case IEEE80211_REASON_ASSOC_EXPIRE: + case IEEE80211_REASON_NOT_AUTHED: + case IEEE80211_REASON_NOT_ASSOCED: + case IEEE80211_REASON_ASSOC_LEAVE: + case IEEE80211_REASON_ASSOC_NOT_AUTHED: + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + default: + ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + } +} + +/* + * IEEE80211_M_STA vap state machine handler. + * This routine handles the main states in the 802.11 protocol. + */ +static int +sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + + IEEE80211_LOCK_ASSERT(ic); + + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate], arg); + vap->iv_state = nstate; /* state transition */ + callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */ + if (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(vap); /* background scan */ + ni = vap->iv_bss; /* NB: no reference held */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) + callout_stop(&vap->iv_swbmiss); + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SLEEP: + /* XXX wakeup */ + case IEEE80211_S_RUN: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_sta_leave(ni); + break; + case IEEE80211_S_ASSOC: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + break; + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + default: + goto invalid; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + } + if (vap->iv_auth->ia_detach != NULL) + vap->iv_auth->ia_detach(vap); + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_INIT: + /* + * Initiate a scan. We can come here as a result + * of an IEEE80211_IOC_SCAN_REQ too in which case + * the vap will be marked with IEEE80211_FEXT_SCANREQ + * and the scan request parameters will be present + * in iv_scanreq. Otherwise we do the default. + */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { + ieee80211_check_scan(vap, + vap->iv_scanreq_flags, + vap->iv_scanreq_duration, + vap->iv_scanreq_mindwell, + vap->iv_scanreq_maxdwell, + vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; + } else + ieee80211_check_scan_current(vap); + break; + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + /* + * These can happen either because of a timeout + * on an assoc/auth response or because of a + * change in state that requires a reset. For + * the former we're called with a non-zero arg + * that is the cause for the failure; pass this + * to the scan code so it can update state. + * Otherwise trigger a new scan unless we're in + * manual roaming mode in which case an application + * must issue an explicit scan request. + */ + if (arg != 0) + ieee80211_scan_assoc_fail(vap, + vap->iv_bss->ni_macaddr, arg); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + case IEEE80211_S_RUN: /* beacon miss */ + /* + * Beacon miss. Notify user space and if not + * under control of a user application (roaming + * manual) kick off a scan to re-connect. + */ + ieee80211_sta_leave(ni); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + default: + goto invalid; + } + break; + case IEEE80211_S_AUTH: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + switch (arg & 0xff) { + case IEEE80211_FC0_SUBTYPE_AUTH: + /* ??? */ + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + sta_authretry(vap, ni, arg>>8); + break; + } + break; + case IEEE80211_S_RUN: + switch (arg & 0xff) { + case IEEE80211_FC0_SUBTYPE_AUTH: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + vap->iv_state = ostate; /* stay RUN */ + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + ieee80211_sta_leave(ni); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { + /* try to reauth */ + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + } + break; + } + break; + default: + goto invalid; + } + break; + case IEEE80211_S_ASSOC: + switch (ostate) { + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + break; + case IEEE80211_S_SLEEP: /* cannot happen */ + case IEEE80211_S_RUN: + ieee80211_sta_leave(ni); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { + IEEE80211_SEND_MGMT(ni, arg ? + IEEE80211_FC0_SUBTYPE_REASSOC_REQ : + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + } + break; + default: + goto invalid; + } + break; + case IEEE80211_S_RUN: + if (vap->iv_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } + switch (ostate) { + case IEEE80211_S_RUN: + break; + case IEEE80211_S_AUTH: /* when join is done in fw */ + case IEEE80211_S_ASSOC: +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap)) { + ieee80211_note(vap, "%s with %s ssid ", + (vap->iv_opmode == IEEE80211_M_STA ? + "associated" : "synchronized"), + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(vap->iv_bss->ni_essid, + ni->ni_esslen); + /* XXX MCS/HT */ + printf(" channel %d start %uMb\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), + IEEE80211_RATE2MBS(ni->ni_txrate)); + } +#endif + ieee80211_scan_assoc_success(vap, ni->ni_macaddr); + ieee80211_notify_node_join(ni, + arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + break; + case IEEE80211_S_SLEEP: + ieee80211_sta_pwrsave(vap, 0); + break; + default: + goto invalid; + } + ieee80211_sync_curchan(ic); + if (ostate != IEEE80211_S_RUN && + (vap->iv_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. + */ + vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( + 2 * vap->iv_bmissthreshold * ni->ni_intval); + vap->iv_swbmiss_count = 0; + callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, + ieee80211_swbmiss, vap); + } + /* + * 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_S_SLEEP: + ieee80211_sta_pwrsave(vap, 0); + break; + default: + invalid: + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "%s: invalid state transition %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); + break; + } + return 0; +} + +/* + * Return non-zero if the frame is an echo of a multicast + * frame sent by ourself. The dir is known to be DSTODS. + */ +static __inline int +isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) +{ +#define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh) +#define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh) + const uint8_t *sa; + + KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); + + if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) + return 0; + sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4; + return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr); +#undef WH4 +#undef QWH4 +} + +/* + * Return non-zero if the frame is an echo of a multicast + * frame sent by ourself. The dir is known to be FROMDS. + */ +static __inline int +isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) +{ + KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + return 0; + return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); +} + +/* + * 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 ieee80211vap *vap, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return 0; + } + return 1; +} + +/* + * Process a received frame. The node associated with the sender + * should be supplied. If nothing was found in the node table then + * the caller is assumed to supply a reference to iv_bss instead. + * The RSSI and a timestamp are also supplied. The RSSI data is used + * during AP scanning to select a AP to associate with; it can have + * any units so long as values have consistent units and higher values + * mean ``better signal''. The receive timestamp is currently not used + * by the 802.11 layer. + */ +static int +sta_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp) +{ +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct ether_header *eh; + int hdrspace, need_tap; + uint8_t dir, type, subtype, qos; + uint8_t *bssid; + uint16_t rxseq; + + if (m->m_flags & M_AMPDU) { + /* + * Fastpath for A-MPDU reorder q resubmission. Frames + * w/ M_AMPDU marked have already passed through here + * but were received out of order and been held on the + * reorder queue. When resubmitted they are marked + * with the M_AMPDU flag and we can bypass most of the + * normal processing. + */ + wh = mtod(m, struct ieee80211_frame *); + type = IEEE80211_FC0_TYPE_DATA; + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + subtype = IEEE80211_FC0_SUBTYPE_QOS; + hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ + goto resubmit_ampdu; + } + + KASSERT(ni != NULL, ("null node")); + ni->ni_inact = ni->ni_inact_reload; + + need_tap = 1; /* mbuf need to be tapped. */ + type = -1; /* undefined */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_pkthdr.len); + vap->iv_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) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); + vap->iv_stats.is_rx_badversion++; + goto err; + } + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + bssid = wh->i_addr2; + if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { + /* not interested in */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); + vap->iv_stats.is_rx_wrongbss++; + goto out; + } + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + if (HAS_SEQ(type)) { + uint8_t tid = ieee80211_gettid(wh); + if (IEEE80211_QOS_HAS_SEQ(wh) && + TID_TO_WME_AC(tid) >= WME_AC_VI) + ic->ic_wme.wme_hipri_traffic++; + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && + (wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { + /* duplicate, discard */ + IEEE80211_DISCARD_MAC(vap, 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); + vap->iv_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + goto out; + } + ni->ni_rxseqs[tid] = rxseq; + } + } + + 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(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "data too short: expecting %u", hdrspace); + vap->iv_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + /* + * Handle A-MPDU re-ordering. The station must be + * associated and negotiated HT. The frame must be + * a QoS frame (not QoS null data) and not previously + * processed for A-MPDU re-ordering. If the frame is + * to be processed directly then ieee80211_ampdu_reorder + * will return 0; otherwise it has consumed the mbuf + * and we should do nothing more with it. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + subtype == IEEE80211_FC0_SUBTYPE_QOS && + (dir == IEEE80211_FC1_DIR_FROMDS || + dir == IEEE80211_FC1_DIR_DSTODS) && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + if (dir == IEEE80211_FC1_DIR_FROMDS) { + if ((ifp->if_flags & IFF_SIMPLEX) && + isfromds_mcastecho(vap, wh)) { + /* + * In IEEE802.11 network, multicast + * packets sent from "me" are broadcast + * from the AP; silently discard for + * SIMPLEX interface. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "multicast echo"); + vap->iv_stats.is_rx_mcastecho++; + goto out; + } + if ((vap->iv_flags & IEEE80211_F_DWDS) && + IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* + * DWDS sta's must drop 3-address mcast frames + * as they will be sent separately as a 4-addr + * frame. Accepting the 3-addr frame will + * confuse the bridge into thinking the sending + * sta is located at the end of WDS link. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, + "3-address data", "%s", "DWDS enabled"); + vap->iv_stats.is_rx_mcastecho++; + goto out; + } + } else if (dir == IEEE80211_FC1_DIR_DSTODS) { + if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT, wh, "4-address data", + "%s", "DWDS not enabled"); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + if ((ifp->if_flags & IFF_SIMPLEX) && + isdstods_mcastecho(vap, wh)) { + /* + * In IEEE802.11 network, multicast + * packets sent from "me" are broadcast + * from the AP; silently discard for + * SIMPLEX interface. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, + "4-address data", "%s", "multicast echo"); + vap->iv_stats.is_rx_mcastecho++; + goto out; + } + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, + "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + 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 ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "WEP", "%s", "PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + IEEE80211_NODE_STAT(ni, rx_noprivacy); + goto out; + } + key = ieee80211_crypto_decap(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 { + /* XXX M_WEP and IEEE80211_F_PRIVACY */ + key = NULL; + } + + /* + * Save QoS bits for use below--before we strip the header. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { + qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? + ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : + ((struct ieee80211_qosframe *)wh)->i_qos[0]; + } else + qos = 0; + + /* + * Next up, any fragmentation. + */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + m = ieee80211_defrag(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(vap, key, m, 0)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "demic error"); + vap->iv_stats.is_rx_demicfail++; + IEEE80211_NODE_STAT(ni, rx_demicfail); + goto out; + } + + /* copy to listener after decrypt */ + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + need_tap = 0; + + /* + * Finally, strip the 802.11 header. + */ + m = ieee80211_decap(vap, m, hdrspace); + if (m == NULL) { + /* XXX mask bit to check for both */ + /* don't count Null data frames as errors */ + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || + subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) + goto out; + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "decap error"); + vap->iv_stats.is_rx_decap++; + IEEE80211_NODE_STAT(ni, rx_decap); + goto err; + } + 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(vap, IEEE80211_MSG_INPUT, + eh->ether_shost, "data", + "unauthorized port: ether type 0x%x len %u", + eh->ether_type, m->m_pkthdr.len); + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && + (key == NULL && (m->m_flags & M_WEP) == 0) && + eh->ether_type != htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + vap->iv_stats.is_rx_unencrypted++; + IEEE80211_NODE_STAT(ni, rx_unencrypted); + goto out; + } + } + /* XXX require HT? */ + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + m->m_pkthdr.len >= 3*FF_LLC_SIZE) { + struct llc *llc; + + /* + * Check for fast-frame tunnel encapsulation. + */ + if (m->m_len < FF_LLC_SIZE && + (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "m_pullup(llc) failed"); + vap->iv_stats.is_rx_tooshort++; + return IEEE80211_FC0_TYPE_DATA; + } + llc = (struct llc *)(mtod(m, uint8_t *) + + sizeof(struct ether_header)); + if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { + m_adj(m, FF_LLC_SIZE); + m = ieee80211_decap_fastframe(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + } +#undef FF_LLC_SIZE + ieee80211_deliver_data(vap, ni, m); + return IEEE80211_FC0_TYPE_DATA; + + case IEEE80211_FC0_TYPE_MGT: + vap->iv_stats.is_rx_mgmt++; + IEEE80211_NODE_STAT(ni, rx_mgmt); + if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto err; + } + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "mgt", "too short: len %u", + m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } +#ifdef IEEE80211_DEBUG + if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || + ieee80211_msg_dumppkts(vap)) { + if_printf(ifp, "received %s from %s rssi %d\n", + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(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(vap, IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "WEP set but not permitted"); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "mgt", "%s", "WEP set but PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + goto out; + } + hdrspace = ieee80211_hdrspace(ic, wh); + key = ieee80211_crypto_decap(ni, m, hdrspace); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); + m_freem(m); + return IEEE80211_FC0_TYPE_MGT; + + case IEEE80211_FC0_TYPE_CTL: + vap->iv_stats.is_rx_ctl++; + IEEE80211_NODE_STAT(ni, rx_ctrl); + goto out; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "bad frame type 0x%x", type); + /* should not come here */ + break; + } +err: + ifp->if_ierrors++; +out: + if (m != NULL) { + if (bpf_peers_present(vap->iv_rawbpf) && need_tap) + bpf_mtap(vap->iv_rawbpf, m); + m_freem(m); + } + return type; +#undef SEQ_LEQ +} + +static void +sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, + int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "open auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_stats.is_rx_bad_auth++; /* XXX */ + return; + } + if (vap->iv_state != IEEE80211_S_AUTH || + seq != IEEE80211_AUTH_OPEN_RESPONSE) { + vap->iv_stats.is_rx_bad_auth++; + return; + } + if (status != 0) { + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ni, "open auth failed (reason %d)", status); + vap->iv_stats.is_rx_auth_fail++; + vap->iv_stats.is_rx_authfail_code = status; + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); + } else + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); +} + +static void +sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, + uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp, + uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + uint8_t *challenge; + int 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 ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DISCARD_MAC(vap, 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(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_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(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "ie %d/%d too long", + frm[0], (frm[1] + 2) - (efrm - frm)); + vap->iv_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(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", "no challenge"); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad challenge len %d", challenge[1]); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + if (vap->iv_state != IEEE80211_S_AUTH) + return; + switch (seq) { + case IEEE80211_AUTH_SHARED_PASS: + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + if (status != 0) { + IEEE80211_NOTE_FRAME(vap, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh, + "shared key auth failed (reason %d)", status); + vap->iv_stats.is_rx_auth_fail++; + vap->iv_stats.is_rx_authfail_code = status; + return; + } + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); + break; + case IEEE80211_AUTH_SHARED_CHALLENGE: + if (!ieee80211_alloc_challenge(ni)) + return; + /* XXX could optimize by passing recvd challenge */ + memcpy(ni->ni_challenge, &challenge[2], challenge[1]); + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH, + wh, "shared key auth", "bad seq %d", seq); + vap->iv_stats.is_rx_bad_auth++; + return; + } + return; +bad: + /* + * Kick the state machine. This short-circuits + * using the mgt frame timeout to trigger the + * state transition. + */ + if (vap->iv_state == IEEE80211_S_AUTH) + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); +} + +static int +ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, + const struct ieee80211_frame *wh) +{ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme; + u_int len = frm[1], qosinfo; + int i; + + if (len < sizeof(struct ieee80211_wme_param)-2) { + IEEE80211_DISCARD_IE(vap, + 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 +} + +static int +ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm, + const struct ieee80211_frame *wh) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_ath_ie *ath; + u_int len = frm[1]; + int capschanged; + uint16_t defkeyix; + + if (len < sizeof(struct ieee80211_ath_ie)-2) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG, + wh, "Atheros", "too short, len %u", len); + return -1; + } + ath = (const struct ieee80211_ath_ie *)frm; + capschanged = (ni->ni_ath_flags != ath->ath_capability); + defkeyix = LE_READ_2(ath->ath_defkeyix); + if (capschanged || defkeyix != ni->ni_ath_defkeyix) { + ni->ni_ath_flags = ath->ath_capability; + ni->ni_ath_defkeyix = defkeyix; + IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni, + "ath ie change: new caps 0x%x defkeyix 0x%x", + ni->ni_ath_flags, ni->ni_ath_defkeyix); + } + if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) { + uint16_t curflags, newflags; + + /* + * Check for turbo mode switch. Calculate flags + * for the new mode and effect the switch. + */ + newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags; + /* NB: BOOST is not in ic_flags, so get it from the ie */ + if (ath->ath_capability & ATHEROS_CAP_BOOST) + newflags |= IEEE80211_CHAN_TURBO; + else + newflags &= ~IEEE80211_CHAN_TURBO; + if (newflags != curflags) + ieee80211_dturbo_switch(vap, newflags); + } + return capschanged; +} + +/* + * Return non-zero if a background scan may be continued: + * o bg scan is active + * o no channel switch is pending + * o there has not been any traffic recently + * + * Note we do not check if there is an administrative enable; + * this is only done to start the scan. We assume that any + * change in state will be accompanied by a request to cancel + * active scans which will otherwise cause this test to fail. + */ +static __inline int +contbgscan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && + (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && + vap->iv_state == IEEE80211_S_RUN && /* XXX? */ + time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); +} + +/* + * Return non-zero if a backgrond scan may be started: + * o bg scanning is administratively enabled + * o no channel switch is pending + * o we are not boosted on a dynamic turbo channel + * o there has not been a scan recently + * o there has not been any traffic recently + */ +static __inline int +startbgscan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + return ((vap->iv_flags & IEEE80211_F_BGSCAN) && + (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && + !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && + time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && + time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); +} + +static void +sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, + int subtype, int rssi, int noise, uint32_t rstamp) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm; + uint8_t *rates, *xrates, *wme, *htcap, *htinfo; + 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 + * Frames otherwise received are discarded. + */ + if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + /* XXX probe response in sta mode when !scanning? */ + if (ieee80211_parse_beacon(ni, m0, &scan) != 0) + return; + /* + * Count frame now that we know it's to be processed. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + vap->iv_stats.is_rx_beacon++; /* XXX remove */ + IEEE80211_NODE_STAT(ni, rx_beacons); + } else + IEEE80211_NODE_STAT(ni, rx_proberesp); + /* + * 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 (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 */ + vap->iv_swbmiss_count++; + vap->iv_bmiss_count = 0; + if (ni->ni_erp != scan.erp) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, + "erp change: was 0x%x, now 0x%x", + ni->ni_erp, scan.erp); + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + (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 */ + /* XXX driver notification */ + } + if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, + "capabilities change: was 0x%x, now 0x%x", + ni->ni_capinfo, scan.capinfo); + /* + * NB: we assume short preamble doesn't + * change dynamically + */ + ieee80211_set_shortslottime(ic, + IEEE80211_IS_CHAN_A(ic->ic_bsschan) || + (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) + | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); + /* XXX statistic */ + } + if (scan.wme != NULL && + (ni->ni_flags & IEEE80211_NODE_QOS) && + ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0) + ieee80211_wme_updateparams(vap); + if (scan.ath != NULL) + ieee80211_parse_athparams(ni, scan.ath, wh); + if (scan.htcap != NULL && scan.htinfo != NULL) { + ieee80211_parse_htcap(ni, scan.htcap); + ieee80211_parse_htinfo(ni, scan.htinfo); + /* XXX state changes? */ + } + if (scan.tim != NULL) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *) scan.tim; +#if 0 + int aid = IEEE80211_AID(ni->ni_associd); + int ix = aid / NBBY; + int min = tim->tim_bitctl &~ 1; + int max = tim->tim_len + min - 4; + if ((tim->tim_bitctl&1) || + (min <= ix && ix <= max && + isset(tim->tim_bitmap - min, aid))) { + /* + * XXX Do not let bg scan kick off + * we are expecting data. + */ + ic->ic_lastdata = ticks; + ieee80211_sta_pwrsave(vap, 0); + } +#endif + ni->ni_dtim_count = tim->tim_count; + ni->ni_dtim_period = tim->tim_period; + } + /* + * If scanning, pass the info to the scan module. + * Otherwise, check if it's the right time to do + * a background scan. Background scanning must + * be enabled and we must not be operating in the + * turbo phase of dynamic turbo mode. Then, + * it's been a while since the last background + * scan and if no data frames have come through + * recently, kick off a scan. Note that this + * is the mechanism by which a background scan + * is started _and_ continued each time we + * return on-channel to receive a beacon from + * our ap. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { + ieee80211_add_scan(vap, &scan, wh, + subtype, rssi, noise, rstamp); + } else if (contbgscan(vap)) { + ieee80211_bg_scan(vap, 0); + } else if (startbgscan(vap)) { + vap->iv_stats.is_scan_bg++; +#if 0 + /* wakeup if we are sleeing */ + ieee80211_set_pwrsave(vap, 0); +#endif + ieee80211_bg_scan(vap, 0); + } + 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(vap, 1); + ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; + } + ieee80211_add_scan(vap, &scan, wh, + subtype, rssi, noise, rstamp); + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_AUTH: { + uint16_t algo, seq, status; + /* + * auth frame format + * [2] algorithm + * [2] sequence + * [2] status + * [tlv*] challenge + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + algo = le16toh(*(uint16_t *)frm); + seq = le16toh(*(uint16_t *)(frm + 2)); + status = le16toh(*(uint16_t *)(frm + 4)); + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, + "recv auth frame with algorithm %d seq %d", algo, seq); + + if (vap->iv_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, + wh, "auth", "%s", "TKIP countermeasures enabled"); + vap->iv_stats.is_rx_auth_countermeasures++; + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_REASON_MIC_FAILURE); + } + return; + } + if (algo == IEEE80211_AUTH_ALG_SHARED) + sta_auth_shared(ni, wh, frm + 6, efrm, rssi, + noise, rstamp, seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + sta_auth_open(ni, wh, rssi, noise, rstamp, + seq, status); + else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "auth", "unsupported alg %d", algo); + vap->iv_stats.is_rx_auth_unsupported++; + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { + uint16_t capinfo, associd; + uint16_t status; + + if (vap->iv_state != IEEE80211_S_ASSOC) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + + /* + * asresp frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WME + * [tlv] HT capabilities + * [tlv] HT info + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + ni = vap->iv_bss; + capinfo = le16toh(*(uint16_t *)frm); + frm += 2; + status = le16toh(*(uint16_t *)frm); + frm += 2; + if (status != 0) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, "%sassoc failed (reason %d)", + ISREASSOC(subtype) ? "re" : "", status); + vap->iv_stats.is_rx_auth_fail++; /* XXX */ + return; + } + associd = le16toh(*(uint16_t *)frm); + frm += 2; + + rates = xrates = wme = htcap = htinfo = NULL; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); + switch (*frm) { + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; + break; + case IEEE80211_ELEMID_HTINFO: + htinfo = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswmeoui(frm)) + wme = frm; + else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) { + /* + * Accept pre-draft HT ie's if the + * standard ones have not been seen. + */ + if (ishtcapoui(frm)) { + if (htcap == NULL) + htcap = frm; + } else if (ishtinfooui(frm)) { + if (htinfo == NULL) + htcap = frm; + } + } + /* XXX Atheros OUI support */ + break; + } + frm += frm[1] + 2; + } + + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1], return); + rate = ieee80211_setup_rates(ni, rates, xrates, + IEEE80211_F_JOIN | + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (rate & IEEE80211_RATE_BASIC) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, + "%sassoc failed (rate set mismatch)", + ISREASSOC(subtype) ? "re" : ""); + vap->iv_stats.is_rx_assoc_norate++; + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); + return; + } + + ni->ni_capinfo = capinfo; + ni->ni_associd = associd; + if (ni->ni_jointime == 0) + ni->ni_jointime = time_uptime; + if (wme != NULL && + ieee80211_parse_wmeparams(vap, wme, wh) >= 0) { + ni->ni_flags |= IEEE80211_NODE_QOS; + ieee80211_wme_updateparams(vap); + } else + ni->ni_flags &= ~IEEE80211_NODE_QOS; + /* + * Setup HT state according to the negotiation. + * + * NB: shouldn't need to check if HT use is enabled but some + * ap's send back HT ie's even when we don't indicate we + * are HT capable in our AssocReq. + */ + if (htcap != NULL && htinfo != NULL && + (vap->iv_flags_ext & IEEE80211_FEXT_HT)) { + ieee80211_ht_node_init(ni, htcap); + ieee80211_parse_htinfo(ni, htinfo); + ieee80211_setup_htrates(ni, htcap, + IEEE80211_F_JOIN | IEEE80211_F_DOBRS); + ieee80211_setup_basic_htrates(ni, htinfo); + } + /* + * Configure state now that we are associated. + * + * XXX may need different/additional driver callbacks? + */ + if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || + (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, + IEEE80211_IS_CHAN_A(ic->ic_curchan) || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + /* + * Honor ERP protection. + * + * NB: ni_erp should zero for non-11g operation. + */ + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2, + "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s", + ISREASSOC(subtype) ? "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" : "", + ni->ni_flags & IEEE80211_NODE_HT ? + (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "", + ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? + ", fast-frames" : "", + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? + ", turbo" : "" + ); + ieee80211_new_state(vap, IEEE80211_S_RUN, subtype); + break; + } + + case IEEE80211_FC0_SUBTYPE_DEAUTH: { + uint16_t reason; + + if (vap->iv_state == IEEE80211_S_SCAN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { + /* NB: can happen when in promiscuous mode */ + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + + /* + * deauth frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); + + vap->iv_stats.is_rx_deauth++; + vap->iv_stats.is_rx_deauth_code = reason; + IEEE80211_NODE_STAT(ni, rx_deauth); + + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "recv deauthenticate (reason %d)", reason); + ieee80211_new_state(vap, IEEE80211_S_AUTH, + (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); + break; + } + + case IEEE80211_FC0_SUBTYPE_DISASSOC: { + uint16_t reason; + + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_ASSOC && + vap->iv_state != IEEE80211_S_AUTH) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { + /* NB: can happen when in promiscuous mode */ + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + + /* + * disassoc frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); + + vap->iv_stats.is_rx_disassoc++; + vap->iv_stats.is_rx_disassoc_code = reason; + IEEE80211_NODE_STAT(ni, rx_disassoc); + + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "recv disassociate (reason %d)", reason); + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); + break; + } + + case IEEE80211_FC0_SUBTYPE_ACTION: + if (vap->iv_state == IEEE80211_S_RUN) { + if (ieee80211_parse_action(ni, m0) == 0) + ic->ic_recv_action(ni, frm, efrm); + } else + vap->iv_stats.is_rx_mgtdiscard++; + break; + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + vap->iv_stats.is_rx_mgtdiscard++; + return; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); + vap->iv_stats.is_rx_badsubtype++; + break; + } +#undef ISREASSOC +#undef ISPROBE +} diff --git a/sys/net80211/ieee80211_sta.h b/sys/net80211/ieee80211_sta.h new file mode 100644 index 0000000..1508a7c --- /dev/null +++ b/sys/net80211/ieee80211_sta.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_STA_H_ +#define _NET80211_IEEE80211_STA_H_ + +/* + * Station-mode implementation definitions. + */ +void ieee80211_sta_attach(struct ieee80211com *); +void ieee80211_sta_detach(struct ieee80211com *); +void ieee80211_sta_vattach(struct ieee80211vap *); +#endif /* !_NET80211_IEEE80211_STA_H_ */ diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 3171cef..5ada0a2 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,9 +31,6 @@ /* * 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 <net80211/ieee80211_netbsd.h> @@ -48,6 +45,7 @@ #include <net80211/_ieee80211.h> #include <net80211/ieee80211.h> #include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_dfs.h> #include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */ #include <net80211/ieee80211_node.h> #include <net80211/ieee80211_power.h> @@ -75,8 +73,8 @@ #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ #define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ -#define IEEE80211_FIXED_RATE_NONE -1 -#define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */ +#define IEEE80211_FIXED_RATE_NONE 0xff +#define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */ #define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX #define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX @@ -85,40 +83,57 @@ #define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000) #define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000)) -struct ieee80211_aclator; -struct sysctl_ctx_list; +/* + * 802.11 control state is split into a common portion that maps + * 1-1 to a physical device and one or more "Virtual AP's" (VAP) + * that are bound to an ieee80211com instance and share a single + * underlying device. Each VAP has a corresponding OS device + * entity through which traffic flows and that applications use + * for issuing ioctls, etc. + */ + +/* + * Data common to one or more virtual AP's. State shared by + * the underlying device and the net80211 layer is exposed here; + * e.g. device-specific callbacks. + */ +struct ieee80211vap; +typedef void (*ieee80211vap_attach)(struct ieee80211vap *); + +struct ieee80211_appie { + uint16_t ie_len; /* size of ie_data */ + uint8_t ie_data[]; /* user-specified IE's */ +}; struct ieee80211com { - SLIST_ENTRY(ieee80211com) ic_next; struct ifnet *ic_ifp; /* associated device */ ieee80211_com_lock_t ic_comlock; /* state update lock */ - ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */ + TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */ 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_headroom; /* driver tx headroom needs */ enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ enum ieee80211_opmode ic_opmode; /* operation mode */ struct ifmedia ic_media; /* interface media config */ uint8_t ic_myaddr[IEEE80211_ADDR_LEN]; + struct callout ic_inact; /* inactivity processing */ + struct task ic_parent_task; /* deferred parent processing */ uint32_t ic_flags; /* state flags */ uint32_t ic_flags_ext; /* extended state flags */ uint32_t ic_flags_ven; /* vendor state flags */ uint32_t ic_caps; /* capabilities */ uint32_t ic_htcaps; /* HT capabilities */ + uint32_t ic_cryptocaps; /* crypto capabilities */ uint8_t ic_modecaps[2]; /* set of mode capabilities */ - uint16_t ic_curmode; /* current mode */ - struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; + uint8_t ic_promisc; /* vap's needing promisc mode */ + uint8_t ic_allmulti; /* vap's needing all multicast*/ + uint8_t ic_nrunning; /* vap's marked running */ + uint8_t ic_curmode; /* current mode */ uint16_t ic_bintval; /* beacon interval */ uint16_t ic_lintval; /* listen interval */ uint16_t ic_holdover; /* PM hold over duration */ uint16_t ic_txpowlimit; /* global tx power limit */ - int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */ - int ic_ampdu_density;/* A-MPDU density */ - int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */ - int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */ + struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; /* * Channel state: @@ -148,30 +163,27 @@ struct ieee80211com { struct ieee80211_channel *ic_curchan; /* current channel */ struct ieee80211_channel *ic_bsschan; /* bss channel */ struct ieee80211_channel *ic_prevchan; /* previous channel */ - int ic_countrycode; /* ISO country code */ - uint16_t ic_regdomain; /* regulatory domain */ - uint8_t ic_location; /* unknown, indoor, outdoor */ + struct ieee80211_regdomain ic_regdomain;/* regulatory data */ + struct ieee80211_appie *ic_countryie; /* calculated country ie */ + struct ieee80211_channel *ic_countryie_chan; + + /* 802.11h/DFS state */ + struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */ + int ic_csa_count; /* count for doing CSA */ + struct ieee80211_dfs_state ic_dfs; /* DFS state */ struct ieee80211_scan_state *ic_scan; /* scan state */ - enum ieee80211_roamingmode ic_roaming; /* roaming mode */ int ic_lastdata; /* time of last data frame */ int ic_lastscan; /* time last scan completed */ - int ic_des_nssid; /* # desired ssids */ - struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */ - uint8_t ic_des_bssid[IEEE80211_ADDR_LEN]; - struct ieee80211_channel *ic_des_chan; /* desired channel */ - int ic_des_mode; /* desired phymode */ - u_int ic_bgscanidle; /* bg scan idle threshold */ - u_int ic_bgscanintvl; /* bg scan min interval */ - u_int ic_scanvalid; /* scan cache valid threshold */ - struct ieee80211_roam ic_roam; /* sta-mode roaming state */ + /* NB: this is the union of all vap stations/neighbors */ + int ic_max_keyix; /* max h/w key index */ struct ieee80211_node_table ic_sta; /* stations/neighbors */ + /* XXX multi-bss: split out common/vap parts */ struct ieee80211_wme_state ic_wme; /* WME/WMM state */ - const struct ieee80211_aclator *ic_acl; /* aclator glue */ - void *ic_as; /* private aclator state */ + /* XXX multi-bss: can per-vap be done/make sense? */ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ uint16_t ic_nonerpsta; /* # non-ERP stations */ uint16_t ic_longslotsta; /* # long slot time stations */ @@ -183,91 +195,56 @@ struct ieee80211com { int ic_lastnonerp; /* last time non-ERP sta noted*/ int ic_lastnonht; /* last time non-HT sta noted */ - struct ifqueue ic_mgtq; - enum ieee80211_state ic_state; /* 802.11 state */ - struct callout ic_mgtsend; /* mgmt frame response timer */ - uint32_t *ic_aid_bitmap; /* association id map */ - uint16_t ic_max_aid; - 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 bpf_if *ic_rawbpf; /* packet filter structure */ - struct ieee80211_node *ic_bss; /* information for this node */ - int ic_fixed_rate; /* 802.11 rate or -1 */ - int ic_mcast_rate; /* rate for mcast frames */ - uint16_t ic_rtsthreshold; - uint16_t ic_fragthreshold; - 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 */ - - uint16_t ic_txmin; /* min tx retry count */ - uint16_t ic_txmax; /* max tx retry count */ - uint16_t ic_txlifetime; /* tx lifetime */ - struct callout ic_inact; /* inactivity timer wait */ - void *ic_opt_ie; /* user-specified IE's */ - uint16_t ic_opt_ie_len; /* length of ni_opt_ie */ - 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 */ - - /* - * 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; - + /* virtual ap create/delete */ + struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, + int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t macaddr[IEEE80211_ADDR_LEN]); + void (*ic_vap_delete)(struct ieee80211vap *); + /* operating mode attachment */ + ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX]; + /* return hardware/radio capabilities */ + void (*ic_getradiocaps)(struct ieee80211com *, + int *, struct ieee80211_channel []); + /* check and/or prepare regdomain state change */ + int (*ic_setregdomain)(struct ieee80211com *, + struct ieee80211_regdomain *, + int, struct ieee80211_channel []); /* send/recv 802.11 management frame */ - int (*ic_send_mgmt)(struct ieee80211com *, - struct ieee80211_node *, int, int); - void (*ic_recv_mgmt)(struct ieee80211com *, - struct mbuf *, struct ieee80211_node *, - int, int, int, uint32_t); + int (*ic_send_mgmt)(struct ieee80211_node *, + int, int); /* send raw 802.11 frame */ int (*ic_raw_xmit)(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); - /* reset device state after 802.11 parameter/state change */ - int (*ic_reset)(struct ifnet *); - /* [schedule] beacon frame update */ - void (*ic_update_beacon)(struct ieee80211com *, int); /* update device state for 802.11 slot time change */ void (*ic_updateslot)(struct ifnet *); + /* handle multicast state changes */ + void (*ic_update_mcast)(struct ifnet *); + /* handle promiscuous mode changes */ + void (*ic_update_promisc)(struct ifnet *); /* new station association callback/notification */ void (*ic_newassoc)(struct ieee80211_node *, int); /* node state management */ - struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*); + struct ieee80211_node* (*ic_node_alloc)(struct ieee80211_node_table *); void (*ic_node_free)(struct ieee80211_node *); void (*ic_node_cleanup)(struct ieee80211_node *); + void (*ic_node_age)(struct ieee80211_node *); + void (*ic_node_drain)(struct ieee80211_node *); int8_t (*ic_node_getrssi)(const struct ieee80211_node*); void (*ic_node_getsignal)(const struct ieee80211_node*, int8_t *, int8_t *); + void (*ic_node_getmimoinfo)( + const struct ieee80211_node*, + struct ieee80211_mimo_info *); /* scanning support */ void (*ic_scan_start)(struct ieee80211com *); void (*ic_scan_end)(struct ieee80211com *); void (*ic_set_channel)(struct ieee80211com *); - void (*ic_scan_curchan)(struct ieee80211com *, + void (*ic_scan_curchan)(struct ieee80211_scan_state *, unsigned long); - void (*ic_scan_mindwell)(struct ieee80211com *); - /* per-vap eventually... */ - int (*ic_newstate)(struct ieee80211com *, - enum ieee80211_state, int); - void (*ic_set_tim)(struct ieee80211_node *, int); + void (*ic_scan_mindwell)(struct ieee80211_scan_state *); /* * 802.11n ADDBA support. A simple/generic implementation @@ -282,6 +259,9 @@ struct ieee80211com { int (*ic_send_action)(struct ieee80211_node *, int category, int action, uint16_t args[4]); + /* check if A-MPDU should be enabled this station+ac */ + int (*ic_ampdu_enable)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); /* start/stop doing A-MPDU tx aggregation for a station */ int (*ic_addba_request)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, @@ -294,11 +274,154 @@ struct ieee80211com { struct ieee80211_tx_ampdu *); }; +struct ieee80211_aclator; + +struct ieee80211vap { + struct ifmedia iv_media; /* interface media config */ + struct ifnet *iv_ifp; /* associated device */ + struct bpf_if *iv_rawbpf; /* packet filter structure */ + struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */ + struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */ + + TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ + struct ieee80211com *iv_ic; /* back ptr to common state */ + uint32_t iv_debug; /* debug msg flags */ + struct ieee80211_stats iv_stats; /* statistics */ + + uint8_t iv_myaddr[IEEE80211_ADDR_LEN]; + uint32_t iv_flags; /* state flags */ + uint32_t iv_flags_ext; /* extended state flags */ + uint32_t iv_flags_ven; /* vendor state flags */ + uint32_t iv_caps; /* capabilities */ + uint32_t iv_htcaps; /* HT capabilities */ + enum ieee80211_opmode iv_opmode; /* operation mode */ + enum ieee80211_state iv_state; /* state machine state */ + void (*iv_newstate_cb)(struct ieee80211vap *, + enum ieee80211_state, int); + struct callout iv_mgtsend; /* mgmt frame response timer */ + /* inactivity timer settings */ + int iv_inact_init; /* setting for new station */ + int iv_inact_auth; /* auth but not assoc setting */ + int iv_inact_run; /* authorized setting */ + int iv_inact_probe; /* inactive probe time */ + + int iv_des_nssid; /* # desired ssids */ + struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */ + uint8_t iv_des_bssid[IEEE80211_ADDR_LEN]; + struct ieee80211_channel *iv_des_chan; /* desired channel */ + uint16_t iv_des_mode; /* desired mode */ + int iv_nicknamelen; /* XXX junk */ + uint8_t iv_nickname[IEEE80211_NWID_LEN]; + u_int iv_bgscanidle; /* bg scan idle threshold */ + u_int iv_bgscanintvl; /* bg scan min interval */ + u_int iv_scanvalid; /* scan cache valid threshold */ + u_int iv_scanreq_duration; + u_int iv_scanreq_mindwell; + u_int iv_scanreq_maxdwell; + uint16_t iv_scanreq_flags;/* held scan request params */ + uint8_t iv_scanreq_nssid; + struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID]; + /* sta-mode roaming state */ + enum ieee80211_roamingmode iv_roaming; /* roaming mode */ + struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX]; + + uint8_t iv_bmissthreshold; + uint8_t iv_bmiss_count; /* current beacon miss count */ + int iv_bmiss_max; /* max bmiss before scan */ + uint16_t iv_swbmiss_count;/* beacons in last period */ + uint16_t iv_swbmiss_period;/* s/w bmiss period */ + struct callout iv_swbmiss; /* s/w beacon miss timer */ + + int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */ + int iv_ampdu_density;/* A-MPDU density */ + int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */ + int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */ + u_int iv_ampdu_mintraffic[WME_NUM_AC]; + + uint32_t *iv_aid_bitmap; /* association id map */ + uint16_t iv_max_aid; + uint16_t iv_sta_assoc; /* stations associated */ + uint16_t iv_ps_sta; /* stations in power save */ + uint16_t iv_ps_pending; /* ps sta's w/ pending frames */ + uint16_t iv_txseq; /* mcast xmit seq# space */ + uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */ + uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/ + uint8_t iv_dtim_period; /* DTIM period */ + uint8_t iv_dtim_count; /* DTIM count from last bcn */ + /* set/unset aid pwrsav state */ + int iv_csa_count; /* count for doing CSA */ + + struct ieee80211_node *iv_bss; /* information for this node */ + struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX]; + uint16_t iv_rtsthreshold; + uint16_t iv_fragthreshold; + int iv_inact_timer; /* inactivity timer wait */ + /* application-specified IE's to attach to mgt frames */ + struct ieee80211_appie *iv_appie_beacon; + struct ieee80211_appie *iv_appie_probereq; + struct ieee80211_appie *iv_appie_proberesp; + struct ieee80211_appie *iv_appie_assocreq; + struct ieee80211_appie *iv_appie_assocresp; + struct ieee80211_appie *iv_appie_wpa; + uint8_t *iv_wpa_ie; + uint8_t *iv_rsn_ie; + uint16_t iv_max_keyix; /* max h/w key index */ + ieee80211_keyix iv_def_txkey; /* default/group tx key index */ + struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID]; + int (*iv_key_alloc)(struct ieee80211vap *, + const struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); + int (*iv_key_delete)(struct ieee80211vap *, + const struct ieee80211_key *); + int (*iv_key_set)(struct ieee80211vap *, + const struct ieee80211_key *, + const uint8_t mac[IEEE80211_ADDR_LEN]); + void (*iv_key_update_begin)(struct ieee80211vap *); + void (*iv_key_update_end)(struct ieee80211vap *); + + const struct ieee80211_authenticator *iv_auth; /* authenticator glue */ + void *iv_ec; /* private auth state */ + + const struct ieee80211_aclator *iv_acl; /* acl glue */ + void *iv_as; /* private aclator state */ + + /* operate-mode detach hook */ + void (*iv_opdetach)(struct ieee80211vap *); + /* receive processing */ + int (*iv_input)(struct ieee80211_node *, + struct mbuf *, int rssi, int noise, + uint32_t rstamp); + void (*iv_recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, int, int, uint32_t); + void (*iv_deliver_data)(struct ieee80211vap *, + struct ieee80211_node *, struct mbuf *); +#if 0 + /* send processing */ + int (*iv_send_mgmt)(struct ieee80211_node *, + int, int); +#endif + /* beacon miss processing */ + void (*iv_bmiss)(struct ieee80211vap *); + /* reset device state after 802.11 parameter/state change */ + int (*iv_reset)(struct ieee80211vap *, u_long); + /* [schedule] beacon frame update */ + void (*iv_update_beacon)(struct ieee80211vap *, int); + /* power save handling */ + void (*iv_update_ps)(struct ieee80211vap *, int); + int (*iv_set_tim)(struct ieee80211_node *, int); + /* state machine processing */ + int (*iv_newstate)(struct ieee80211vap *, + enum ieee80211_state, int); + /* 802.3 output method for raw frame xmit */ + int (*iv_output)(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +}; +MALLOC_DECLARE(M_80211_VAP); + #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 */ -/* NB: bits 0x4c available */ +/* ic_flags/iv_flags */ #define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/ #define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */ #define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */ @@ -322,6 +445,7 @@ struct ieee80211com { #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_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/ #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 */ @@ -329,22 +453,32 @@ struct ieee80211com { #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_PCF 0x20000000 /* CONF: PCF enabled */ #define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */ +#define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */ /* Atheros protocol-specific flags */ #define IEEE80211_F_ATHEROS \ (IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP) /* Check if an Atheros capability was negotiated for use */ -#define IEEE80211_ATH_CAP(ic, ni, bit) \ - ((ic)->ic_flags & (ni)->ni_ath_flags & (bit)) +#define IEEE80211_ATH_CAP(vap, ni, bit) \ + ((vap)->iv_flags & (ni)->ni_ath_flags & (bit)) -/* ic_flags_ext */ +/* ic_flags_ext/iv_flags_ext */ #define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */ #define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */ +#define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */ /* 0x00000006 reserved */ #define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */ +#define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */ +#define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */ +#define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */ +#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */ #define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/ #define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */ +#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */ +/* NB: immutable: should be set only when creating a vap */ +#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */ #define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/ #define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */ #define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */ @@ -357,12 +491,8 @@ struct ieee80211com { #define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */ #define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */ -/* ic_caps */ -#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ -#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 */ +/* ic_caps/iv_caps: device driver capabilities */ +/* 0x2f 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 */ @@ -374,7 +504,7 @@ struct ieee80211com { #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 */ +/* 0x20000 available */ #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*/ @@ -386,10 +516,8 @@ struct ieee80211com { #define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ /* XXX protection/barker? */ -#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */ - /* - * ic_htcaps: HT-specific device/driver capabilities + * ic_htcaps/iv_htcaps: HT-specific device/driver capabilities * * NB: the low 16-bits are the 802.11 definitions, the upper * 16-bits are used to define s/w/driver capabilities. @@ -401,18 +529,23 @@ struct ieee80211com { void ieee80211_ifattach(struct ieee80211com *); void ieee80211_ifdetach(struct ieee80211com *); +int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t macaddr[IEEE80211_ADDR_LEN]); +int ieee80211_vap_attach(struct ieee80211vap *, + ifm_change_cb_t, ifm_stat_cb_t); +void ieee80211_vap_detach(struct ieee80211vap *); const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *); void ieee80211_announce(struct ieee80211com *); void ieee80211_announce_channels(struct ieee80211com *); -void ieee80211_media_init(struct ieee80211com *, - ifm_change_cb_t, ifm_stat_cb_t); +void ieee80211_drain(struct ieee80211com *); +void ieee80211_media_init(struct ieee80211com *); 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 ieee80211com *, u_long, caddr_t); -int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t); -int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t); +int ieee80211_ioctl(struct ifnet *, u_long, caddr_t); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); @@ -431,14 +564,14 @@ enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *); * Key update synchronization methods. XXX should not be visible. */ static __inline void -ieee80211_key_update_begin(struct ieee80211com *ic) +ieee80211_key_update_begin(struct ieee80211vap *vap) { - ic->ic_crypto.cs_key_update_begin(ic); + vap->iv_key_update_begin(vap); } static __inline void -ieee80211_key_update_end(struct ieee80211com *ic) +ieee80211_key_update_end(struct ieee80211vap *vap) { - ic->ic_crypto.cs_key_update_end(ic); + vap->iv_key_update_end(vap); } /* @@ -472,13 +605,25 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) } /* - * Notify a driver that beacon state has been updated. + * Notify a vap that beacon state has been updated. */ static __inline void -ieee80211_beacon_notify(struct ieee80211com *ic, int what) +ieee80211_beacon_notify(struct ieee80211vap *vap, int what) +{ + if (vap->iv_state == IEEE80211_S_RUN) + vap->iv_update_beacon(vap, what); +} + +/* + * Calculate HT channel promotion flags for a channel. + * XXX belongs in ieee80211_ht.h but needs IEEE80211_FEXT_* + */ +static __inline int +ieee80211_htchanflags(const struct ieee80211_channel *c) { - if (ic->ic_state == IEEE80211_S_RUN) - ic->ic_update_beacon(ic, what); + return IEEE80211_IS_CHAN_HT40(c) ? + IEEE80211_FEXT_HT | IEEE80211_FEXT_USEHT40 : + IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FEXT_HT : 0; } /* @@ -525,44 +670,44 @@ ieee80211_beacon_notify(struct ieee80211com *ic, int what) #define IEEE80211_MSG_ANY 0xffffffff /* anything */ #ifdef IEEE80211_DEBUG -#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__); \ +#define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m)) +#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \ + if (ieee80211_msg(_vap, _m)) \ + ieee80211_note(_vap, _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__);\ +#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \ + if (ieee80211_msg(_vap, _m)) \ + ieee80211_note_mac(_vap, (_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__); \ +#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \ + if (ieee80211_msg(_vap, _m)) \ + ieee80211_note_mac(_vap, _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__); \ +#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \ + if (ieee80211_msg(_vap, _m)) \ + ieee80211_note_frame(_vap, _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) +void ieee80211_note(struct ieee80211vap *, const char *, ...); +void ieee80211_note_mac(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...); +void ieee80211_note_frame(struct ieee80211vap *, + const struct ieee80211_frame *, const char *, ...); +#define ieee80211_msg_debug(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_DEBUG) +#define ieee80211_msg_dumppkts(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS) +#define ieee80211_msg_input(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_INPUT) +#define ieee80211_msg_radius(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_RADIUS) +#define ieee80211_msg_dumpradius(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_RADDUMP) +#define ieee80211_msg_dumpradkeys(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_RADKEYS) +#define ieee80211_msg_scan(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_SCAN) +#define ieee80211_msg_assoc(_vap) \ + ((_vap)->iv_debug & IEEE80211_MSG_ASSOC) /* * Emit a debug message about discarding a frame or information @@ -570,37 +715,37 @@ void ieee80211_note_frame(struct ieee80211com *ic, * 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__);\ +#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \ + if ((_vap)->iv_debug & (_m)) \ + ieee80211_discard_frame(_vap, _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__);\ +#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \ + if ((_vap)->iv_debug & (_m)) \ + ieee80211_discard_ie(_vap, _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__);\ +#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \ + if ((_vap)->iv_debug & (_m)) \ + ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\ } while (0) -void ieee80211_discard_frame(struct ieee80211com *, +void ieee80211_discard_frame(struct ieee80211vap *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); -void ieee80211_discard_ie(struct ieee80211com *, +void ieee80211_discard_ie(struct ieee80211vap *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); -void ieee80211_discard_mac(struct ieee80211com *, +void ieee80211_discard_mac(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...); #else -#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) -#define IEEE80211_NOTE(_ic, _m, _ni, _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 - -#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, ...) +#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) +#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) +#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) +#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) +#define ieee80211_msg_dumppkts(_vap) 0 +#define ieee80211_msg(_vap, _m) 0 + +#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) +#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) +#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) #endif #endif /* _NET80211_IEEE80211_VAR_H_ */ diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c new file mode 100644 index 0000000..f3d90f9 --- /dev/null +++ b/sys/net80211/ieee80211_wds.c @@ -0,0 +1,865 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 WDS mode support. + */ +#include "opt_inet.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_llc.h> +#include <net/ethernet.h> + +#include <net/bpf.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_wds.h> +#include <net80211/ieee80211_input.h> + +static void wds_vattach(struct ieee80211vap *); +static int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int wds_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp); +static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *, + int subtype, int rssi, int noise, u_int32_t rstamp); + +void +ieee80211_wds_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach; +} + +void +ieee80211_wds_detach(struct ieee80211com *ic) +{ +} + +static void +wds_vdetach(struct ieee80211vap *vap) +{ + if (vap->iv_bss != NULL) { + /* XXX locking? */ + if (vap->iv_bss->ni_wdsvap == vap) + vap->iv_bss->ni_wdsvap = NULL; + } +} + +static void +wds_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = wds_newstate; + vap->iv_input = wds_input; + vap->iv_recv_mgmt = wds_recv_mgmt; + vap->iv_opdetach = wds_vdetach; +} + +static int +ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *ni, *obss; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS, + "%s: creating link to %s on channel %u\n", __func__, + ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan)); + + /* NB: vap create must specify the bssid for the link */ + KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid")); + /* NB: we should only be called on RUN transition */ + KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state")); + + if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) { + /* + * Dynamic/non-legacy WDS. Reference the associated + * station specified by the desired bssid setup at vap + * create. Point ni_wdsvap at the WDS vap so 4-address + * frames received through the associated AP vap will + * be dispatched upward (e.g. to a bridge) as though + * they arrived on the WDS vap. + */ + IEEE80211_NODE_LOCK(nt); + obss = NULL; + ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid); + if (ni == NULL) { + /* + * Node went away before we could hookup. This + * should be ok; no traffic will flow and a leave + * event will be dispatched that should cause + * the vap to be destroyed. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS, + "%s: station %s went away\n", + __func__, ether_sprintf(vap->iv_des_bssid)); + /* XXX stat? */ + } else if (ni->ni_wdsvap != NULL) { + /* + * Node already setup with a WDS vap; we cannot + * allow multiple references so disallow. If + * ni_wdsvap points at us that's ok; we should + * do nothing anyway. + */ + /* XXX printf instead? */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS, + "%s: station %s in use with %s\n", + __func__, ether_sprintf(vap->iv_des_bssid), + ni->ni_wdsvap->iv_ifp->if_xname); + /* XXX stat? */ + } else { + /* + * Committed to new node, setup state. + */ + obss = vap->iv_bss; + vap->iv_bss = ni; + ni->ni_wdsvap = vap; + } + IEEE80211_NODE_UNLOCK(nt); + if (obss != NULL) { + /* NB: deferred to avoid recursive lock */ + ieee80211_free_node(obss); + } + } else { + /* + * Legacy WDS vap setup. + */ + /* + * The far end does not associate so we just create + * create a new node and install it as the vap's + * bss node. We must simulate an association and + * authorize the port for traffic to flow. + * XXX check if node already in sta table? + */ + ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan); + if (ni != NULL) { + obss = vap->iv_bss; + vap->iv_bss = ieee80211_ref_node(ni); + ni->ni_flags |= IEEE80211_NODE_AREF; + if (obss != NULL) + ieee80211_free_node(obss); + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, 1); + /* tell the authenticator about new station */ + if (vap->iv_auth->ia_node_join != NULL) + vap->iv_auth->ia_node_join(ni); + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ni); + + ieee80211_notify_node_join(ni, 1 /*newassoc*/); + /* XXX inject l2uf frame */ + } + } + + /* + * Flush pending frames now that were setup. + */ + if (ni != NULL && IEEE80211_NODE_WDSQ_QLEN(ni) != 0) { + int8_t rssi, noise; + + IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni, + "flush wds queue, %u packets queued", + IEEE80211_NODE_WDSQ_QLEN(ni)); + ic->ic_node_getsignal(ni, &rssi, &noise); + for (;;) { + struct mbuf *m; + + IEEE80211_NODE_WDSQ_LOCK(ni); + _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m); + IEEE80211_NODE_WDSQ_UNLOCK(ni); + if (m == NULL) + break; + /* XXX cheat and re-use last rstamp */ + ieee80211_input(ni, m, rssi, noise, ni->ni_rstamp); + } + } + return (ni == NULL ? ENOENT : 0); +} + +/* + * Propagate multicast frames of an ap vap to all DWDS links. + * The caller is assumed to have verified this frame is multicast. + */ +void +ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) +{ + struct ieee80211com *ic = vap0->iv_ic; + struct ifnet *parent = ic->ic_ifp; + const struct ether_header *eh = mtod(m, const struct ether_header *); + struct ieee80211_node *ni; + struct ieee80211vap *vap; + struct ifnet *ifp; + struct mbuf *mcopy; + int err; + + KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost), + ("%s not mcast", ether_sprintf(eh->ether_dhost))); + + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + /* only DWDS vaps are interesting */ + if (vap->iv_opmode != IEEE80211_M_WDS || + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)) + continue; + /* if it came in this interface, don't send it back out */ + ifp = vap->iv_ifp; + if (ifp == m->m_pkthdr.rcvif) + continue; + /* + * Duplicate the frame and send it. We don't need + * to classify or lookup the tx node; this was already + * done by the caller so we can just re-use the info. + */ + mcopy = m_copypacket(m, M_DONTWAIT); + if (mcopy == NULL) { + ifp->if_oerrors++; + /* XXX stat + msg */ + continue; + } + ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + ifp->if_oerrors++; + m_freem(mcopy); + continue; + } + if (ieee80211_classify(ni, mcopy)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS, + eh->ether_dhost, NULL, + "%s", "classification failure"); + vap->iv_stats.is_tx_classify++; + ifp->if_oerrors++; + m_freem(mcopy); + ieee80211_free_node(ni); + continue; + } + mcopy->m_flags |= M_MCAST | M_WDS; + mcopy->m_pkthdr.rcvif = (void *) ni; + + IFQ_HANDOFF(parent, mcopy, err); + if (err) { + /* NB: IFQ_HANDOFF reclaims mbuf */ + ifp->if_oerrors++; + ieee80211_free_node(ni); + } else + ifp->if_opackets++; + } +} + +/* + * Handle DWDS discovery on receipt of a 4-address frame in + * ap mode. Queue the frame and post an event for someone + * to plumb the necessary WDS vap for this station. Frames + * received prior to the vap set running will then be reprocessed + * as if they were just received. + */ +void +ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + int qlen, age; + + IEEE80211_NODE_WDSQ_LOCK(ni); + if (!_IF_QFULL(&ni->ni_wdsq)) { + /* + * 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? */ + /* XXX per/vap beacon interval? */ + /* NB: TU -> secs */ + age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024; + _IEEE80211_NODE_WDSQ_ENQUEUE(ni, m, qlen, age); + IEEE80211_NODE_WDSQ_UNLOCK(ni); + + IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni, + "save frame, %u now queued", qlen); + } else { + vap->iv_stats.is_dwds_qdrop++; + _IF_DROP(&ni->ni_wdsq); + IEEE80211_NODE_WDSQ_UNLOCK(ni); + + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_WDS, + mtod(m, struct ieee80211_frame *), "wds data", + "pending q overflow, drops %d (len %d)", + ni->ni_wdsq.ifq_drops, ni->ni_wdsq.ifq_len); + +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumppkts(vap)) + ieee80211_dump_pkt(ic, mtod(m, caddr_t), + m->m_len, -1, -1); +#endif + /* XXX tail drop? */ + m_freem(m); + } + ieee80211_notify_wds_discover(ni); +} + +/* + * Age frames on the WDS pending 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 + * so we can check and/or adjust only the head of the list. + * If a frame's age exceeds the threshold then discard it. + * The number of frames discarded is returned to the caller. + */ +int +ieee80211_node_wdsq_age(struct ieee80211_node *ni) +{ +#ifdef IEEE80211_DEBUG + struct ieee80211vap *vap = ni->ni_vap; +#endif + struct mbuf *m; + int discard = 0; + + IEEE80211_NODE_WDSQ_LOCK(ni); + while (_IF_POLL(&ni->ni_wdsq, m) != NULL && + M_AGE_GET(m) < IEEE80211_INACT_WAIT) { + IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni, + "discard frame, age %u", M_AGE_GET(m)); + + /* XXX could be optimized */ + _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m); + m_freem(m); + discard++; + } + if (m != NULL) + M_AGE_SUB(m, IEEE80211_INACT_WAIT); + IEEE80211_NODE_WDSQ_UNLOCK(ni); + + IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni, + "discard %u frames for age", discard); +#if 0 + IEEE80211_NODE_STAT_ADD(ni, wds_discard, discard); +#endif + return discard; +} + +/* + * IEEE80211_M_WDS vap state machine handler. + */ +static int +wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + int error; + + IEEE80211_LOCK_ASSERT(ic); + + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); + vap->iv_state = nstate; /* state transition */ + callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */ + if (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(vap); /* background scan */ + ni = vap->iv_bss; /* NB: no reference held */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) + callout_stop(&vap->iv_swbmiss); + error = 0; + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + default: + break; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + } + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_INIT: + ieee80211_check_scan_current(vap); + break; + default: + break; + } + break; + case IEEE80211_S_RUN: + if (ostate == IEEE80211_S_INIT) { + /* + * Already have a channel; bypass the scan + * and startup immediately. + */ + error = ieee80211_create_wds(vap, ic->ic_curchan); + } + break; + default: + break; + } + return error; +} + +/* + * Process a received frame. The node associated with the sender + * should be supplied. If nothing was found in the node table then + * the caller is assumed to supply a reference to iv_bss instead. + * The RSSI and a timestamp are also supplied. The RSSI data is used + * during AP scanning to select a AP to associate with; it can have + * any units so long as values have consistent units and higher values + * mean ``better signal''. The receive timestamp is currently not used + * by the 802.11 layer. + */ +static int +wds_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int noise, uint32_t rstamp) +{ +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct ether_header *eh; + int hdrspace, need_tap; + uint8_t dir, type, subtype, qos; + uint16_t rxseq; + + if (m->m_flags & M_AMPDU) { + /* + * Fastpath for A-MPDU reorder q resubmission. Frames + * w/ M_AMPDU marked have already passed through here + * but were received out of order and been held on the + * reorder queue. When resubmitted they are marked + * with the M_AMPDU flag and we can bypass most of the + * normal processing. + */ + wh = mtod(m, struct ieee80211_frame *); + type = IEEE80211_FC0_TYPE_DATA; + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + subtype = IEEE80211_FC0_SUBTYPE_QOS; + hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ + goto resubmit_ampdu; + } + + KASSERT(ni != NULL, ("null node")); + + need_tap = 1; /* mbuf need to be tapped. */ + type = -1; /* undefined */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_pkthdr.len); + vap->iv_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) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); + vap->iv_stats.is_rx_badversion++; + goto err; + } + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + /* NB: WDS vap's do not scan */ + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, + "too short (3): len %u", m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } + /* NB: the TA is implicitly verified by finding the wds peer node */ + if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) && + !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) { + /* not interested in */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + wh->i_addr1, NULL, "%s", "not to bss"); + vap->iv_stats.is_rx_wrongbss++; + goto out; + } + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = noise; + ni->ni_rstamp = rstamp; + if (HAS_SEQ(type)) { + uint8_t tid = ieee80211_gettid(wh); + if (IEEE80211_QOS_HAS_SEQ(wh) && + TID_TO_WME_AC(tid) >= WME_AC_VI) + ic->ic_wme.wme_hipri_traffic++; + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && + (wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { + /* duplicate, discard */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + wh->i_addr1, "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); + vap->iv_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + goto out; + } + ni->ni_rxseqs[tid] = rxseq; + } + 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(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "data too short: expecting %u", hdrspace); + vap->iv_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + if (dir != IEEE80211_FC1_DIR_DSTODS) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + /* + * Only legacy WDS traffic should take this path. + */ + if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "not legacy wds"); + vap->iv_stats.is_rx_wrongdir++;/*XXX*/ + goto out; + } + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + ni->ni_inact = ni->ni_inact_reload; + /* + * Handle A-MPDU re-ordering. The station must be + * associated and negotiated HT. The frame must be + * a QoS frame (not QoS null data) and not previously + * processed for A-MPDU re-ordering. If the frame is + * to be processed directly then ieee80211_ampdu_reorder + * will return 0; otherwise it has consumed the mbuf + * and we should do nothing more with it. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + subtype == IEEE80211_FC0_SUBTYPE_QOS && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + + /* + * 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 ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "WEP", "%s", "PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + IEEE80211_NODE_STAT(ni, rx_noprivacy); + goto out; + } + key = ieee80211_crypto_decap(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 { + /* XXX M_WEP and IEEE80211_F_PRIVACY */ + key = NULL; + } + + /* + * Save QoS bits for use below--before we strip the header. + */ + if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { + qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? + ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : + ((struct ieee80211_qosframe *)wh)->i_qos[0]; + } else + qos = 0; + + /* + * Next up, any fragmentation. + */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + m = ieee80211_defrag(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(vap, key, m, 0)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "demic error"); + vap->iv_stats.is_rx_demicfail++; + IEEE80211_NODE_STAT(ni, rx_demicfail); + goto out; + } + + /* copy to listener after decrypt */ + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + need_tap = 0; + + /* + * Finally, strip the 802.11 header. + */ + m = ieee80211_decap(vap, m, hdrspace); + if (m == NULL) { + /* XXX mask bit to check for both */ + /* don't count Null data frames as errors */ + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || + subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) + goto out; + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "decap error"); + vap->iv_stats.is_rx_decap++; + IEEE80211_NODE_STAT(ni, rx_decap); + goto err; + } + 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(vap, IEEE80211_MSG_INPUT, + eh->ether_shost, "data", + "unauthorized port: ether type 0x%x len %u", + eh->ether_type, m->m_pkthdr.len); + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && + (key == NULL && (m->m_flags & M_WEP) == 0) && + eh->ether_type != htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + vap->iv_stats.is_rx_unencrypted++; + IEEE80211_NODE_STAT(ni, rx_unencrypted); + goto out; + } + } + /* XXX require HT? */ + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && +#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) + m->m_pkthdr.len >= 3*FF_LLC_SIZE) { + struct llc *llc; + + /* + * Check for fast-frame tunnel encapsulation. + */ + if (m->m_len < FF_LLC_SIZE && + (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "fast-frame", + "%s", "m_pullup(llc) failed"); + vap->iv_stats.is_rx_tooshort++; + return IEEE80211_FC0_TYPE_DATA; + } + llc = (struct llc *)(mtod(m, uint8_t *) + + sizeof(struct ether_header)); + if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { + m_adj(m, FF_LLC_SIZE); + m = ieee80211_decap_fastframe(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + } +#undef FF_LLC_SIZE + ieee80211_deliver_data(vap, ni, m); + return IEEE80211_FC0_TYPE_DATA; + + case IEEE80211_FC0_TYPE_MGT: + vap->iv_stats.is_rx_mgmt++; + IEEE80211_NODE_STAT(ni, rx_mgmt); + if (dir != IEEE80211_FC1_DIR_NODS) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto err; + } + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "mgt", "too short: len %u", + m->m_pkthdr.len); + vap->iv_stats.is_rx_tooshort++; + goto out; + } +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) { + if_printf(ifp, "received %s from %s rssi %d\n", + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(wh->i_addr2), rssi); + } +#endif + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "WEP set but not permitted"); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); + m_freem(m); + return IEEE80211_FC0_TYPE_MGT; + + case IEEE80211_FC0_TYPE_CTL: + vap->iv_stats.is_rx_ctl++; + IEEE80211_NODE_STAT(ni, rx_ctrl); + goto out; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "bad", "frame type 0x%x", type); + /* should not come here */ + break; + } +err: + ifp->if_ierrors++; +out: + if (m != NULL) { + if (bpf_peers_present(vap->iv_rawbpf) && need_tap) + bpf_mtap(vap->iv_rawbpf, m); + m_freem(m); + } + return type; +#undef SEQ_LEQ +} + +static void +wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, + int subtype, int rssi, int noise, u_int32_t rstamp) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + u_int8_t *frm, *efrm; + + wh = mtod(m0, struct ieee80211_frame *); + frm = (u_int8_t *)&wh[1]; + efrm = mtod(m0, u_int8_t *) + m0->m_len; + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_DISASSOC: + vap->iv_stats.is_rx_mgtdiscard++; + break; + case IEEE80211_FC0_SUBTYPE_ACTION: + if (vap->iv_state != IEEE80211_S_RUN || + IEEE80211_IS_MULTICAST(wh->i_addr1)) { + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + ni->ni_inact = ni->ni_inact_reload; + if (ieee80211_parse_action(ni, m0) == 0) + ic->ic_recv_action(ni, frm, efrm); + break; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); + vap->iv_stats.is_rx_badsubtype++; + break; + } +} diff --git a/sys/net80211/ieee80211_wds.h b/sys/net80211/ieee80211_wds.h new file mode 100644 index 0000000..c34fb6e --- /dev/null +++ b/sys/net80211/ieee80211_wds.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_WDS_H_ +#define _NET80211_IEEE80211_WDS_H_ + +/* + * WDS implementation definitions. + */ +void ieee80211_wds_attach(struct ieee80211com *); +void ieee80211_wds_detach(struct ieee80211com *); + +void ieee80211_dwds_mcast(struct ieee80211vap *, struct mbuf *); +void ieee80211_dwds_discover(struct ieee80211_node *, struct mbuf *); +int ieee80211_node_wdsq_age(struct ieee80211_node *); +#endif /* !_NET80211_IEEE80211_WDS_H_ */ diff --git a/sys/net80211/ieee80211_xauth.c b/sys/net80211/ieee80211_xauth.c index c829b6e..2341ffb 100644 --- a/sys/net80211/ieee80211_xauth.c +++ b/sys/net80211/ieee80211_xauth.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2004 Video54 Technologies, Inc. - * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$"); * of the available callbacks--the user mode authenticator process works * entirely from messages about stations joining and leaving. */ +#include "opt_wlan.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -54,6 +56,9 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> +/* XXX number of references from net80211 layer; needed for module code */ +static int nrefs = 0; + /* * One module handles everything for now. May want * to split things up for embedded applications. @@ -66,30 +71,6 @@ static const struct ieee80211_authenticator xauth = { .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); +IEEE80211_AUTH_MODULE(xauth, 1); +IEEE80211_AUTH_ALG(x8021x, IEEE80211_AUTH_8021X, xauth); +IEEE80211_AUTH_ALG(wpa, IEEE80211_AUTH_WPA, xauth); |