From 2843bf259ec9ca475410e4e9e53e7cd47cd55333 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 8 Dec 2004 17:26:47 +0000 Subject: Update 802.11 support; too much new functionality to fully describe here but it includes completed 802.11g, WPA, 802.11i, 802.1x, WME/WMM, AP-side power-save, crypto plugin framework, authenticator plugin framework, and access control plugin frameowrk. --- sys/net80211/_ieee80211.h | 188 +++ sys/net80211/ieee80211.c | 459 ++++--- sys/net80211/ieee80211.h | 414 +++--- sys/net80211/ieee80211_acl.c | 301 +++++ sys/net80211/ieee80211_crypto.c | 701 ++++++---- sys/net80211/ieee80211_crypto.h | 184 ++- sys/net80211/ieee80211_crypto_ccmp.c | 603 +++++++++ sys/net80211/ieee80211_crypto_none.c | 149 +++ sys/net80211/ieee80211_crypto_tkip.c | 987 ++++++++++++++ sys/net80211/ieee80211_crypto_wep.c | 499 +++++++ sys/net80211/ieee80211_freebsd.c | 338 +++++ sys/net80211/ieee80211_freebsd.h | 223 ++++ sys/net80211/ieee80211_input.c | 2403 +++++++++++++++++++++++++++------- sys/net80211/ieee80211_ioctl.c | 2111 +++++++++++++++++++++++------ sys/net80211/ieee80211_ioctl.h | 330 ++++- sys/net80211/ieee80211_node.c | 1746 ++++++++++++++++++++---- sys/net80211/ieee80211_node.h | 255 +++- sys/net80211/ieee80211_output.c | 1326 ++++++++++++++++--- sys/net80211/ieee80211_proto.c | 759 +++++++++-- sys/net80211/ieee80211_proto.h | 192 ++- sys/net80211/ieee80211_radiotap.h | 4 +- sys/net80211/ieee80211_var.h | 383 +++--- sys/net80211/ieee80211_xauth.c | 100 ++ 23 files changed, 12428 insertions(+), 2227 deletions(-) create mode 100644 sys/net80211/_ieee80211.h create mode 100644 sys/net80211/ieee80211_acl.c create mode 100644 sys/net80211/ieee80211_crypto_ccmp.c create mode 100644 sys/net80211/ieee80211_crypto_none.c create mode 100644 sys/net80211/ieee80211_crypto_tkip.c create mode 100644 sys/net80211/ieee80211_crypto_wep.c create mode 100644 sys/net80211/ieee80211_freebsd.c create mode 100644 sys/net80211/ieee80211_freebsd.h create mode 100644 sys/net80211/ieee80211_xauth.c (limited to 'sys') diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h new file mode 100644 index 0000000..d0ed282 --- /dev/null +++ b/sys/net80211/_ieee80211.h @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211__IEEE80211_H_ +#define _NET80211__IEEE80211_H_ + +enum ieee80211_phytype { + IEEE80211_T_DS, /* direct sequence spread spectrum */ + IEEE80211_T_FH, /* frequency hopping */ + IEEE80211_T_OFDM, /* frequency division multiplexing */ + IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ +}; +#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ + +/* XXX not really a mode; there are really multiple PHY's */ +enum ieee80211_phymode { + IEEE80211_MODE_AUTO = 0, /* autoselect */ + IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ + IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ + IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ + IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ + IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */ + IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */ +}; +#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO_G+1) + +enum ieee80211_opmode { + IEEE80211_M_STA = 1, /* infrastructure station */ + IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ + IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ + IEEE80211_M_HOSTAP = 6, /* Software Access Point */ + IEEE80211_M_MONITOR = 8 /* Monitor mode */ +}; + +/* + * 802.11g protection mode. + */ +enum ieee80211_protmode { + IEEE80211_PROT_NONE = 0, /* no protection */ + IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ + IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +/* + * Authentication mode. + */ +enum ieee80211_authmode { + IEEE80211_AUTH_NONE = 0, + IEEE80211_AUTH_OPEN = 1, /* open */ + IEEE80211_AUTH_SHARED = 2, /* shared-key */ + IEEE80211_AUTH_8021X = 3, /* 802.1x */ + IEEE80211_AUTH_AUTO = 4, /* auto-select/accept */ + /* NB: these are used only for ioctls */ + IEEE80211_AUTH_WPA = 5, /* WPA/RSN w/ 802.1x/PSK */ +}; + +/* + * Roaming mode is effectively who controls the operation + * of the 802.11 state machine when operating as a station. + * State transitions are controlled either by the driver + * (typically when management frames are processed by the + * hardware/firmware), the host (auto/normal operation of + * the 802.11 layer), or explicitly through ioctl requests + * when applications like wpa_supplicant want control. + */ +enum ieee80211_roamingmode { + IEEE80211_ROAMING_DEVICE= 0, /* driver/hardware control */ + IEEE80211_ROAMING_AUTO = 1, /* 802.11 layer control */ + IEEE80211_ROAMING_MANUAL= 2, /* application control */ +}; + +/* + * Channels are specified by frequency and attributes. + */ +struct ieee80211_channel { + u_int16_t ic_freq; /* setting in Mhz */ + u_int16_t ic_flags; /* see below */ +}; + +#define IEEE80211_CHAN_MAX 255 +#define IEEE80211_CHAN_BYTES 32 /* howmany(IEEE80211_CHAN_MAX, NBBY) */ +#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ +#define IEEE80211_CHAN_ANYC \ + ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) + +/* bits 0-3 are for private use by drivers */ +/* channel attributes */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* + * Useful combinations of channel characteristics. + */ +#define IEEE80211_CHAN_FHSS \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) +#define IEEE80211_CHAN_A \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_B \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) +#define IEEE80211_CHAN_PUREG \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) +#define IEEE80211_CHAN_T \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) +#define IEEE80211_CHAN_108G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) + +#define IEEE80211_IS_CHAN_FHSS(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) +#define IEEE80211_IS_CHAN_A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) +#define IEEE80211_IS_CHAN_B(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) +#define IEEE80211_IS_CHAN_PUREG(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) +#define IEEE80211_IS_CHAN_G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) +#define IEEE80211_IS_CHAN_T(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) +#define IEEE80211_IS_CHAN_108G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) + +#define IEEE80211_IS_CHAN_2GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) +#define IEEE80211_IS_CHAN_5GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) +#define IEEE80211_IS_CHAN_OFDM(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) +#define IEEE80211_IS_CHAN_CCK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) +#define IEEE80211_IS_CHAN_GFSK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) + +/* ni_chan encoding for FH phy */ +#define IEEE80211_FH_CHANMOD 80 +#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) +#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) +#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) + +/* + * 802.11 rate set. + */ +#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ +#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ + +struct ieee80211_rateset { + u_int8_t rs_nrates; + u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; +}; + +#endif /* _NET80211__IEEE80211_H_ */ diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 7bbeedc..d7e0122 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,70 +37,84 @@ __FBSDID("$FreeBSD$"); * IEEE 802.11 generic handler */ -#include "opt_inet.h" - #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include + #include -#include #include -#include #include -#include -#include #include #include -#ifdef INET -#include -#include -#endif - -#ifdef IEEE80211_DEBUG -int ieee80211_debug = 0; -SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug, - 0, "IEEE 802.11 media debugging printfs"); -#endif - -static void ieee80211_set11gbasicrates(struct ieee80211_rateset *, - enum ieee80211_phymode); - static const char *ieee80211_phymode_name[] = { "auto", /* IEEE80211_MODE_AUTO */ "11a", /* IEEE80211_MODE_11A */ "11b", /* IEEE80211_MODE_11B */ "11g", /* IEEE80211_MODE_11G */ "FH", /* IEEE80211_MODE_FH */ - "turbo", /* IEEE80211_MODE_TURBO */ + "turboA", /* IEEE80211_MODE_TURBO_A */ + "turboG", /* IEEE80211_MODE_TURBO_G */ }; +/* list of all instances */ +SLIST_HEAD(ieee80211_list, ieee80211com); +static struct ieee80211_list ieee80211_list = + SLIST_HEAD_INITIALIZER(ieee80211_list); +static u_int8_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; + u_int8_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); +} + void -ieee80211_ifattach(struct ifnet *ifp) +ieee80211_ifattach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_channel *c; int i; ether_ifattach(ifp, ic->ic_myaddr); bpfattach2(ifp, DLT_IEEE802_11, sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); - ieee80211_crypto_attach(ifp); + + ieee80211_crypto_attach(ic); /* * Fill in 802.11 available channel set, mark @@ -135,7 +149,9 @@ ieee80211_ifattach(struct ifnet *ifp) if (IEEE80211_IS_CHAN_FHSS(c)) ic->ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_curmode */ @@ -143,25 +159,45 @@ ieee80211_ifattach(struct ifnet *ifp) ic->ic_curmode = IEEE80211_MODE_AUTO; ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + /* + * Enable WME by default if we're capable. + */ + if (ic->ic_caps & IEEE80211_C_WME) + ic->ic_flags |= IEEE80211_F_WME; + (void) ieee80211_setmode(ic, ic->ic_curmode); if (ic->ic_lintval == 0) - ic->ic_lintval = 100; /* default sleep */ + ic->ic_lintval = IEEE80211_BINTVAL_DEFAULT; ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ + ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; + IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); + + ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; - ieee80211_node_attach(ifp); - ieee80211_proto_attach(ifp); + ieee80211_node_attach(ic); + ieee80211_proto_attach(ic); + + ieee80211_add_vap(ic); + + ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ } void -ieee80211_ifdetach(struct ifnet *ifp) +ieee80211_ifdetach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; - ieee80211_proto_detach(ifp); - ieee80211_crypto_detach(ifp); - ieee80211_node_detach(ifp); + ieee80211_remove_vap(ic); + + ieee80211_sysctl_detach(ic); + ieee80211_proto_detach(ic); + ieee80211_crypto_detach(ic); + ieee80211_node_detach(ic); ifmedia_removeall(&ic->ic_media); + + IEEE80211_BEACON_LOCK_DESTROY(ic); + bpfdetach(ifp); ether_ifdetach(ifp); } @@ -203,11 +239,11 @@ ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) else if (c == IEEE80211_CHAN_ANYC) return IEEE80211_CHAN_ANY; else if (c != NULL) { - if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n", + if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n", c->ic_freq, c->ic_flags); return 0; /* XXX */ } else { - if_printf(&ic->ic_if, "invalid channel (NULL)\n"); + if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); return 0; /* XXX */ } } @@ -244,13 +280,13 @@ ieee80211_ieee2mhz(u_int chan, u_int flags) * ieee80211_attach and before most anything else. */ void -ieee80211_media_init(struct ifnet *ifp, +ieee80211_media_init(struct ieee80211com *ic, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { #define ADD(_ic, _s, _o) \ ifmedia_add(&(_ic)->ic_media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; struct ifmediareq imr; int i, j, mode, rate, maxrate, mword, mopt, r; struct ieee80211_rateset *rs; @@ -260,7 +296,7 @@ ieee80211_media_init(struct ifnet *ifp, * Do late attach work that must wait for any subclass * (i.e. driver) work such as overriding methods. */ - ieee80211_node_lateattach(ifp); + ieee80211_node_lateattach(ic); /* * Fill in media characteristics. @@ -276,6 +312,7 @@ ieee80211_media_init(struct ifnet *ifp, IFM_IEEE80211_11G, IFM_IEEE80211_FH, IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, }; if ((ic->ic_modecaps & (1<ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; - printf("%s%d%sMbps", (i != 0 ? " " : ""), - (rate & IEEE80211_RATE_VAL) / 2, - ((rate & 0x1) != 0 ? ".5" : "")); ADD(ic, mword, mopt); if (ic->ic_caps & IEEE80211_C_IBSS) ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); @@ -326,7 +359,6 @@ ieee80211_media_init(struct ifnet *ifp, if (rate > maxrate) maxrate = rate; } - printf("\n"); } for (i = 0; i < allrates.rs_nrates; i++) { mword = ieee80211_rate2media(ic, allrates.rs_rates[i], @@ -352,6 +384,31 @@ ieee80211_media_init(struct ifnet *ifp, #undef ADD } +void +ieee80211_announce(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + int i, mode, rate, mword; + struct ieee80211_rateset *rs; + + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { + if ((ic->ic_modecaps & (1<ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + mword = ieee80211_rate2media(ic, rate, mode); + if (mword == 0) + continue; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + (rate & IEEE80211_RATE_VAL) / 2, + ((rate & 0x1) != 0 ? ".5" : "")); + } + printf("\n"); + } +} + static int findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) { @@ -366,17 +423,50 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) } /* + * Find an instance by it's mac address. + */ +struct ieee80211com * +ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic; + + /* XXX lock */ + SLIST_FOREACH(ic, &ieee80211_list, ic_next) + if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) + return ic; + return NULL; +} + +static struct ieee80211com * +ieee80211_find_instance(struct ifnet *ifp) +{ + struct ieee80211com *ic; + + /* XXX lock */ + /* XXX not right for multiple instances but works for now */ + SLIST_FOREACH(ic, &ieee80211_list, ic_next) + if (ic->ic_ifp == ifp) + return ic; + return NULL; +} + +/* * Handle a media change request. */ int ieee80211_media_change(struct ifnet *ifp) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211com *ic; struct ifmedia_entry *ime; enum ieee80211_opmode newopmode; enum ieee80211_phymode newphymode; int i, j, newrate, error = 0; + ic = ieee80211_find_instance(ifp); + if (!ic) { + if_printf(ifp, "%s: no 802.11 instance!\n", __func__); + return EINVAL; + } ime = ic->ic_media.ifm_cur; /* * First, identify the phy mode. @@ -401,13 +491,16 @@ ieee80211_media_change(struct ifnet *ifp) return EINVAL; } /* - * Turbo mode is an ``option''. Eventually it - * needs to be applied to 11g too. + * Turbo mode is an ``option''. + * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { - if (newphymode != IEEE80211_MODE_11A) + if (newphymode == IEEE80211_MODE_11A) + newphymode = IEEE80211_MODE_TURBO_A; + else if (newphymode == IEEE80211_MODE_11G) + newphymode = IEEE80211_MODE_TURBO_G; + else return EINVAL; - newphymode = IEEE80211_MODE_TURBO; } /* * Validate requested mode is available. @@ -514,14 +607,15 @@ ieee80211_media_change(struct ifnet *ifp) break; case IEEE80211_M_IBSS: ic->ic_flags |= IEEE80211_F_IBSSON; -#ifdef notdef - if (ic->ic_curmode == IEEE80211_MODE_11G) - ieee80211_set11gbasicrates( - &ic->ic_suprates[newphymode], - IEEE80211_MODE_11B); -#endif break; } + /* + * Yech, slot time may change depending on the + * operating mode so reset it to be sure everything + * is setup appropriately. + */ + ieee80211_reset_erp(ic); + ieee80211_wme_initparams(ic); /* after opmode change */ error = ENETRESET; } #ifdef notdef @@ -534,24 +628,39 @@ ieee80211_media_change(struct ifnet *ifp) void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni = NULL; - int old_status = imr->ifm_status; + struct ieee80211com *ic; + struct ieee80211_rateset *rs; + ic = ieee80211_find_instance(ifp); + if (!ic) { + if_printf(ifp, "%s: no 802.11 instance!\n", __func__); + return; + } imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; - if (ic->ic_state == IEEE80211_S_RUN) { + if (ic->ic_state == IEEE80211_S_RUN) imr->ifm_status |= IFM_ACTIVE; - ifp->if_link_state = LINK_STATE_UP; + /* + * Calculate a current rate if possible. + */ + if (ic->ic_fixed_rate != -1) { + /* + * A fixed rate is set, report that. + */ + rs = &ic->ic_sup_rates[ic->ic_curmode]; + imr->ifm_active |= ieee80211_rate2media(ic, + rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); + } else if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * In station mode report the current transmit rate. + */ + rs = &ic->ic_bss->ni_rates; + imr->ifm_active |= ieee80211_rate2media(ic, + rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); } else - ifp->if_link_state = LINK_STATE_DOWN; - imr->ifm_active |= IFM_AUTO; + imr->ifm_active |= IFM_AUTO; switch (ic->ic_opmode) { case IEEE80211_M_STA: - ni = ic->ic_bss; - /* calculate rate subtype */ - imr->ifm_active |= ieee80211_rate2media(ic, - ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); break; case IEEE80211_M_IBSS: imr->ifm_active |= IFM_IEEE80211_ADHOC; @@ -579,58 +688,41 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_MODE_FH: imr->ifm_active |= IFM_IEEE80211_FH; break; - case IEEE80211_MODE_TURBO: + case IEEE80211_MODE_TURBO_A: imr->ifm_active |= IFM_IEEE80211_11A | IFM_IEEE80211_TURBO; break; + case IEEE80211_MODE_TURBO_G: + imr->ifm_active |= IFM_IEEE80211_11G + | IFM_IEEE80211_TURBO; + break; } - - /* Notify that the link state has changed. */ - if (imr->ifm_status != old_status) - rt_ifmsg(ifp); } void -ieee80211_watchdog(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - - if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) - ieee80211_timeout_nodes(ic); - - if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) - ifp->if_timer = 1; -} - -/* - * Mark the basic rates for the 11g rate table based on the - * operating mode. For real 11g we mark all the 11b rates - * and 6, 12, and 24 OFDM. For 11b compatibility we mark only - * 11b rates. There's also a pseudo 11a-mode used to mark only - * the basic OFDM rates. - */ -static void -ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) +ieee80211_watchdog(struct ieee80211com *ic) { - static const struct ieee80211_rateset basic[] = { - { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ - { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */ - { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */ - { 0 }, /* IEEE80211_MODE_FH */ - { 0 }, /* IEEE80211_MODE_TURBO */ - }; - int i, j; - - for (i = 0; i < rs->rs_nrates; i++) { - rs->rs_rates[i] &= IEEE80211_RATE_VAL; - for (j = 0; j < basic[mode].rs_nrates; j++) - if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { - rs->rs_rates[i] |= IEEE80211_RATE_BASIC; - break; - } + struct ieee80211_node_table *nt; + int need_inact_timer = 0; + + if (ic->ic_state != IEEE80211_S_INIT) { + if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + nt = &ic->ic_scan; + if (nt->nt_inact_timer) { + if (--nt->nt_inact_timer == 0) + nt->nt_timeout(nt); + need_inact_timer += nt->nt_inact_timer; + } + nt = ic->ic_sta; + if (nt != NULL && nt->nt_inact_timer) { + if (--nt->nt_inact_timer == 0) + nt->nt_timeout(nt); + need_inact_timer += nt->nt_inact_timer; + } } + if (ic->ic_mgt_timer != 0 || need_inact_timer) + ic->ic_ifp->if_timer = 1; } /* @@ -649,7 +741,8 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ - IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ + IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ + IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ }; struct ieee80211_channel *c; u_int modeflags; @@ -657,8 +750,9 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) /* validate new mode */ if ((ic->ic_modecaps & (1<ic_modecaps)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: mode %u not supported (caps 0x%x)\n", + __func__, mode, ic->ic_modecaps); return EINVAL; } @@ -680,8 +774,8 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) } } if (i > IEEE80211_CHAN_MAX) { - IEEE80211_DPRINTF(("%s: no channels found for mode %u\n", - __func__, mode)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: no channels found for mode %u\n", __func__, mode); return EINVAL; } @@ -713,56 +807,82 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) ic->ic_ibss_chan = &ic->ic_channels[i]; break; } + KASSERT(ic->ic_ibss_chan != NULL && + isset(ic->ic_chan_active, + ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), + ("Bad IBSS channel %u", + ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); } + /* + * If the desired channel is set but no longer valid then reset it. + */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* - * Set/reset state flags that influence beacon contents, etc. - * - * XXX what if we have stations already associated??? - * XXX probably not right for autoselect? + * Do mode-specific rate setup. */ - if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) - ic->ic_flags |= IEEE80211_F_SHPREAMBLE; if (mode == IEEE80211_MODE_11G) { - if (ic->ic_caps & IEEE80211_C_SHSLOT) - ic->ic_flags |= IEEE80211_F_SHSLOT; + /* + * Use a mixed 11b/11g rate set. + */ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], IEEE80211_MODE_11G); - } else { - ic->ic_flags &= ~IEEE80211_F_SHSLOT; + } else if (mode == IEEE80211_MODE_11B) { + /* + * Force pure 11b rate set. + */ + ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], + IEEE80211_MODE_11B); } + /* + * Setup an initial rate set according to the + * current/default channel selected above. This + * will be changed when scanning but must exist + * now so driver have a consistent state of ic_ibss_chan. + */ + if (ic->ic_bss) /* NB: can be called before lateattach */ + ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; ic->ic_curmode = mode; + ieee80211_reset_erp(ic); /* reset ERP state */ + ieee80211_wme_initparams(ic); /* reset WME stat */ + return 0; #undef N } /* * Return the phy mode for with the specified channel so the - * caller can select a rate set. This is problematic and the - * work here assumes how things work elsewhere in this code. + * caller can select a rate set. This is problematic for channels + * where multiple operating modes are possible (e.g. 11g+11b). + * In those cases we defer to the current operating mode when set. */ enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) { - /* - * NB: this assumes the channel would not be supplied to us - * unless it was already compatible with the current mode. - */ - if (ic->ic_curmode != IEEE80211_MODE_AUTO) - return ic->ic_curmode; - /* - * In autoselect mode; deduce a mode based on the channel - * characteristics. We assume that turbo-only channels - * are not considered when the channel set is constructed. - */ - if (IEEE80211_IS_CHAN_5GHZ(chan)) + if (IEEE80211_IS_CHAN_5GHZ(chan)) { + /* + * This assumes all 11a turbo channels are also + * usable withut turbo, which is currently true. + */ + if (ic->ic_curmode == IEEE80211_MODE_TURBO_A) + return IEEE80211_MODE_TURBO_A; return IEEE80211_MODE_11A; - else if (IEEE80211_IS_CHAN_FHSS(chan)) + } else if (IEEE80211_IS_CHAN_FHSS(chan)) return IEEE80211_MODE_FH; - else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) + else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { + /* + * This assumes all 11g channels are also usable + * for 11b, which is currently true. + */ + if (ic->ic_curmode == IEEE80211_MODE_TURBO_G) + return IEEE80211_MODE_TURBO_G; + if (ic->ic_curmode == IEEE80211_MODE_11B) + return IEEE80211_MODE_11B; return IEEE80211_MODE_11G; - else + } else return IEEE80211_MODE_11B; } @@ -812,7 +932,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m mask = rate & IEEE80211_RATE_VAL; switch (mode) { case IEEE80211_MODE_11A: - case IEEE80211_MODE_TURBO: + case IEEE80211_MODE_TURBO_A: mask |= IFM_IEEE80211_11A; break; case IEEE80211_MODE_11B: @@ -830,6 +950,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: + case IEEE80211_MODE_TURBO_G: mask |= IFM_IEEE80211_11G; break; } @@ -869,33 +990,3 @@ ieee80211_media2rate(int mword) ieeerates[IFM_SUBTYPE(mword)] : 0; #undef N } - -/* - * Module glue. - * - * NB: the module name is "wlan" for compatibility with NetBSD. - */ - -static int -ieee80211_modevent(module_t mod, int type, void *unused) -{ - switch (type) { - case MOD_LOAD: - if (bootverbose) - printf("wlan: <802.11 Link Layer>\n"); - return 0; - case MOD_UNLOAD: - return 0; - } - return EINVAL; -} - -static moduledata_t ieee80211_mod = { - "wlan", - ieee80211_modevent, - 0 -}; -DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); -MODULE_VERSION(wlan, 1); -MODULE_DEPEND(wlan, rc4, 1, 1, 1); -MODULE_DEPEND(wlan, ether, 1, 1, 1); diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index f21c99a..420534e 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -1,6 +1,7 @@ +/* $NetBSD: ieee80211.h,v 1.4 2003/10/15 11:43:51 dyoung Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,7 +50,7 @@ struct ieee80211_plcp_hdr { u_int8_t i_service; u_int16_t i_length; u_int16_t i_crc; -} __attribute__((__packed__)); +} __packed; #define IEEE80211_PLCP_SFD 0xF3A0 #define IEEE80211_PLCP_SERVICE 0x00 @@ -66,7 +67,7 @@ struct ieee80211_frame { u_int8_t i_seq[2]; /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ /* see below */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_qosframe { u_int8_t i_fc[2]; @@ -78,7 +79,7 @@ struct ieee80211_qosframe { u_int8_t i_qos[2]; /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ /* see below */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_qoscntl { u_int8_t i_qos[2]; @@ -92,7 +93,7 @@ struct ieee80211_frame_addr4 { u_int8_t i_addr3[IEEE80211_ADDR_LEN]; u_int8_t i_seq[2]; u_int8_t i_addr4[IEEE80211_ADDR_LEN]; -} __attribute__((__packed__)); +} __packed; struct ieee80211_qosframe_addr4 { @@ -104,48 +105,7 @@ struct ieee80211_qosframe_addr4 { u_int8_t i_seq[2]; u_int8_t i_addr4[IEEE80211_ADDR_LEN]; u_int8_t i_qos[2]; -} __attribute__((__packed__)); - -/* - * Management Notification Frame - */ -struct ieee80211_mnf { - u_int8_t mnf_category; - u_int8_t mnf_action; - u_int8_t mnf_dialog; - u_int8_t mnf_status; -} __attribute__((__packed__)); -#define MNF_SETUP_REQ 0 -#define MNF_SETUP_RESP 1 -#define MNF_TEARDOWN 2 - -/* - * WME/802.11e Tspec Element - */ -struct ieee80211_wme_tspec { - u_int8_t ts_id; - u_int8_t ts_len; - u_int8_t ts_oui[3]; - u_int8_t ts_oui_type; - u_int8_t ts_oui_subtype; - u_int8_t ts_version; - u_int8_t ts_tsinfo[3]; - u_int8_t ts_nom_msdu[2]; - u_int8_t ts_max_msdu[2]; - u_int8_t ts_min_svc[4]; - u_int8_t ts_max_svc[4]; - u_int8_t ts_inactv_intv[4]; - u_int8_t ts_susp_intv[4]; - u_int8_t ts_start_svc[4]; - u_int8_t ts_min_rate[4]; - u_int8_t ts_mean_rate[4]; - u_int8_t ts_max_burst[4]; - u_int8_t ts_min_phy[4]; - u_int8_t ts_peak_rate[4]; - u_int8_t ts_delay[4]; - u_int8_t ts_surplus[2]; - u_int8_t ts_medium_time[2]; -} __attribute__((__packed__)); +} __packed; #define IEEE80211_FC0_VERSION_MASK 0x03 #define IEEE80211_FC0_VERSION_SHIFT 0 @@ -187,6 +147,7 @@ struct ieee80211_wme_tspec { #define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60 #define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70 #define IEEE80211_FC0_SUBTYPE_QOS 0x80 +#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0 #define IEEE80211_FC1_DIR_MASK 0x03 #define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ @@ -210,9 +171,121 @@ struct ieee80211_wme_tspec { #define IEEE80211_QOS_TXOP 0x00ff /* bit 8 is reserved */ -#define IEEE80211_QOS_ACKPOLICY 0x0600 -#define IEEE80211_QOS_ESOP 0x0800 -#define IEEE80211_QOS_TID 0xf000 +#define IEEE80211_QOS_ACKPOLICY 0x60 +#define IEEE80211_QOS_ACKPOLICY_S 5 +#define IEEE80211_QOS_ESOP 0x10 +#define IEEE80211_QOS_ESOP_S 4 +#define IEEE80211_QOS_TID 0x0f + +/* does frame have QoS sequence control data */ +#define IEEE80211_QOS_HAS_SEQ(wh) \ + (((wh)->i_fc[0] & \ + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + +/* + * WME/802.11e information element. + */ +struct ieee80211_wme_info { + u_int8_t wme_id; /* IEEE80211_ELEMID_VENDOR */ + u_int8_t wme_len; /* length in bytes */ + u_int8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */ + u_int8_t wme_type; /* OUI type */ + u_int8_t wme_subtype; /* OUI subtype */ + u_int8_t wme_version; /* spec revision */ + u_int8_t wme_info; /* QoS info */ +} __packed; + +/* + * WME/802.11e Tspec Element + */ +struct ieee80211_wme_tspec { + u_int8_t ts_id; + u_int8_t ts_len; + u_int8_t ts_oui[3]; + u_int8_t ts_oui_type; + u_int8_t ts_oui_subtype; + u_int8_t ts_version; + u_int8_t ts_tsinfo[3]; + u_int8_t ts_nom_msdu[2]; + u_int8_t ts_max_msdu[2]; + u_int8_t ts_min_svc[4]; + u_int8_t ts_max_svc[4]; + u_int8_t ts_inactv_intv[4]; + u_int8_t ts_susp_intv[4]; + u_int8_t ts_start_svc[4]; + u_int8_t ts_min_rate[4]; + u_int8_t ts_mean_rate[4]; + u_int8_t ts_max_burst[4]; + u_int8_t ts_min_phy[4]; + u_int8_t ts_peak_rate[4]; + u_int8_t ts_delay[4]; + u_int8_t ts_surplus[2]; + u_int8_t ts_medium_time[2]; +} __packed; + +/* + * WME AC parameter field + */ +struct ieee80211_wme_acparams { + u_int8_t acp_aci_aifsn; + u_int8_t acp_logcwminmax; + u_int16_t acp_txop; +} __packed; + +#define WME_NUM_AC 4 /* 4 AC categories */ + +#define WME_PARAM_ACI 0x60 /* Mask for ACI field */ +#define WME_PARAM_ACI_S 5 /* Shift for ACI field */ +#define WME_PARAM_ACM 0x10 /* Mask for ACM bit */ +#define WME_PARAM_ACM_S 4 /* Shift for ACM bit */ +#define WME_PARAM_AIFSN 0x0f /* Mask for aifsn field */ +#define WME_PARAM_AIFSN_S 0 /* Shift for aifsn field */ +#define WME_PARAM_LOGCWMIN 0x0f /* Mask for CwMin field (in log) */ +#define WME_PARAM_LOGCWMIN_S 0 /* Shift for CwMin field */ +#define WME_PARAM_LOGCWMAX 0xf0 /* Mask for CwMax field (in log) */ +#define WME_PARAM_LOGCWMAX_S 4 /* Shift for CwMax field */ + +#define WME_AC_TO_TID(_ac) ( \ + ((_ac) == WME_AC_VO) ? 6 : \ + ((_ac) == WME_AC_VI) ? 5 : \ + ((_ac) == WME_AC_BK) ? 1 : \ + 0) + +#define TID_TO_WME_AC(_tid) ( \ + ((_tid) < 1) ? WME_AC_BE : \ + ((_tid) < 3) ? WME_AC_BK : \ + ((_tid) < 6) ? WME_AC_VI : \ + WME_AC_VO) + +/* + * WME Parameter Element + */ +struct ieee80211_wme_param { + u_int8_t param_id; + u_int8_t param_len; + u_int8_t param_oui[3]; + u_int8_t param_oui_type; + u_int8_t param_oui_sybtype; + u_int8_t param_version; + u_int8_t param_qosInfo; +#define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */ + u_int8_t param_reserved; + struct ieee80211_wme_acparams params_acParams[WME_NUM_AC]; +} __packed; + +/* + * Management Notification Frame + */ +struct ieee80211_mnf { + u_int8_t mnf_category; + u_int8_t mnf_action; + u_int8_t mnf_dialog; + u_int8_t mnf_status; +} __packed; +#define MNF_SETUP_REQ 0 +#define MNF_SETUP_RESP 1 +#define MNF_TEARDOWN 2 /* * Control frames. @@ -223,7 +296,7 @@ struct ieee80211_frame_min { u_int8_t i_addr1[IEEE80211_ADDR_LEN]; u_int8_t i_addr2[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_rts { u_int8_t i_fc[2]; @@ -231,21 +304,21 @@ struct ieee80211_frame_rts { u_int8_t i_ra[IEEE80211_ADDR_LEN]; u_int8_t i_ta[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_cts { u_int8_t i_fc[2]; u_int8_t i_dur[2]; u_int8_t i_ra[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_ack { u_int8_t i_fc[2]; u_int8_t i_dur[2]; u_int8_t i_ra[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_pspoll { u_int8_t i_fc[2]; @@ -253,7 +326,7 @@ struct ieee80211_frame_pspoll { u_int8_t i_bssid[IEEE80211_ADDR_LEN]; u_int8_t i_ta[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ u_int8_t i_fc[2]; @@ -261,7 +334,7 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ u_int8_t i_ra[IEEE80211_ADDR_LEN]; u_int8_t i_bssid[IEEE80211_ADDR_LEN]; /* FCS */ -} __attribute__((__packed__)); +} __packed; /* * BEACON management packets @@ -275,7 +348,7 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ * octet information[length] */ -typedef uint8_t *ieee80211_mgt_beacon_t; +typedef u_int8_t *ieee80211_mgt_beacon_t; #define IEEE80211_BEACON_INTERVAL(beacon) \ ((beacon)[8] | ((beacon)[9] << 8)) @@ -301,6 +374,8 @@ typedef uint8_t *ieee80211_mgt_beacon_t; * 802.11i/WPA information element (maximally sized). */ struct ieee80211_ie_wpa { + u_int8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */ + u_int8_t wpa_len; /* length in bytes */ u_int8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ u_int8_t wpa_type; /* OUI type */ u_int16_t wpa_version; /* spec revision */ @@ -309,95 +384,70 @@ struct ieee80211_ie_wpa { u_int32_t wpa_uciphers[8];/* ciphers */ u_int16_t wpa_authselcnt; /* authentication selector cnt*/ u_int32_t wpa_authsels[8];/* selectors */ -} __attribute__((__packed__)); + u_int16_t wpa_caps; /* 802.11i capabilities */ + u_int16_t wpa_pmkidcnt; /* 802.11i pmkid count */ + u_int16_t wpa_pmkids[8]; /* 802.11i pmkids */ +} __packed; /* - * Management information elements + * Management information element payloads. */ -struct ieee80211_information { - char ssid[IEEE80211_NWID_LEN+1]; - struct rates { - u_int8_t *p; - } rates; - struct fh { - u_int16_t dwell; - u_int8_t set; - u_int8_t pattern; - u_int8_t index; - } fh; - struct ds { - u_int8_t channel; - } ds; - struct cf { - u_int8_t count; - u_int8_t period; - u_int8_t maxdur[2]; - u_int8_t dur[2]; - } cf; - struct tim { - u_int8_t count; - u_int8_t period; - u_int8_t bitctl; - /* u_int8_t pvt[251]; The driver needs to use this. */ - } tim; - struct ibss { - u_int16_t atim; - } ibss; - struct challenge { - u_int8_t *p; - u_int8_t len; - } challenge; - struct erp { - u_int8_t flags; - } erp; - struct country { - u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ - struct { - u_int8_t schan; /* starting channel */ - u_int8_t nchan; /* number channels */ - u_int8_t maxtxpwr; - } band[4]; /* up to 4 sub bands */ - } country; - struct ath { - u_int8_t flags; - } ath; - struct ieee80211_ie_wpa wpa; -}; enum { - IEEE80211_ELEMID_SSID = 0, - IEEE80211_ELEMID_RATES = 1, - IEEE80211_ELEMID_FHPARMS = 2, - IEEE80211_ELEMID_DSPARMS = 3, - IEEE80211_ELEMID_CFPARMS = 4, - IEEE80211_ELEMID_TIM = 5, - IEEE80211_ELEMID_IBSSPARMS = 6, - IEEE80211_ELEMID_COUNTRY = 7, - IEEE80211_ELEMID_CHALLENGE = 16, + IEEE80211_ELEMID_SSID = 0, + IEEE80211_ELEMID_RATES = 1, + IEEE80211_ELEMID_FHPARMS = 2, + IEEE80211_ELEMID_DSPARMS = 3, + IEEE80211_ELEMID_CFPARMS = 4, + IEEE80211_ELEMID_TIM = 5, + IEEE80211_ELEMID_IBSSPARMS = 6, + IEEE80211_ELEMID_COUNTRY = 7, + IEEE80211_ELEMID_CHALLENGE = 16, /* 17-31 reserved for challenge text extension */ - IEEE80211_ELEMID_ERP = 42, - IEEE80211_ELEMID_XRATES = 50, - IEEE80211_ELEMID_TPC = 150, - IEEE80211_ELEMID_CCKM = 156, - IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ + IEEE80211_ELEMID_ERP = 42, + IEEE80211_ELEMID_RSN = 48, + IEEE80211_ELEMID_XRATES = 50, + IEEE80211_ELEMID_TPC = 150, + IEEE80211_ELEMID_CCKM = 156, + IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ }; -#define IEEE80211_CHALLENGE_LEN 128 - -#define IEEE80211_RATE_BASIC 0x80 -#define IEEE80211_RATE_VAL 0x7f +struct ieee80211_tim_ie { + u_int8_t tim_ie; /* IEEE80211_ELEMID_TIM */ + u_int8_t tim_len; + u_int8_t tim_count; /* DTIM count */ + u_int8_t tim_period; /* DTIM period */ + u_int8_t tim_bitctl; /* bitmap control */ + u_int8_t tim_bitmap[1]; /* variable-length bitmap */ +} __packed; + +struct ieee80211_country_ie { + u_int8_t ie; /* IEEE80211_ELEMID_COUNTRY */ + u_int8_t len; + u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ + struct { + u_int8_t schan; /* starting channel */ + u_int8_t nchan; /* number channels */ + u_int8_t maxtxpwr; /* tx power cap */ + } band[4] __packed; /* up to 4 sub bands */ +} __packed; + +#define IEEE80211_CHALLENGE_LEN 128 + +#define IEEE80211_RATE_BASIC 0x80 +#define IEEE80211_RATE_VAL 0x7f /* EPR information element flags */ -#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 -#define IEEE80211_ERP_USE_PROTECTION 0x02 -#define IEEE80211_ERP_BARKER_MODE 0x04 +#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 +#define IEEE80211_ERP_USE_PROTECTION 0x02 +#define IEEE80211_ERP_LONG_PREAMBLE 0x04 /* Atheros private advanced capabilities info */ -#define ATHEROS_CAP_TURBO_PRIME 0x01 -#define ATHEROS_CAP_COMPRESSION 0x02 -#define ATHEROS_CAP_FAST_FRAME 0x04 +#define ATHEROS_CAP_TURBO_PRIME 0x01 +#define ATHEROS_CAP_COMPRESSION 0x02 +#define ATHEROS_CAP_FAST_FRAME 0x04 /* bits 3-6 reserved */ -#define ATHEROS_CAP_BOOST 0x80 +#define ATHEROS_CAP_BOOST 0x80 #define ATH_OUI 0x7f0300 /* Atheros OUI */ #define ATH_OUI_TYPE 0x01 @@ -405,12 +455,11 @@ enum { #define WPA_OUI 0xf25000 #define WPA_OUI_TYPE 0x01 -#define WPA_OUI_VERSION 1 /* current supported version */ +#define WPA_VERSION 1 /* current supported version */ #define WPA_CSE_NULL 0x00 #define WPA_CSE_WEP40 0x01 #define WPA_CSE_TKIP 0x02 -#define WPA_CSE_WRAP 0x03 /* WPA2/802.11i */ #define WPA_CSE_CCMP 0x04 #define WPA_CSE_WEP104 0x05 @@ -418,6 +467,34 @@ enum { #define WPA_ASE_8021X_UNSPEC 0x01 #define WPA_ASE_8021X_PSK 0x02 +#define RSN_OUI 0xac0f00 +#define RSN_VERSION 1 /* current supported version */ + +#define RSN_CSE_NULL 0x00 +#define RSN_CSE_WEP40 0x01 +#define RSN_CSE_TKIP 0x02 +#define RSN_CSE_WRAP 0x03 +#define RSN_CSE_CCMP 0x04 +#define RSN_CSE_WEP104 0x05 + +#define RSN_ASE_NONE 0x00 +#define RSN_ASE_8021X_UNSPEC 0x01 +#define RSN_ASE_8021X_PSK 0x02 + +#define RSN_CAP_PREAUTH 0x01 + +#define WME_OUI 0xf25000 +#define WME_OUI_TYPE 0x02 +#define WME_INFO_OUI_SUBTYPE 0x00 +#define WME_PARAM_OUI_SUBTYPE 0x01 +#define WME_VERSION 1 + +/* WME stream classes */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ + /* * AUTH management packets * @@ -438,9 +515,9 @@ typedef u_int8_t *ieee80211_mgt_auth_t; #define IEEE80211_AUTH_STATUS(auth) \ ((auth)[4] | ((auth)[5] << 8)) -#define IEEE80211_AUTH_ALG_OPEN 0x0000 -#define IEEE80211_AUTH_ALG_SHARED 0x0001 -#define IEEE80211_AUTH_ALG_LEAP 0x0080 +#define IEEE80211_AUTH_ALG_OPEN 0x0000 +#define IEEE80211_AUTH_ALG_SHARED 0x0001 +#define IEEE80211_AUTH_ALG_LEAP 0x0080 enum { IEEE80211_AUTH_OPEN_REQUEST = 1, @@ -496,13 +573,24 @@ enum { IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, }; -#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ -#define IEEE80211_WEP_IVLEN 3 /* 24bit */ -#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ -#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ -#define IEEE80211_WEP_NKID 4 /* number of key ids */ +#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ +#define IEEE80211_WEP_IVLEN 3 /* 24bit */ +#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ +#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ +#define IEEE80211_WEP_NKID 4 /* number of key ids */ -#define IEEE80211_CRC_LEN 4 +/* + * 802.11i defines an extended IV for use with non-WEP ciphers. + * When the EXTIV bit is set in the key id byte an additional + * 4 bytes immediately follow the IV for TKIP. For CCMP the + * EXTIV bit is likewise set but the 8 bytes represent the + * CCMP header rather than IV+extended-IV. + */ +#define IEEE80211_WEP_EXTIV 0x20 +#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */ +#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */ + +#define IEEE80211_CRC_LEN 4 /* * Maximum acceptable MTU is: @@ -511,26 +599,40 @@ enum { * Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default * mtu is Ethernet-compatible; it's set by ether_ifattach. */ -#define IEEE80211_MTU_MAX 2290 -#define IEEE80211_MTU_MIN 32 +#define IEEE80211_MTU_MAX 2290 +#define IEEE80211_MTU_MIN 32 -#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ +#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_ACK_LEN \ + (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) #define IEEE80211_MIN_LEN \ (sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN) +/* + * The 802.11 spec says at most 2007 stations may be + * associated at once. For most AP's this is way more + * than is feasible so we use a default of 128. This + * number may be overridden by the driver and/or by + * user configuration. + */ +#define IEEE80211_AID_MAX 2007 +#define IEEE80211_AID_DEF 128 + +#define IEEE80211_AID(b) ((b) &~ 0xc000) +#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))) + /* * RTS frame length parameters. The default is specified in * the 802.11 spec. The max may be wrong for jumbo frames. */ -#define IEEE80211_RTS_DEFAULT 512 -#define IEEE80211_RTS_MIN 1 -#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN - -enum { - IEEE80211_AUTH_NONE = 0, - IEEE80211_AUTH_OPEN = 1, - IEEE80211_AUTH_SHARED = 2, -}; +#define IEEE80211_RTS_DEFAULT 512 +#define IEEE80211_RTS_MIN 1 +#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN #endif /* _NET80211_IEEE80211_H_ */ diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c new file mode 100644 index 0000000..e04fa13 --- /dev/null +++ b/sys/net80211/ieee80211_acl.c @@ -0,0 +1,301 @@ +/*- + * Copyright (c) 2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 MAC ACL support. + * + * When this module is loaded the sender address of each received + * frame is passed to the iac_check method and the module indicates + * if the frame should be accepted or rejected. If the policy is + * set to ACL_POLICY_OPEN then all frames are accepted w/o checking + * the address. Otherwise, the address is looked up in the database + * and if found the frame is either accepted (ACL_POLICY_ALLOW) + * or rejected (ACL_POLICY_DENT). + */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +enum { + ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ + ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ + ACL_POLICY_DENY = 2, /* deny traffic from MAC */ +}; + +#define ACL_HASHSIZE 32 + +struct acl { + TAILQ_ENTRY(acl) acl_list; + LIST_ENTRY(acl) acl_hash; + u_int8_t acl_macaddr[IEEE80211_ADDR_LEN]; +}; +struct aclstate { + acl_lock_t as_lock; + int as_policy; + TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ + LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; + struct ieee80211com *as_ic; +}; + +/* simple hash is enough for variation of macaddr */ +#define ACL_HASH(addr) \ + (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) + +MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); + +static int acl_free_all(struct ieee80211com *); + +static int +acl_attach(struct ieee80211com *ic) +{ + struct aclstate *as; + + MALLOC(as, struct aclstate *, sizeof(struct aclstate), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (as == NULL) + return 0; + ACL_LOCK_INIT(as, "acl"); + TAILQ_INIT(&as->as_list); + as->as_policy = ACL_POLICY_OPEN; + as->as_ic = ic; + ic->ic_as = as; + return 1; +} + +static void +acl_detach(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + + acl_free_all(ic); + ic->ic_as = NULL; + ACL_LOCK_DESTROY(as); + FREE(as, M_DEVBUF); +} + +static inline struct acl * +_find_acl(struct aclstate *as, const u_int8_t *macaddr) +{ + struct acl *acl; + int hash; + + hash = ACL_HASH(macaddr); + LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { + if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) + return acl; + } + return NULL; +} + +static void +_acl_free(struct aclstate *as, struct acl *acl) +{ + ACL_LOCK_ASSERT(as); + + TAILQ_REMOVE(&as->as_list, acl, acl_list); + LIST_REMOVE(acl, acl_hash); + FREE(acl, M_80211_ACL); +} + +static int +acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + + switch (as->as_policy) { + case ACL_POLICY_OPEN: + return 1; + case ACL_POLICY_ALLOW: + return _find_acl(as, mac) != NULL; + case ACL_POLICY_DENY: + return _find_acl(as, mac) == NULL; + } + return 0; /* should not happen */ +} + +static int +acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_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, + "ACL: add %s failed, no memory\n", ether_sprintf(mac)); + /* XXX statistic */ + return ENOMEM; + } + + ACL_LOCK(as); + hash = ACL_HASH(mac); + LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { + if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { + ACL_UNLOCK(as); + FREE(new, M_80211_ACL); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: add %s failed, already present\n", + ether_sprintf(mac)); + return EEXIST; + } + } + IEEE80211_ADDR_COPY(new->acl_macaddr, mac); + TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); + LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); + ACL_UNLOCK(as); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: add %s\n", ether_sprintf(mac)); + return 0; +} + +static int +acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + + ACL_LOCK(as); + acl = _find_acl(as, mac); + if (acl != NULL) + _acl_free(as, acl); + ACL_UNLOCK(as); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: remove %s%s\n", ether_sprintf(mac), + acl == NULL ? ", not present" : ""); + + return (acl == NULL ? ENOENT : 0); +} + +static int +acl_free_all(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); + + ACL_LOCK(as); + while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) + _acl_free(as, acl); + ACL_UNLOCK(as); + + return 0; +} + +static int +acl_setpolicy(struct ieee80211com *ic, int policy) +{ + struct aclstate *as = ic->ic_as; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + "ACL: set policy to %u\n", policy); + + switch (policy) { + case IEEE80211_MACCMD_POLICY_OPEN: + as->as_policy = ACL_POLICY_OPEN; + break; + case IEEE80211_MACCMD_POLICY_ALLOW: + as->as_policy = ACL_POLICY_ALLOW; + break; + case IEEE80211_MACCMD_POLICY_DENY: + as->as_policy = ACL_POLICY_DENY; + break; + default: + return EINVAL; + } + return 0; +} + +static int +acl_getpolicy(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + + return as->as_policy; +} + +static const struct ieee80211_aclator mac = { + .iac_name = "mac", + .iac_attach = acl_attach, + .iac_detach = acl_detach, + .iac_check = acl_check, + .iac_add = acl_add, + .iac_remove = acl_remove, + .iac_flush = acl_free_all, + .iac_setpolicy = acl_setpolicy, + .iac_getpolicy = acl_getpolicy, +}; + +/* + * Module glue. + */ +static int +wlan_acl_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("wlan: <802.11 MAC ACL support>\n"); + ieee80211_aclator_register(&mac); + return 0; + case MOD_UNLOAD: + ieee80211_aclator_unregister(&mac); + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_acl_mod = { + "wlan_acl", + wlan_acl_modevent, + 0 +}; +DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_acl, 1); +MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1); diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c index 6cb9ee0..5e71215 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,290 +33,527 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_inet.h" - +/* + * IEEE 802.11 generic crypto support. + */ #include -#include #include -#include -#include + #include -#include -#include -#include -#include -#include -#include - -#include - + #include -#include #include -#include -#include -#include +#include /* XXX ETHER_HDR_LEN */ #include -#include +/* + * Table of registered cipher modules. + */ +static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX]; + +static int _ieee80211_crypto_delkey(struct ieee80211com *, + struct ieee80211_key *); + +/* + * Default "null" key management routines. + */ +static int +null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + return IEEE80211_KEYIX_NONE; +} +static int +null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + return 1; +} +static int +null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + return 1; +} +static void null_key_update(struct ieee80211com *ic) {} + +/* + * Write-arounds for common operations. + */ +static __inline void +cipher_detach(struct ieee80211_key *key) +{ + key->wk_cipher->ic_detach(key); +} -#ifdef INET -#include -#include -#endif +static __inline void * +cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key) +{ + return key->wk_cipher->ic_attach(ic, key); +} -#include -#define arc4_ctxlen() sizeof (struct rc4_state) -#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l) -#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l) +/* + * Wrappers for driver key management methods. + */ +static __inline int +dev_key_alloc(struct ieee80211com *ic, + const struct ieee80211_key *key) +{ + return ic->ic_crypto.cs_key_alloc(ic, key); +} + +static __inline int +dev_key_delete(struct ieee80211com *ic, + const struct ieee80211_key *key) +{ + return ic->ic_crypto.cs_key_delete(ic, key); +} -static void ieee80211_crc_init(void); -static u_int32_t ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len); +static __inline int +dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + return ic->ic_crypto.cs_key_set(ic, key, mac); +} +/* + * Setup crypto support. + */ void -ieee80211_crypto_attach(struct ifnet *ifp) +ieee80211_crypto_attach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211_crypto_state *cs = &ic->ic_crypto; + int i; + /* NB: we assume everything is pre-zero'd */ + cs->cs_def_txkey = IEEE80211_KEYIX_NONE; + ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none; + for (i = 0; i < IEEE80211_WEP_NKID; i++) + ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i], i); /* - * Setup crypto support. + * Initialize the driver key support routines to noop entries. + * This is useful especially for the cipher test modules. */ - ieee80211_crc_init(); - ic->ic_iv = arc4random(); + cs->cs_key_alloc = null_key_alloc; + cs->cs_key_set = null_key_set; + cs->cs_key_delete = null_key_delete; + cs->cs_key_update_begin = null_key_update; + cs->cs_key_update_end = null_key_update; } +/* + * Teardown crypto support. + */ void -ieee80211_crypto_detach(struct ifnet *ifp) +ieee80211_crypto_detach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + ieee80211_crypto_delglobalkeys(ic); +} - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; +/* + * Register a crypto cipher module. + */ +void +ieee80211_crypto_register(const struct ieee80211_cipher *cip) +{ + if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { + printf("%s: cipher %s has an invalid cipher index %u\n", + __func__, cip->ic_name, cip->ic_cipher); + return; } + if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { + printf("%s: cipher %s registered with a different template\n", + __func__, cip->ic_name); + return; + } + ciphers[cip->ic_cipher] = cip; } -struct mbuf * -ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag) +/* + * Unregister a crypto cipher module. + */ +void +ieee80211_crypto_unregister(const struct ieee80211_cipher *cip) { - struct ieee80211com *ic = (void *)ifp; - struct mbuf *m, *n, *n0; - struct ieee80211_frame *wh; - int i, left, len, moff, noff, kid; - u_int32_t iv, crc; - u_int8_t *ivp; - void *ctx; - u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; - u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; - - n0 = NULL; - if ((ctx = ic->ic_wep_ctx) == NULL) { - ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT); - if (ctx == NULL) { - ic->ic_stats.is_crypto_nomem++; - goto fail; - } - ic->ic_wep_ctx = ctx; - } - m = m0; - left = m->m_pkthdr.len; - MGET(n, M_DONTWAIT, m->m_type); - n0 = n; - if (n == NULL) { - if (txflag) - ic->ic_stats.is_tx_nombuf++; - else - ic->ic_stats.is_rx_nombuf++; - goto fail; + if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { + printf("%s: cipher %s has an invalid cipher index %u\n", + __func__, cip->ic_name, cip->ic_cipher); + return; } - M_MOVE_PKTHDR(n, m); - len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; - if (txflag) { - n->m_pkthdr.len += len; - } else { - n->m_pkthdr.len -= len; - left -= len; + if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { + printf("%s: cipher %s registered with a different template\n", + __func__, cip->ic_name); + return; } - n->m_len = MHLEN; - if (n->m_pkthdr.len >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; + /* NB: don't complain about not being registered */ + /* XXX disallow if references */ + ciphers[cip->ic_cipher] = NULL; +} + +int +ieee80211_crypto_available(u_int cipher) +{ + return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL; +} + +/* XXX well-known names! */ +static const char *cipher_modnames[] = { + "wlan_wep", /* IEEE80211_CIPHER_WEP */ + "wlan_tkip", /* IEEE80211_CIPHER_TKIP */ + "wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */ + "wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */ + "wlan_ckip", /* IEEE80211_CIPHER_CKIP */ +}; + +/* + * Establish a relationship between the specified key and cipher + * and, if not a global key, allocate a hardware index from the + * driver. Note that we may be called for global keys but they + * should have a key index already setup so the only work done + * is to setup the cipher reference. + * + * This must be the first call applied to a key; all the other key + * routines assume wk_cipher is setup. + * + * Locking must be handled by the caller using: + * ieee80211_key_update_begin(ic); + * ieee80211_key_update_end(ic); + */ +int +ieee80211_crypto_newkey(struct ieee80211com *ic, + int cipher, struct ieee80211_key *key) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + const struct ieee80211_cipher *cip; + void *keyctx; + int oflags; + + /* + * Validate cipher and set reference to cipher routines. + */ + if (cipher >= IEEE80211_CIPHER_MAX) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: invalid cipher %u\n", __func__, cipher); + ic->ic_stats.is_crypto_badcipher++; + return 0; } - len = sizeof(struct ieee80211_frame); - memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len); - wh = mtod(n, struct ieee80211_frame *); - left -= len; - moff = len; - noff = len; - if (txflag) { - kid = ic->ic_wep_txkey; - wh->i_fc[1] |= IEEE80211_FC1_WEP; - iv = ic->ic_iv; + cip = ciphers[cipher]; + if (cip == NULL) { /* - * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: - * (B, 255, N) with 3 <= B < 8 + * Auto-load cipher module if we have a well-known name + * for it. It might be better to use string names rather + * than numbers and craft a module name based on the cipher + * name; e.g. wlan_cipher_. */ - if (iv >= 0x03ff00 && - (iv & 0xf8ff00) == 0x00ff00) - iv += 0x000100; - ic->ic_iv = iv + 1; - /* put iv in little endian to prepare 802.11i */ - ivp = mtod(n, u_int8_t *) + noff; - for (i = 0; i < IEEE80211_WEP_IVLEN; i++) { - ivp[i] = iv & 0xff; - iv >>= 8; + if (cipher < N(cipher_modnames)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unregistered cipher %u, load module %s\n", + __func__, cipher, cipher_modnames[cipher]); + ieee80211_load_module(cipher_modnames[cipher]); + /* + * If cipher module loaded it should immediately + * call ieee80211_crypto_register which will fill + * in the entry in the ciphers array. + */ + cip = ciphers[cipher]; } - ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */ - noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } else { - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - ivp = mtod(m, u_int8_t *) + moff; - kid = ivp[IEEE80211_WEP_IVLEN] >> 6; - moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } - memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN); - memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key, - ic->ic_nw_keys[kid].wk_len); - arc4_setkey(ctx, keybuf, - IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len); - - /* encrypt with calculating CRC */ - crc = ~0; - while (left > 0) { - len = m->m_len - moff; - if (len == 0) { - m = m->m_next; - moff = 0; - continue; + if (cip == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unable to load cipher %u, module %s\n", + __func__, cipher, + cipher < N(cipher_modnames) ? + cipher_modnames[cipher] : ""); + ic->ic_stats.is_crypto_nocipher++; + return 0; } - if (len > n->m_len - noff) { - len = n->m_len - noff; - if (len == 0) { - MGET(n->m_next, M_DONTWAIT, n->m_type); - if (n->m_next == NULL) { - if (txflag) - ic->ic_stats.is_tx_nombuf++; - else - ic->ic_stats.is_rx_nombuf++; - goto fail; - } - n = n->m_next; - n->m_len = MLEN; - if (left >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - noff = 0; - continue; - } + } + + oflags = key->wk_flags; + /* + * If the hardware does not support the cipher then + * fallback to a host-based implementation. + */ + key->wk_flags &= ~(IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC); + if ((ic->ic_caps & (1<ic_name); + key->wk_flags |= IEEE80211_KEY_SWCRYPT; + } + /* + * Hardware TKIP with software MIC is an important + * combination; we handle it by flagging each key, + * the cipher modules honor it. + */ + if (cipher == IEEE80211_CIPHER_TKIP && + (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: no h/w support for TKIP MIC, falling back to s/w\n", + __func__); + key->wk_flags |= IEEE80211_KEY_SWMIC; + } + + /* + * Bind cipher to key instance. Note we do this + * after checking the device capabilities so the + * cipher module can optimize space usage based on + * whether or not it needs to do the cipher work. + */ + if (key->wk_cipher != cip || key->wk_flags != oflags) { +again: + keyctx = cip->ic_attach(ic, key); + if (keyctx == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unable to attach cipher %s\n", + __func__, cip->ic_name); + key->wk_flags = oflags; /* restore old flags */ + ic->ic_stats.is_crypto_attachfail++; + return 0; } - if (len > left) - len = left; - arc4_encrypt(ctx, mtod(n, caddr_t) + noff, - mtod(m, caddr_t) + moff, len); - if (txflag) - crc = ieee80211_crc_update(crc, - mtod(m, u_int8_t *) + moff, len); - else - crc = ieee80211_crc_update(crc, - mtod(n, u_int8_t *) + noff, len); - left -= len; - moff += len; - noff += len; + cipher_detach(key); + key->wk_cipher = cip; /* XXX refcnt? */ + key->wk_private = keyctx; } - crc = ~crc; - if (txflag) { - *(u_int32_t *)crcbuf = htole32(crc); - if (n->m_len >= noff + sizeof(crcbuf)) - n->m_len = noff + sizeof(crcbuf); - else { - n->m_len = noff; - MGET(n->m_next, M_DONTWAIT, n->m_type); - if (n->m_next == NULL) { - ic->ic_stats.is_tx_nombuf++; - goto fail; + + /* + * Ask the driver for a key index if we don't have one. + * Note that entries in the global key table always have + * an index; this means it's safe to call this routine + * for these entries just to setup the reference to the + * cipher template. Note also that when using software + * crypto we also call the driver to give us a key index. + */ + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + key->wk_keyix = dev_key_alloc(ic, key); + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + /* + * Driver has no room; fallback to doing crypto + * in the host. We change the flags and start the + * procedure over. If we get back here then there's + * no hope and we bail. Note that this can leave + * the key in a inconsistent state if the caller + * continues to use it. + */ + if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { + ic->ic_stats.is_crypto_swfallback++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: no h/w resources for cipher %s, " + "falling back to s/w\n", __func__, + cip->ic_name); + oflags = key->wk_flags; + key->wk_flags |= IEEE80211_KEY_SWCRYPT; + if (cipher == IEEE80211_CIPHER_TKIP) + key->wk_flags |= IEEE80211_KEY_SWMIC; + goto again; } - n = n->m_next; - n->m_len = sizeof(crcbuf); - noff = 0; - } - arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf, - sizeof(crcbuf)); - } else { - n->m_len = noff; - for (noff = 0; noff < sizeof(crcbuf); noff += len) { - len = sizeof(crcbuf) - noff; - if (len > m->m_len - moff) - len = m->m_len - moff; - if (len > 0) - arc4_encrypt(ctx, crcbuf + noff, - mtod(m, caddr_t) + moff, len); - m = m->m_next; - moff = 0; + ic->ic_stats.is_crypto_keyfail++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: unable to setup cipher %s\n", + __func__, cip->ic_name); + return 0; } - if (crc != le32toh(*(u_int32_t *)crcbuf)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - if_printf(ifp, "decrypt CRC error\n"); - if (ieee80211_debug > 1) - ieee80211_dump_pkt(n0->m_data, - n0->m_len, -1, -1); - } -#endif - ic->ic_stats.is_rx_decryptcrc++; - goto fail; + } + return 1; +#undef N +} + +/* + * Remove the key (no locking, for internal use). + */ +static int +_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +{ + u_int16_t keyix; + + KASSERT(key->wk_cipher != NULL, ("No cipher!")); + + keyix = key->wk_keyix; + if (keyix != IEEE80211_KEYIX_NONE) { + /* + * Remove hardware entry. + */ + /* XXX key cache */ + if (!dev_key_delete(ic, key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: driver did not delete key index %u\n", + __func__, keyix); + ic->ic_stats.is_crypto_delkey++; + /* XXX recovery? */ } } - m_freem(m0); - return n0; + cipher_detach(key); + memset(key, 0, sizeof(*key)); + key->wk_cipher = &ieee80211_cipher_none; + key->wk_private = cipher_attach(ic, key); + /* NB: cannot depend on key index to decide this */ + if (&ic->ic_nw_keys[0] <= key && + key < &ic->ic_nw_keys[IEEE80211_WEP_NKID]) + key->wk_keyix = keyix; /* preserve shared key state */ + else + key->wk_keyix = IEEE80211_KEYIX_NONE; + return 1; +} + +/* + * Remove the specified key. + */ +int +ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +{ + int status; + + ieee80211_key_update_begin(ic); + status = _ieee80211_crypto_delkey(ic, key); + ieee80211_key_update_end(ic); + return status; +} - fail: - m_freem(m0); - m_freem(n0); - return NULL; +/* + * Clear the global key table. + */ +void +ieee80211_crypto_delglobalkeys(struct ieee80211com *ic) +{ + int i; + + ieee80211_key_update_begin(ic); + for (i = 0; i < IEEE80211_WEP_NKID; i++) + (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]); + ieee80211_key_update_end(ic); } /* - * CRC 32 -- routine from RFC 2083 + * Set the contents of the specified key. + * + * Locking must be handled by the caller using: + * ieee80211_key_update_begin(ic); + * ieee80211_key_update_end(ic); */ +int +ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key, + const u_int8_t macaddr[IEEE80211_ADDR_LEN]) +{ + const struct ieee80211_cipher *cip = key->wk_cipher; -/* Table of CRCs of all 8-bit messages */ -static u_int32_t ieee80211_crc_table[256]; + KASSERT(cip != NULL, ("No cipher!")); -/* Make the table for a fast CRC. */ -static void -ieee80211_crc_init(void) + /* + * Give cipher a chance to validate key contents. + * XXX should happen before modifying state. + */ + if (!cip->ic_setkey(key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: cipher %s rejected key index %u len %u flags 0x%x\n", + __func__, cip->ic_name, key->wk_keyix, + key->wk_keylen, key->wk_flags); + ic->ic_stats.is_crypto_setkey_cipher++; + return 0; + } + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + /* XXX nothing allocated, should not happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: no key index; should not happen!\n", __func__); + ic->ic_stats.is_crypto_setkey_nokey++; + return 0; + } + return dev_key_set(ic, key, macaddr); +} + +/* + * Add privacy headers appropriate for the specified key. + */ +struct ieee80211_key * +ieee80211_crypto_encap(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) { - u_int32_t c; - int n, k; - - for (n = 0; n < 256; n++) { - c = (u_int32_t)n; - for (k = 0; k < 8; k++) { - if (c & 1) - c = 0xedb88320UL ^ (c >> 1); - else - c = c >> 1; + struct ieee80211_key *k; + struct ieee80211_frame *wh; + const struct ieee80211_cipher *cip; + u_int8_t keyix; + + /* + * Multicast traffic always uses the multicast key. + * Otherwise if a unicast key is set we use that and + * it is always key index 0. When no unicast key is + * set we fall back to the default transmit key. + */ + wh = mtod(m, struct ieee80211_frame *); + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "%s: No default xmit key for frame to %s\n", + __func__, ether_sprintf(wh->i_addr1)); + ic->ic_stats.is_tx_nodefkey++; + return NULL; } - ieee80211_crc_table[n] = c; + keyix = ic->ic_def_txkey; + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + } else { + keyix = 0; + k = &ni->ni_ucastkey; } + cip = k->wk_cipher; + return (cip->ic_encap(k, m, keyix<<6) ? k : NULL); } /* - * Update a running CRC with the bytes buf[0..len-1]--the CRC - * should be initialized to all 1's, and the transmitted value - * is the 1's complement of the final running CRC + * Validate and strip privacy headers (and trailer) for a + * received frame that has the WEP/Privacy bit set. */ - -static u_int32_t -ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len) +struct ieee80211_key * +ieee80211_crypto_decap(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) { - u_int8_t *endbuf; +#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) +#define IEEE80211_WEP_MINLEN \ + (sizeof(struct ieee80211_frame) + ETHER_HDR_LEN + \ + IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN) + struct ieee80211_key *k; + struct ieee80211_frame *wh; + const struct ieee80211_cipher *cip; + u_int8_t *ivp; + u_int8_t keyid; + int hdrlen; + + /* NB: this minimum size data frame could be bigger */ + if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: WEP data frame too short, len %u\n", + __func__, m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; /* XXX need unique stat? */ + return NULL; + } + + /* + * Locate the key. If unicast and there is no unicast + * key then we fall back to the key id in the header. + * This assumes unicast keys are only configured when + * the key id in the header is meaningless (typically 0). + */ + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_hdrsize(wh); + ivp = mtod(m, u_int8_t *) + hdrlen; /* XXX contig */ + keyid = ivp[IEEE80211_WEP_IVLEN]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) + k = &ic->ic_nw_keys[keyid >> 6]; + else + k = &ni->ni_ucastkey; + + /* + * Insure crypto header is contiguous for all decap work. + */ + cip = k->wk_cipher; + if (m->m_len < hdrlen + cip->ic_header && + (m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] unable to pullup %s header\n", + ether_sprintf(wh->i_addr2), cip->ic_name); + ic->ic_stats.is_rx_wepfail++; /* XXX */ + return 0; + } - for (endbuf = buf + len; buf < endbuf; buf++) - crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); - return crc; + return (cip->ic_decap(k, m) ? k : NULL); +#undef IEEE80211_WEP_MINLEN +#undef IEEE80211_WEP_HDRLEN } diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h index 89b13dc..d7a238a 100644 --- a/sys/net80211/ieee80211_crypto.h +++ b/sys/net80211/ieee80211_crypto.h @@ -1,6 +1,7 @@ +/* $NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,13 +39,184 @@ * 802.11 protocol crypto-related definitions. */ #define IEEE80211_KEYBUF_SIZE 16 +#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */ +/* + * Old WEP-style key. Deprecated. + */ struct ieee80211_wepkey { - int wk_len; - u_int8_t wk_key[IEEE80211_KEYBUF_SIZE]; + u_int wk_len; /* key length in bytes */ + u_int8_t wk_key[IEEE80211_KEYBUF_SIZE]; +}; + +struct ieee80211_cipher; + +/* + * Crypto key state. There is sufficient room for all supported + * ciphers (see below). The underlying ciphers are handled + * separately through loadable cipher modules that register with + * the generic crypto support. A key has a reference to an instance + * of the cipher; any per-key state is hung off wk_private by the + * cipher when it is attached. Ciphers are automatically called + * to detach and cleanup any such state when the key is deleted. + * + * The generic crypto support handles encap/decap of cipher-related + * frame contents for both hardware- and software-based implementations. + * A key requiring software crypto support is automatically flagged and + * the cipher is expected to honor this and do the necessary work. + * Ciphers such as TKIP may also support mixed hardware/software + * encrypt/decrypt and MIC processing. + */ +/* XXX need key index typedef */ +/* XXX pack better? */ +/* XXX 48-bit rsc/tsc */ +struct ieee80211_key { + u_int8_t wk_keylen; /* key length in bytes */ + u_int8_t wk_flags; +#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */ +#define IEEE80211_KEY_RECV 0x02 /* key used for recv */ +#define IEEE80211_KEY_SWCRYPT 0x04 /* host-based encrypt/decrypt */ +#define IEEE80211_KEY_SWMIC 0x08 /* host-based enmic/demic */ + u_int16_t wk_keyix; /* key index */ + u_int8_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 */ + u_int64_t wk_keyrsc; /* key receive sequence counter */ + u_int64_t wk_keytsc; /* key transmit sequence counter */ + const struct ieee80211_cipher *wk_cipher; + void *wk_private; /* private cipher state */ +}; + +/* + * NB: these values are ordered carefully; there are lots of + * of implications in any reordering. In particular beware + * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY. + */ +#define IEEE80211_CIPHER_WEP 0 +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_OCB 2 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CIPHER_CKIP 5 +#define IEEE80211_CIPHER_NONE 6 /* pseudo value */ + +#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1) + +#define IEEE80211_KEYIX_NONE ((u_int16_t) -1) + +#if defined(__KERNEL__) || defined(_KERNEL) + +struct ieee80211com; +struct ieee80211_node; +struct mbuf; + +/* + * Crypto state kept in each ieee80211com. Some of this + * can/should be shared when virtual AP's are supported. + * + * XXX save reference to ieee80211com to properly encapsulate state. + * XXX split out crypto capabilities from ic_caps + */ +struct ieee80211_crypto_state { + struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID]; + u_int16_t cs_def_txkey; /* default/group tx key index */ + + int (*cs_key_alloc)(struct ieee80211com *, + const struct ieee80211_key *); + int (*cs_key_delete)(struct ieee80211com *, + const struct ieee80211_key *); + int (*cs_key_set)(struct ieee80211com *, + const struct ieee80211_key *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + void (*cs_key_update_begin)(struct ieee80211com *); + void (*cs_key_update_end)(struct ieee80211com *); +}; + +extern void ieee80211_crypto_attach(struct ieee80211com *); +extern void ieee80211_crypto_detach(struct ieee80211com *); +extern int ieee80211_crypto_newkey(struct ieee80211com *, + int cipher, struct ieee80211_key *); +extern int ieee80211_crypto_delkey(struct ieee80211com *, + struct ieee80211_key *); +extern int ieee80211_crypto_setkey(struct ieee80211com *, + struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]); +extern void ieee80211_crypto_delglobalkeys(struct ieee80211com *); + +/* + * Template for a supported cipher. Ciphers register with the + * crypto code and are typically loaded as separate modules + * (the null cipher is always present). + * XXX may need refcnts + */ +struct ieee80211_cipher { + const char *ic_name; /* printable name */ + u_int ic_cipher; /* IEEE80211_CIPHER_* */ + u_int ic_header; /* size of privacy header (bytes) */ + u_int ic_trailer; /* size of privacy trailer (bytes) */ + u_int ic_miclen; /* size of mic trailer (bytes) */ + void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *); + void (*ic_detach)(struct ieee80211_key *); + int (*ic_setkey)(struct ieee80211_key *); + int (*ic_encap)(struct ieee80211_key *, struct mbuf *, + u_int8_t keyid); + int (*ic_decap)(struct ieee80211_key *, struct mbuf *); + int (*ic_enmic)(struct ieee80211_key *, struct mbuf *); + int (*ic_demic)(struct ieee80211_key *, struct mbuf *); }; +extern const struct ieee80211_cipher ieee80211_cipher_none; + +extern void ieee80211_crypto_register(const struct ieee80211_cipher *); +extern void ieee80211_crypto_unregister(const struct ieee80211_cipher *); +extern int ieee80211_crypto_available(u_int cipher); -extern void ieee80211_crypto_attach(struct ifnet *); -extern void ieee80211_crypto_detach(struct ifnet *); -extern struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int); +extern struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); +extern struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); + +/* + * Check and remove any MIC. + */ +static __inline int +ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k, + struct mbuf *m) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? cip->ic_demic(k, m) : 1); +} + +/* + * Add any MIC. + */ +static __inline int +ieee80211_crypto_enmic(struct ieee80211com *ic, + struct ieee80211_key *k, struct mbuf *m) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m) : 1); +} + +/* + * Reset key state to an unused state. The crypto + * key allocation mechanism insures other state (e.g. + * key data) is properly setup before a key is used. + */ +static __inline void +ieee80211_crypto_resetkey(struct ieee80211com *ic, + struct ieee80211_key *k, u_int16_t ix) +{ + k->wk_cipher = &ieee80211_cipher_none;; + k->wk_private = k->wk_cipher->ic_attach(ic, k); + k->wk_keyix = ix; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; +} + +/* + * Crypt-related notification methods. + */ +extern void ieee80211_notify_replay_failure(struct ieee80211com *, + const struct ieee80211_frame *, const struct ieee80211_key *, + u_int64_t rsc); +extern void ieee80211_notify_michael_failure(struct ieee80211com *, + const struct ieee80211_frame *, u_int keyix); +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ #endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c new file mode 100644 index 0000000..b3373b2 --- /dev/null +++ b/sys/net80211/ieee80211_crypto_ccmp.c @@ -0,0 +1,603 @@ +/*- + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11i AES-CCMP crypto support. + * + * Part of this module is derived from similar code in the Host + * AP driver. The code is used with the consent of the author and + * it's license is included below. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#define AES_BLOCK_LEN 16 + +struct ccmp_ctx { + struct ieee80211com *cc_ic; /* for diagnostics */ + rijndael_ctx cc_aes; +}; + +static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *); +static void ccmp_detach(struct ieee80211_key *); +static int ccmp_setkey(struct ieee80211_key *); +static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, u_int8_t keyid); +static int ccmp_decap(struct ieee80211_key *, struct mbuf *); +static int ccmp_enmic(struct ieee80211_key *, struct mbuf *); +static int ccmp_demic(struct ieee80211_key *, struct mbuf *); + +static const struct ieee80211_cipher ccmp = { + .ic_name = "AES-CCM", + .ic_cipher = IEEE80211_CIPHER_AES_CCM, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = IEEE80211_WEP_MICLEN, + .ic_miclen = 0, + .ic_attach = ccmp_attach, + .ic_detach = ccmp_detach, + .ic_setkey = ccmp_setkey, + .ic_encap = ccmp_encap, + .ic_decap = ccmp_decap, + .ic_enmic = ccmp_enmic, + .ic_demic = ccmp_demic, +}; + +static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); +static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn, + struct mbuf *, int hdrlen); + +static void * +ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx; + + MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + return NULL; + } + ctx->cc_ic = ic; + return ctx; +} + +static void +ccmp_detach(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + FREE(ctx, M_DEVBUF); +} + +static int +ccmp_setkey(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (128/NBBY)) { + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 128/NBBY); + return 0; + } + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) + rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY); + return 1; +} + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +{ + u_int8_t *ivp; + int hdrlen; + + hdrlen = ieee80211_hdrsize(mtod(m, void *)); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + M_PREPEND(m, ccmp.ic_header, M_NOWAIT); + if (m == NULL) + return 0; + ivp = mtod(m, u_int8_t *); + ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen); + ivp += hdrlen; + + k->wk_keytsc++; /* XXX wrap at 48 bits */ + ivp[0] = k->wk_keytsc >> 0; /* PN0 */ + ivp[1] = k->wk_keytsc >> 8; /* PN1 */ + ivp[2] = 0; /* Reserved */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* PN2 */ + ivp[5] = k->wk_keytsc >> 24; /* PN3 */ + ivp[6] = k->wk_keytsc >> 32; /* PN4 */ + ivp[7] = k->wk_keytsc >> 40; /* PN5 */ + + /* + * Finally, do software encrypt if neeed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !ccmp_encrypt(k, m, hdrlen)) + return 0; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +ccmp_enmic(struct ieee80211_key *k, struct mbuf *m) +{ + + return 1; +} + +static __inline uint64_t +READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) +{ + uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + uint16_t iv16 = (b4 << 0) | (b5 << 8); + return (((uint64_t)iv16) << 32) | iv32; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. The specified key should be correct but + * is also verified. + */ +static int +ccmp_decap(struct ieee80211_key *k, struct mbuf *m) +{ + struct ccmp_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + uint8_t *ivp; + uint64_t pn; + int hdrlen; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_hdrsize(wh); + ivp = mtod(m, uint8_t *) + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "[%s] Missing ExtIV for AES-CCM cipher\n", + ether_sprintf(wh->i_addr2)); + ctx->cc_ic->ic_stats.is_rx_ccmpformat++; + return 0; + } + pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); + if (pn <= k->wk_keyrsc) { + /* + * Replay violation. + */ + ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn); + ctx->cc_ic->ic_stats.is_rx_ccmpreplay++; + return 0; + } + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. Note that for the + * latter we leave the header in place for use in the + * decryption work. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !ccmp_decrypt(k, pn, m, hdrlen)) + return 0; + + /* + * Copy up 802.11 header and strip crypto bits. + */ + ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + ccmp.ic_header, hdrlen); + m_adj(m, ccmp.ic_header); + m_adj(m, -ccmp.ic_trailer); + + /* + * Ok to update rsc now. + */ + k->wk_keyrsc = pn; + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +ccmp_demic(struct ieee80211_key *k, struct mbuf *m) +{ + return 1; +} + +static __inline void +xor_block(uint8_t *b, const uint8_t *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +static void +ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, + u_int64_t pn, size_t dlen, + uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN], + uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN]) +{ +#define IS_4ADDRESS(wh) \ + ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) +#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh) + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + /* NB: b0[1] set below */ + IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2); + b0[8] = pn >> 40; + b0[9] = pn >> 32; + b0[10] = pn >> 24; + b0[11] = pn >> 16; + b0[12] = pn >> 8; + b0[13] = pn >> 0; + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + aad[0] = 0; /* AAD length >> 8 */ + /* NB: aad[1] set below */ + aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */ + aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */ + /* NB: we know 3 addresses are contiguous */ + memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN); + aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK; + aad[23] = 0; /* all bits masked */ + /* + * Construct variable-length portion of AAD based + * on whether this is a 4-address frame/QOS frame. + * We always zero-pad to 32 bytes before running it + * through the cipher. + * + * We also fill in the priority bits of the CCM + * initial block as we know whether or not we have + * a QOS frame. + */ + if (IS_4ADDRESS(wh)) { + IEEE80211_ADDR_COPY(aad + 24, + ((struct ieee80211_frame_addr4 *)wh)->i_addr4); + if (IS_QOS_DATA(wh)) { + struct ieee80211_qosframe_addr4 *qwh4 = + (struct ieee80211_qosframe_addr4 *) wh; + aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */ + aad[31] = 0; + b0[1] = aad[30]; + aad[1] = 22 + IEEE80211_ADDR_LEN + 2; + } else { + *(u_int16_t *)&aad[30] = 0; + b0[1] = 0; + aad[1] = 22 + IEEE80211_ADDR_LEN; + } + } else { + if (IS_QOS_DATA(wh)) { + struct ieee80211_qosframe *qwh = + (struct ieee80211_qosframe*) wh; + aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */ + aad[25] = 0; + b0[1] = aad[24]; + aad[1] = 22 + 2; + } else { + *(u_int16_t *)&aad[24] = 0; + b0[1] = 0; + aad[1] = 22; + } + *(u_int16_t *)&aad[26] = 0; + *(u_int32_t *)&aad[28] = 0; + } + + /* Start with the first block and AAD */ + rijndael_encrypt(ctx, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + rijndael_encrypt(ctx, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + rijndael_encrypt(ctx, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + rijndael_encrypt(ctx, b0, s0); +#undef IS_QOS_DATA +#undef IS_4ADDRESS +} + +#define CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do { \ + /* Authentication */ \ + xor_block(_b, _pos, _len); \ + rijndael_encrypt(&ctx->cc_aes, _b, _b); \ + /* Encryption, with counter */ \ + _b0[14] = (_i >> 8) & 0xff; \ + _b0[15] = _i & 0xff; \ + rijndael_encrypt(&ctx->cc_aes, _b0, _e); \ + xor_block(_pos, _e, _len); \ +} while (0) + +static int +ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ + struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + struct mbuf *m = m0; + int data_len, i; + uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], + e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN]; + uint8_t *pos; + u_int space; + + ctx->cc_ic->ic_stats.is_crypto_ccmp++; + + wh = mtod(m, struct ieee80211_frame *); + data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header); + ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc, + data_len, b0, aad, b, s0); + + i = 1; + pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; + /* NB: assumes header is entirely in first mbuf */ + space = m->m_len - (hdrlen + ccmp.ic_header); + for (;;) { + if (space > data_len) + space = data_len; + /* + * Do full blocks. + */ + while (space >= AES_BLOCK_LEN) { + CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN); + pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; + data_len -= AES_BLOCK_LEN; + i++; + } + if (data_len <= 0) /* no more data */ + break; + m = m->m_next; + if (m == NULL) { /* last buffer */ + if (space != 0) { + /* + * Short last block. + */ + CCMP_ENCRYPT(i, b, b0, pos, e, space); + } + break; + } + if (space != 0) { + uint8_t *pos_next; + u_int space_next; + u_int len; + + /* + * Block straddles buffers, split references. We + * do not handle splits that require >2 buffers. + */ + pos_next = mtod(m, uint8_t *); + len = min(data_len, AES_BLOCK_LEN); + space_next = len > space ? len - space : 0; + KASSERT(m->m_len >= space_next, + ("not enough data in following buffer, " + "m_len %u need %u\n", m->m_len, space_next)); + + xor_block(b+space, pos_next, space_next); + CCMP_ENCRYPT(i, b, b0, pos, e, space); + xor_block(pos_next, e+space, space_next); + data_len -= len; + /* XXX could check for data_len <= 0 */ + i++; + + pos = pos_next + space_next; + space = m->m_len - space_next; + } else { + /* + * Setup for next buffer. + */ + pos = mtod(m, uint8_t *); + space = m->m_len; + } + } + /* tack on MIC */ + xor_block(b, s0, ccmp.ic_trailer); + return m_append(m0, ccmp.ic_trailer, b); +} +#undef CCMP_ENCRYPT + +#define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \ + /* Decrypt, with counter */ \ + _b0[14] = (_i >> 8) & 0xff; \ + _b0[15] = _i & 0xff; \ + rijndael_encrypt(&ctx->cc_aes, _b0, _b); \ + xor_block(_pos, _b, _len); \ + /* Authentication */ \ + xor_block(_a, _pos, _len); \ + rijndael_encrypt(&ctx->cc_aes, _a, _a); \ +} while (0) + +static int +ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen) +{ + struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + uint8_t aad[2 * AES_BLOCK_LEN]; + uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; + uint8_t mic[AES_BLOCK_LEN]; + size_t data_len; + int i; + uint8_t *pos; + u_int space; + + ctx->cc_ic->ic_stats.is_crypto_ccmp++; + + wh = mtod(m, struct ieee80211_frame *); + data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer); + ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b); + m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic); + xor_block(mic, b, ccmp.ic_trailer); + + i = 1; + pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; + space = m->m_len - (hdrlen + ccmp.ic_header); + for (;;) { + if (space > data_len) + space = data_len; + while (space >= AES_BLOCK_LEN) { + CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN); + pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; + data_len -= AES_BLOCK_LEN; + i++; + } + if (data_len <= 0) /* no more data */ + break; + m = m->m_next; + if (m == NULL) { /* last buffer */ + if (space != 0) /* short last block */ + CCMP_DECRYPT(i, b, b0, pos, a, space); + break; + } + if (space != 0) { + uint8_t *pos_next; + u_int space_next; + u_int len; + + /* + * Block straddles buffers, split references. We + * do not handle splits that require >2 buffers. + */ + pos_next = mtod(m, uint8_t *); + len = min(data_len, AES_BLOCK_LEN); + space_next = len > space ? len - space : 0; + KASSERT(m->m_len >= space_next, + ("not enough data in following buffer, " + "m_len %u need %u\n", m->m_len, space_next)); + + xor_block(b+space, pos_next, space_next); + CCMP_DECRYPT(i, b, b0, pos, a, space); + xor_block(pos_next, b+space, space_next); + data_len -= len; + i++; + + pos = pos_next + space_next; + space = m->m_len - space_next; + } else { + /* + * Setup for next buffer. + */ + pos = mtod(m, uint8_t *); + space = m->m_len; + } + } + if (memcmp(mic, a, ccmp.ic_trailer) != 0) { + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "[%s] AES-CCM decrypt failed; MIC mismatch\n", + ether_sprintf(wh->i_addr2)); + ctx->cc_ic->ic_stats.is_rx_ccmpmic++; + return 0; + } + return 1; +} +#undef CCMP_DECRYPT + +/* + * Module glue. + */ +static int +ccmp_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_crypto_register(&ccmp); + return 0; + case MOD_UNLOAD: + ieee80211_crypto_unregister(&ccmp); + return 0; + } + return EINVAL; +} + +static moduledata_t ccmp_mod = { + "wlan_ccmp", + ccmp_modevent, + 0 +}; +DECLARE_MODULE(wlan_ccmp, ccmp_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_ccmp, 1); +MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c new file mode 100644 index 0000000..6d53ea9 --- /dev/null +++ b/sys/net80211/ieee80211_crypto_none.c @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 NULL crypto support. + */ +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +static void *none_attach(struct ieee80211com *, struct ieee80211_key *); +static void none_detach(struct ieee80211_key *); +static int none_setkey(struct ieee80211_key *); +static int none_encap(struct ieee80211_key *, struct mbuf *, u_int8_t); +static int none_decap(struct ieee80211_key *, struct mbuf *); +static int none_enmic(struct ieee80211_key *, struct mbuf *); +static int none_demic(struct ieee80211_key *, struct mbuf *); + +const struct ieee80211_cipher ieee80211_cipher_none = { + .ic_name = "NONE", + .ic_cipher = IEEE80211_CIPHER_NONE, + .ic_header = 0, + .ic_trailer = 0, + .ic_miclen = 0, + .ic_attach = none_attach, + .ic_detach = none_detach, + .ic_setkey = none_setkey, + .ic_encap = none_encap, + .ic_decap = none_decap, + .ic_enmic = none_enmic, + .ic_demic = none_demic, +}; + +static void * +none_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + return ic; /* for diagnostics+stats */ +} + +static void +none_detach(struct ieee80211_key *k) +{ + (void) k; +} + +static int +none_setkey(struct ieee80211_key *k) +{ + (void) k; + return 1; +} + +static int +none_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +{ + struct ieee80211com *ic = k->wk_private; +#ifdef IEEE80211_DEBUG + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); +#endif + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] key (id %u) is invalid\n", + ether_sprintf(wh->i_addr1), keyid>>6); + ic->ic_stats.is_tx_badcipher++; + return 0; +} + +static int +none_decap(struct ieee80211_key *k, struct mbuf *m) +{ + struct ieee80211com *ic = k->wk_private; +#ifdef IEEE80211_DEBUG + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + const u_int8_t *ivp = (const u_int8_t *)&wh[1]; +#endif + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + /* XXX useful to know dst too */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] key (id %u) is invalid\n", + ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6); + ic->ic_stats.is_rx_badkeyid++; + return 0; +} + +static int +none_enmic(struct ieee80211_key *k, struct mbuf *m) +{ + struct ieee80211com *ic = k->wk_private; + + ic->ic_stats.is_tx_badcipher++; + return 0; +} + +static int +none_demic(struct ieee80211_key *k, struct mbuf *m) +{ + struct ieee80211com *ic = k->wk_private; + + ic->ic_stats.is_rx_badkeyid++; + return 0; +} diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c new file mode 100644 index 0000000..89ff8ba --- /dev/null +++ b/sys/net80211/ieee80211_crypto_tkip.c @@ -0,0 +1,987 @@ +/*- + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11i TKIP crypto support. + * + * Part of this module is derived from similar code in the Host + * AP driver. The code is used with the consent of the author and + * it's license is included below. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *); +static void tkip_detach(struct ieee80211_key *); +static int tkip_setkey(struct ieee80211_key *); +static int tkip_encap(struct ieee80211_key *, struct mbuf *m, u_int8_t keyid); +static int tkip_enmic(struct ieee80211_key *, struct mbuf *); +static int tkip_decap(struct ieee80211_key *, struct mbuf *); +static int tkip_demic(struct ieee80211_key *, struct mbuf *); + +static const struct ieee80211_cipher tkip = { + .ic_name = "TKIP", + .ic_cipher = IEEE80211_CIPHER_TKIP, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = IEEE80211_WEP_CRCLEN, + .ic_miclen = IEEE80211_WEP_MICLEN, + .ic_attach = tkip_attach, + .ic_detach = tkip_detach, + .ic_setkey = tkip_setkey, + .ic_encap = tkip_encap, + .ic_decap = tkip_decap, + .ic_enmic = tkip_enmic, + .ic_demic = tkip_demic, +}; + +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 */ + + u16 tx_ttak[5]; + int tx_phase1_done; + u8 tx_rc4key[16]; /* XXX for test module; make locals? */ + + u16 rx_ttak[5]; + int rx_phase1_done; + u8 rx_rc4key[16]; /* XXX for test module; make locals? */ + uint64_t rx_rsc; /* held until MIC verified */ +}; + +static void michael_mic(struct tkip_ctx *, const u8 *key, + struct mbuf *m, u_int off, size_t data_len, + u8 mic[IEEE80211_WEP_MICLEN]); +static int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *, + struct mbuf *, int hdr_len); +static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *, + struct mbuf *, int hdr_len); + +static void * +tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct tkip_ctx *ctx; + + MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + return NULL; + } + + ctx->tc_ic = ic; + return ctx; +} + +static void +tkip_detach(struct ieee80211_key *k) +{ + struct tkip_ctx *ctx = k->wk_private; + + FREE(ctx, M_DEVBUF); +} + +static int +tkip_setkey(struct ieee80211_key *k) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (128/NBBY)) { + (void) ctx; /* XXX */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 128/NBBY); + return 0; + } + k->wk_keytsc = 1; /* TSC starts at 1 */ + return 1; +} + +/* + * Add privacy headers and do any s/w encryption required. + */ +static int +tkip_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + u_int8_t *ivp; + int hdrlen; + + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { +#ifdef IEEE80211_DEBUG + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); +#endif + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] Discard frame due to countermeasures (%s)\n", + ether_sprintf(wh->i_addr2), __func__); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + hdrlen = ieee80211_hdrsize(mtod(m, void *)); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + M_PREPEND(m, tkip.ic_header, M_NOWAIT); + if (m == NULL) + return 0; + ivp = mtod(m, u_int8_t *); + memmove(ivp, ivp + tkip.ic_header, hdrlen); + ivp += hdrlen; + + ivp[0] = k->wk_keytsc >> 8; /* TSC1 */ + ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */ + ivp[2] = k->wk_keytsc >> 0; /* TSC0 */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* TSC2 */ + ivp[5] = k->wk_keytsc >> 24; /* TSC3 */ + ivp[6] = k->wk_keytsc >> 32; /* TSC4 */ + ivp[7] = k->wk_keytsc >> 40; /* TSC5 */ + + /* + * Finally, do software encrypt if neeed. + */ + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + if (!tkip_encrypt(ctx, k, m, hdrlen)) + return 0; + /* NB: tkip_encrypt handles wk_keytsc */ + } else + k->wk_keytsc++; /* XXX wrap at 48 bits */ + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +tkip_enmic(struct ieee80211_key *k, struct mbuf *m) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (k->wk_flags & IEEE80211_KEY_SWMIC) { + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + int hdrlen = ieee80211_hdrsize(wh); + uint8_t mic[IEEE80211_WEP_MICLEN]; + + ctx->tc_ic->ic_stats.is_crypto_tkipenmic++; + + michael_mic(ctx, k->wk_txmic, + m, hdrlen, m->m_pkthdr.len - hdrlen, mic); + return m_append(m, tkip.ic_miclen, mic); + } + return 1; +} + +static __inline uint64_t +READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) +{ + uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + uint16_t iv16 = (b4 << 0) | (b5 << 8); + return (((uint64_t)iv16) << 32) | iv32; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. If necessary, decrypt the frame using + * the specified key. + */ +static int +tkip_decap(struct ieee80211_key *k, struct mbuf *m) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + struct ieee80211_frame *wh; + uint8_t *ivp; + int hdrlen; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_hdrsize(wh); + ivp = mtod(m, uint8_t *) + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "[%s] Missing ExtIV for TKIP cipher\n", + ether_sprintf(wh->i_addr2)); + ctx->tc_ic->ic_stats.is_rx_tkipformat++; + return 0; + } + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] Discard frame due to countermeasures (%s)\n", + ether_sprintf(wh->i_addr2), __func__); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + + ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], + ivp[6], ivp[7]); + if (ctx->rx_rsc <= k->wk_keyrsc) { + /* + * Replay violation; notify upper layer. + */ + ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc); + ctx->tc_ic->ic_stats.is_rx_tkipreplay++; + return 0; + } + /* + * NB: We can't update the rsc in the key until MIC is verified. + * + * We assume we are not preempted between doing the check above + * and updating wk_keyrsc when stripping the MIC in tkip_demic. + * Otherwise we might process another packet and discard it as + * a replay. + */ + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !tkip_decrypt(ctx, k, m, hdrlen)) + return 0; + + /* + * Copy up 802.11 header and strip crypto bits. + */ + memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *), hdrlen); + m_adj(m, tkip.ic_header); + m_adj(m, -tkip.ic_trailer); + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +tkip_demic(struct ieee80211_key *k, struct mbuf *m) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (k->wk_flags & IEEE80211_KEY_SWMIC) { + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + int hdrlen = ieee80211_hdrsize(wh); + u8 mic[IEEE80211_WEP_MICLEN]; + u8 mic0[IEEE80211_WEP_MICLEN]; + + ctx->tc_ic->ic_stats.is_crypto_tkipdemic++; + + michael_mic(ctx, k->wk_rxmic, + m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen), + mic); + m_copydata(m, m->m_pkthdr.len - tkip.ic_miclen, + tkip.ic_miclen, mic0); + if (memcmp(mic, mic0, tkip.ic_miclen)) { + /* NB: 802.11 layer handles statistic and debug msg */ + ieee80211_notify_michael_failure(ctx->tc_ic, wh, + k->wk_keyix); + return 0; + } + } + /* + * Strip MIC from the tail. + */ + m_adj(m, -tkip.ic_miclen); + + /* + * Ok to update rsc now that MIC has been verified. + */ + k->wk_keyrsc = ctx->rx_rsc; + + return 1; +} + +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +static const __u32 crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static __inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + +static __inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + +static __inline u8 Hi8(u16 val) +{ + return val >> 8; +} + +static __inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + +static __inline u16 Hi16(u32 val) +{ + return val >> 16; +} + +static __inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (((u16) hi) << 8); +} + +static __inline u16 Mk16_le(const u16 *v) +{ + return le16toh(*v); +} + +static const u16 Sbox[256] = { + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + +static __inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + +#ifndef _BYTE_ORDER +#error "Don't know native byte order" +#endif + +static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + u16 *PPK = (u16 *) &WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((const u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((const u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((const u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((const u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((const u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((const u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((const u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((const u16 *) &TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((const u16 *) &TK[0])) >> 1); + +#if _BYTE_ORDER == _BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + +static void +wep_encrypt(u8 *key, struct mbuf *m0, u_int off, size_t data_len, + uint8_t icv[IEEE80211_WEP_CRCLEN]) +{ + u32 i, j, k, crc; + size_t buflen; + u8 S[256]; + u8 *pos; + struct mbuf *m; +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + m = m0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + m = m->m_next; + if (m == NULL) { + KASSERT(data_len == 0, + ("out of buffers with data_len %u\n", data_len)); + break; + } + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + icv[k] ^= S[(S[i] + S[j]) & 0xff]; + } +} + +static int +wep_decrypt(u8 *key, struct mbuf *m, u_int off, size_t data_len) +{ + u32 i, j, k, crc; + u8 S[256]; + u8 *pos, icv[4]; + size_t buflen; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + crc = ~0; + i = j = 0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + m = m->m_next; + if (m == NULL) { + KASSERT(data_len == 0, + ("out of buffers with data_len %u\n", data_len)); + break; + } + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with the + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return -1; + } + } + + return 0; +} + + +static __inline u32 rotl(u32 val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + + +static __inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + + +static __inline u32 xswap(u32 val) +{ + return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); +} + + +#define michael_block(l, r) \ +do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ +} while (0) + + +static __inline u32 get_le32_split(u8 b0, u8 b1, u8 b2, u8 b3) +{ + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); +} + +static __inline u32 get_le32(const u8 *p) +{ + return get_le32_split(p[0], p[1], p[2], p[3]); +} + + +static __inline void put_le32(u8 *p, u32 v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + +/* + * Craft pseudo header used to calculate the MIC. + */ +static void +michael_mic_hdr(const struct ieee80211_frame *wh0, uint8_t hdr[16]) +{ + const struct ieee80211_frame_addr4 *wh = + (const struct ieee80211_frame_addr4 *) wh0; + + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4); + break; + } + + hdr[12] = 0; /* XXX qos priority */ + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + +static void +michael_mic(struct tkip_ctx *ctx, const u8 *key, + struct mbuf *m, u_int off, size_t data_len, + u8 mic[IEEE80211_WEP_MICLEN]) +{ + uint8_t hdr[16]; + u32 l, r; + const uint8_t *data; + u_int space; + + michael_mic_hdr(mtod(m, struct ieee80211_frame *), hdr); + + l = get_le32(key); + r = get_le32(key + 4); + + /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */ + l ^= get_le32(hdr); + michael_block(l, r); + l ^= get_le32(&hdr[4]); + michael_block(l, r); + l ^= get_le32(&hdr[8]); + michael_block(l, r); + l ^= get_le32(&hdr[12]); + michael_block(l, r); + + /* first buffer has special handling */ + data = mtod(m, const uint8_t *) + off; + space = m->m_len - off; + for (;;) { + if (space > data_len) + space = data_len; + /* collect 32-bit blocks from current buffer */ + while (space >= sizeof(uint32_t)) { + l ^= get_le32(data); + michael_block(l, r); + data += sizeof(uint32_t), space -= sizeof(uint32_t); + data_len -= sizeof(uint32_t); + } + if (data_len < sizeof(uint32_t)) + break; + m = m->m_next; + if (m == NULL) { + KASSERT(0, ("out of data, data_len %u\n", data_len)); + break; + } + if (space != 0) { + const uint8_t *data_next; + /* + * Block straddles buffers, split references. + */ + data_next = mtod(m, const uint8_t *); + KASSERT(m->m_len >= sizeof(uint32_t) - space, + ("not enough data in following buffer, " + "m_len %u need %u\n", m->m_len, + sizeof(uint32_t) - space)); + switch (space) { + case 1: + l ^= get_le32_split(data[0], data_next[0], + data_next[1], data_next[2]); + data = data_next + 3; + space = m->m_len - 3; + break; + case 2: + l ^= get_le32_split(data[0], data[1], + data_next[0], data_next[1]); + data = data_next + 2; + space = m->m_len - 2; + break; + case 3: + l ^= get_le32_split(data[0], data[1], + data[2], data_next[0]); + data = data_next + 1; + space = m->m_len - 1; + break; + } + michael_block(l, r); + data_len -= sizeof(uint32_t); + } else { + /* + * Setup for next buffer. + */ + data = mtod(m, const uint8_t *); + space = m->m_len; + } + } + /* Last block and padding (0x5a, 4..7 x 0) */ + switch (data_len) { + case 0: + l ^= get_le32_split(0x5a, 0, 0, 0); + break; + case 1: + l ^= get_le32_split(data[0], 0x5a, 0, 0); + break; + case 2: + l ^= get_le32_split(data[0], data[1], 0x5a, 0); + break; + case 3: + l ^= get_le32_split(data[0], data[1], data[2], 0x5a); + break; + } + michael_block(l, r); + /* l ^= 0; */ + michael_block(l, r); + + put_le32(mic, l); + put_le32(mic + 4, r); +} + +static int +tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, + struct mbuf *m, int hdrlen) +{ + struct ieee80211_frame *wh; + uint8_t icv[IEEE80211_WEP_CRCLEN]; + + ctx->tc_ic->ic_stats.is_crypto_tkip++; + + wh = mtod(m, struct ieee80211_frame *); + if (!ctx->tx_phase1_done) { + tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2, + (u32)(key->wk_keytsc >> 16)); + ctx->tx_phase1_done = 1; + } + tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak, + (u16) key->wk_keytsc); + + wep_encrypt(ctx->tx_rc4key, + m, hdrlen + tkip.ic_header, + m->m_pkthdr.len - (hdrlen + tkip.ic_header), + icv); + (void) m_append(m, IEEE80211_WEP_CRCLEN, icv); /* XXX check return */ + + key->wk_keytsc++; /* XXX wrap at 48 bits */ + if ((u16)(key->wk_keytsc) == 0) + ctx->tx_phase1_done = 0; + return 1; +} + +static int +tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, + struct mbuf *m, int hdrlen) +{ + struct ieee80211_frame *wh; + u32 iv32; + u16 iv16; + + ctx->tc_ic->ic_stats.is_crypto_tkip++; + + wh = mtod(m, struct ieee80211_frame *); + /* NB: tkip_decap already verified header and left seq in rx_rsc */ + iv16 = (u16) ctx->rx_rsc; + iv32 = (u32) (ctx->rx_rsc >> 16); + + if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) { + tkip_mixing_phase1(ctx->rx_ttak, key->wk_key, + wh->i_addr2, iv32); + ctx->rx_phase1_done = 1; + } + tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16); + + /* NB: m is unstripped; deduct headers + ICV to get payload */ + if (wep_decrypt(ctx->rx_rc4key, + m, hdrlen + tkip.ic_header, + m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) { + if (iv32 != (u32)(key->wk_keyrsc >> 16)) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + ctx->rx_phase1_done = 0; + } + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "[%s] TKIP ICV mismatch on decrypt\n", + ether_sprintf(wh->i_addr2)); + ctx->tc_ic->ic_stats.is_rx_tkipicv++; + return 0; + } + return 1; +} + +/* + * Module glue. + */ +static int +tkip_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_crypto_register(&tkip); + return 0; + case MOD_UNLOAD: + ieee80211_crypto_unregister(&tkip); + return 0; + } + return EINVAL; +} + +static moduledata_t tkip_mod = { + "wlan_tkip", + tkip_modevent, + 0 +}; +DECLARE_MODULE(wlan_tkip, tkip_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_tkip, 1); +MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c new file mode 100644 index 0000000..2edb5bb --- /dev/null +++ b/sys/net80211/ieee80211_crypto_wep.c @@ -0,0 +1,499 @@ +/*- + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 WEP crypto support. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +static void *wep_attach(struct ieee80211com *, struct ieee80211_key *); +static void wep_detach(struct ieee80211_key *); +static int wep_setkey(struct ieee80211_key *); +static int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid); +static int wep_decap(struct ieee80211_key *, struct mbuf *); +static int wep_enmic(struct ieee80211_key *, struct mbuf *); +static int wep_demic(struct ieee80211_key *, struct mbuf *); + +static const struct ieee80211_cipher wep = { + .ic_name = "WEP", + .ic_cipher = IEEE80211_CIPHER_WEP, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, + .ic_trailer = IEEE80211_WEP_CRCLEN, + .ic_miclen = 0, + .ic_attach = wep_attach, + .ic_detach = wep_detach, + .ic_setkey = wep_setkey, + .ic_encap = wep_encap, + .ic_decap = wep_decap, + .ic_enmic = wep_enmic, + .ic_demic = wep_demic, +}; + +static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); +static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); + +struct wep_ctx { + struct ieee80211com *wc_ic; /* for diagnostics */ + u_int32_t wc_iv; /* initial vector for crypto */ +}; + +static void * +wep_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct wep_ctx *ctx; + + MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + return NULL; + } + + ctx->wc_ic = ic; + get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); + return ctx; +} + +static void +wep_detach(struct ieee80211_key *k) +{ + struct wep_ctx *ctx = k->wk_private; + + FREE(ctx, M_DEVBUF); +} + +static int +wep_setkey(struct ieee80211_key *k) +{ + return k->wk_keylen >= 40/NBBY; +} + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) +{ + struct wep_ctx *ctx = k->wk_private; + u_int32_t iv; + u_int8_t *ivp; + int hdrlen; + + hdrlen = ieee80211_hdrsize(mtod(m, void *)); + + /* + * Copy down 802.11 header and add the IV + KeyID. + */ + M_PREPEND(m, wep.ic_header, M_NOWAIT); + if (m == NULL) + return 0; + ivp = mtod(m, u_int8_t *); + ovbcopy(ivp + wep.ic_header, ivp, hdrlen); + ivp += hdrlen; + + /* + * XXX + * IV must not duplicate during the lifetime of the key. + * But no mechanism to renew keys is defined in IEEE 802.11 + * for WEP. And the IV may be duplicated at other stations + * because the session key itself is shared. So we use a + * pseudo random IV for now, though it is not the right way. + * + * NB: Rather than use a strictly random IV we select a + * random one to start and then increment the value for + * each frame. This is an explicit tradeoff between + * overhead and security. Given the basic insecurity of + * WEP this seems worthwhile. + */ + + /* + * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: + * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 + */ + iv = ctx->wc_iv; + if ((iv & 0xff00) == 0xff00) { + int B = (iv & 0xff0000) >> 16; + if (3 <= B && B < 16) + iv += 0x0100; + } + ctx->wc_iv = iv + 1; + + /* + * NB: Preserve byte order of IV for packet + * sniffers; it doesn't matter otherwise. + */ +#if _BYTE_ORDER == _BIG_ENDIAN + ivp[0] = iv >> 0; + ivp[1] = iv >> 8; + ivp[2] = iv >> 16; +#else + ivp[2] = iv >> 0; + ivp[1] = iv >> 8; + ivp[0] = iv >> 16; +#endif + ivp[3] = keyid; + + /* + * Finally, do software encrypt if neeed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !wep_encrypt(k, m, hdrlen)) + return 0; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +wep_enmic(struct ieee80211_key *k, struct mbuf *m) +{ + + return 1; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. If necessary, decrypt the frame using + * the specified key. + */ +static int +wep_decap(struct ieee80211_key *k, struct mbuf *m) +{ + struct wep_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + int hdrlen; + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_hdrsize(wh); + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !wep_decrypt(k, m, hdrlen)) { + IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, + "[%s] WEP ICV mismatch on decrypt\n", + ether_sprintf(wh->i_addr2)); + ctx->wc_ic->ic_stats.is_rx_wepfail++; + return 0; + } + + /* + * Copy up 802.11 header and strip crypto bits. + */ + ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen); + m_adj(m, wep.ic_header); + m_adj(m, -wep.ic_trailer); + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +wep_demic(struct ieee80211_key *k, struct mbuf *skb) +{ + return 1; +} + +static const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static int +wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ +#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + struct wep_ctx *ctx = key->wk_private; + struct mbuf *m = m0; + u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + uint8_t icv[IEEE80211_WEP_CRCLEN]; + uint32_t i, j, k, crc; + size_t buflen, data_len; + uint8_t S[256]; + uint8_t *pos; + u_int off, keylen; + + ctx->wc_ic->ic_stats.is_crypto_wep++; + + /* NB: this assumes the header was pulled up */ + memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; + for (i = 0; i < 256; i++) { + j = (j + S[i] + rc4key[i % keylen]) & 0xff; + S_SWAP(i, j); + } + + off = hdrlen + wep.ic_header; + data_len = m->m_pkthdr.len - off; + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + if (m->m_next == NULL) { + if (data_len != 0) { /* out of data */ + IEEE80211_DPRINTF(ctx->wc_ic, + IEEE80211_MSG_CRYPTO, + "[%s] out of data for WEP (data_len %u)\n", + ether_sprintf(mtod(m0, + struct ieee80211_frame *)->i_addr2), + data_len); + return 0; + } + break; + } + m = m->m_next; + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + icv[k] ^= S[(S[i] + S[j]) & 0xff]; + } + return m_append(m0, IEEE80211_WEP_CRCLEN, icv); +#undef S_SWAP +} + +static int +wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) +{ +#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + struct wep_ctx *ctx = key->wk_private; + struct mbuf *m = m0; + u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + uint8_t icv[IEEE80211_WEP_CRCLEN]; + uint32_t i, j, k, crc; + size_t buflen, data_len; + uint8_t S[256]; + uint8_t *pos; + u_int off, keylen; + + ctx->wc_ic->ic_stats.is_crypto_wep++; + + /* NB: this assumes the header was pulled up */ + memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; + for (i = 0; i < 256; i++) { + j = (j + S[i] + rc4key[i % keylen]) & 0xff; + S_SWAP(i, j); + } + + off = hdrlen + wep.ic_header; + data_len = m->m_pkthdr.len - (off + wep.ic_trailer), + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + pos = mtod(m, uint8_t *) + off; + buflen = m->m_len - off; + for (;;) { + if (buflen > data_len) + buflen = data_len; + data_len -= buflen; + for (k = 0; k < buflen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + m = m->m_next; + if (m == NULL) { + if (data_len != 0) { /* out of data */ + IEEE80211_DPRINTF(ctx->wc_ic, + IEEE80211_MSG_CRYPTO, + "[%s] out of data for WEP (data_len %u)\n", + ether_sprintf(mtod(m0, + struct ieee80211_frame *)->i_addr2), + data_len); + return 0; + } + break; + } + pos = mtod(m, uint8_t *); + buflen = m->m_len; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + /* XXX assumes ICV is contiguous in mbuf */ + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return 0; + } + } + return 1; +#undef S_SWAP +} + +/* + * Module glue. + */ +static int +wep_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_crypto_register(&wep); + return 0; + case MOD_UNLOAD: + ieee80211_crypto_unregister(&wep); + return 0; + } + return EINVAL; +} + +static moduledata_t wep_mod = { + "wlan_wep", + wep_modevent, + 0 +}; +DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_wep, 1); +MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); +MODULE_DEPEND(wlan_wep, rc4, 1, 1, 1); diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c new file mode 100644 index 0000000..26ce11b --- /dev/null +++ b/sys/net80211/ieee80211_freebsd.c @@ -0,0 +1,338 @@ +/*- + * Copyright (c) 2003-2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 support (FreeBSD-specific code) + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters"); + +#ifdef IEEE80211_DEBUG +int ieee80211_debug = 0; +SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, + 0, "debugging printfs"); +#endif + +static int +ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) +{ + int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT; + int error; + + error = sysctl_handle_int(oidp, &inact, 0, req); + if (error || !req->newptr) + return error; + *(int *)arg1 = inact / IEEE80211_INACT_WAIT; + return 0; +} + +static int +ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211com *ic = arg1; + const char *name = ic->ic_ifp->if_xname; + + return SYSCTL_OUT(req, name, strlen(name)); +} + +void +ieee80211_sysctl_attach(struct ieee80211com *ic) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *oid; + char num[14]; /* sufficient for 32 bits */ + + 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", + __func__); + return; + } + sysctl_ctx_init(ctx); + snprintf(num, sizeof(num), "%u", ic->ic_vap); + oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), + OID_AUTO, num, CTLFLAG_RD, NULL, ""); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A", + "parent device"); +#ifdef IEEE80211_DEBUG + ic->ic_debug = ieee80211_debug; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "debug", CTLFLAG_RW, &ic->ic_debug, 0, + "control debugging printfs"); +#endif + /* XXX inherit from tunables */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0, + ieee80211_sysctl_inact, "I", + "station inactivity timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0, + ieee80211_sysctl_inact, "I", + "station inactivity probe timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0, + ieee80211_sysctl_inact, "I", + "station authentication timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0, + ieee80211_sysctl_inact, "I", + "station initial state timeout (sec)"); + ic->ic_sysctl = ctx; +} + +void +ieee80211_sysctl_detach(struct ieee80211com *ic) +{ + + if (ic->ic_sysctl != NULL) { + sysctl_ctx_free(ic->ic_sysctl); + ic->ic_sysctl = NULL; + } +} + +int +ieee80211_node_dectestref(struct ieee80211_node *ni) +{ + /* XXX need equivalent of atomic_dec_and_test */ + atomic_subtract_int(&ni->ni_refcnt, 1); + return atomic_cmpset_int(&ni->ni_refcnt, 0, 1); +} + +/* + * Allocate and setup a management frame of the specified + * size. We return the mbuf and a pointer to the start + * of the contiguous data area that's been reserved based + * on the packet length. The data area is forced to 32-bit + * alignment and the buffer length to a multiple of 4 bytes. + * This is done mainly so beacon frames (that require this) + * can use this interface too. + */ +struct mbuf * +ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen) +{ + struct mbuf *m; + u_int len; + + /* + * NB: we know the mbuf routines will align the data area + * so we don't need to do anything special. + */ + /* XXX 4-address frame? */ + len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); + KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len)); + if (len < MINCLSIZE) { + m = m_gethdr(M_NOWAIT, MT_HEADER); + /* + * Align the data in case additional headers are added. + * This should only happen when a WEP header is added + * which only happens for shared key authentication mgt + * frames which all fit in MHLEN. + */ + if (m != NULL) + MH_ALIGN(m, len); + } else + m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR); + if (m != NULL) { + m->m_data += sizeof(struct ieee80211_frame); + *frm = m->m_data; + } + return m; +} + +#include + +void +get_random_bytes(void *p, size_t n) +{ + u_int8_t *dp = p; + + while (n > 0) { + u_int32_t v = arc4random(); + size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n; + bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n); + dp += sizeof(u_int32_t), n -= nb; + } +} + +void +ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_join_event iev; + + if (ni == ic->ic_bss) { + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid); + rt_ieee80211msg(ifp, newassoc ? + RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, + &iev, sizeof(iev)); + if_link_state_change(ifp, LINK_STATE_UP); + } else if (newassoc) { + /* fire off wireless event only for new station */ + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); + rt_ieee80211msg(ifp, RTM_IEEE80211_JOIN, &iev, sizeof(iev)); + } +} + +void +ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_leave_event iev; + + if (ni == ic->ic_bss) { + rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); + if_link_state_change(ifp, LINK_STATE_DOWN); + } else { + /* fire off wireless event station leaving */ + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr); + rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev)); + } +} + +void +ieee80211_notify_scan_done(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: notify scan done\n", ic->ic_ifp->if_xname); + + /* dispatch wireless event indicating scan completed */ + rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); +} + +void +ieee80211_notify_replay_failure(struct ieee80211com *ic, + const struct ieee80211_frame *wh, const struct ieee80211_key *k, + u_int64_t rsc) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] %s replay detected \n", + ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name, + rsc, k->wk_keyrsc); + + if (ifp != NULL) { /* NB: for cipher test modules */ + struct ieee80211_replay_event iev; + + IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); + IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); + iev.iev_cipher = k->wk_cipher->ic_cipher; + iev.iev_keyix = k->wk_keyix; + iev.iev_keyrsc = k->wk_keyrsc; + iev.iev_rsc = rsc; + rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); + } +} + +void +ieee80211_notify_michael_failure(struct ieee80211com *ic, + const struct ieee80211_frame *wh, u_int keyix) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] Michael MIC verification failed \n", + ether_sprintf(wh->i_addr2), keyix); + ic->ic_stats.is_rx_tkipmic++; + + if (ifp != NULL) { /* NB: for cipher test modules */ + struct ieee80211_michael_event iev; + + IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); + IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); + iev.iev_cipher = IEEE80211_CIPHER_TKIP; + iev.iev_keyix = keyix; + rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev)); + } +} + +void +ieee80211_load_module(const char *modname) +{ + struct thread *td = curthread; + + if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) { + mtx_lock(&Giant); + (void) linker_load_module(modname, NULL, NULL, NULL, NULL); + mtx_unlock(&Giant); + } +} + +/* + * Module glue. + * + * NB: the module name is "wlan" for compatibility with NetBSD. + */ +static int +wlan_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("wlan: <802.11 Link Layer>\n"); + return 0; + case MOD_UNLOAD: + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_mod = { + "wlan", + wlan_modevent, + 0 +}; +DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan, 1); +MODULE_DEPEND(wlan, ether, 1, 1, 1); diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h new file mode 100644 index 0000000..bb0e912 --- /dev/null +++ b/sys/net80211/ieee80211_freebsd.h @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2003, 2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_FREEBSD_H_ +#define _NET80211_IEEE80211_FREEBSD_H_ + +/* + * 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. + */ +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) +#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock) +#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_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) + +/* + * Node table scangen locking definitions. + */ +typedef struct mtx ieee80211_scan_lock_t; +#define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \ + mtx_init(&(_nt)->nt_scanlock, _name, "802.11 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) + +/* + * Per-node power-save queue definitions. + */ +#define IEEE80211_NODE_SAVEQ_INIT(_ni, _name) do { \ + mtx_init(&(_ni)->ni_savedq.ifq_mtx, _name, "802.11 ps queue", MTX_DEF);\ + (_ni)->ni_savedq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \ +} while (0) +#define IEEE80211_NODE_SAVEQ_DESTROY(_ni) \ + mtx_destroy(&(_ni)->ni_savedq.ifq_mtx) +#define IEEE80211_NODE_SAVEQ_QLEN(_ni) \ + _IF_QLEN(&(_ni)->ni_savedq) +#define IEEE80211_NODE_SAVEQ_LOCK(_ni) do { \ + IF_LOCK(&(_ni)->ni_savedq); \ +} while (0) +#define IEEE80211_NODE_SAVEQ_UNLOCK(_ni) do { \ + IF_UNLOCK(&(_ni)->ni_savedq); \ +} while (0) +#define IEEE80211_NODE_SAVEQ_DEQUEUE(_ni, _m, _qlen) do { \ + IEEE80211_NODE_SAVEQ_LOCK(_ni); \ + _IF_DEQUEUE(&(_ni)->ni_savedq, _m); \ + (_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni); \ + IEEE80211_NODE_SAVEQ_UNLOCK(_ni); \ +} while (0) +#define IEEE80211_NODE_SAVEQ_DRAIN(_ni, _qlen) do { \ + IEEE80211_NODE_SAVEQ_LOCK(_ni); \ + (_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni); \ + _IF_DRAIN(&(_ni)->ni_savedq); \ + IEEE80211_NODE_SAVEQ_UNLOCK(_ni); \ +} while (0) +/* XXX could be optimized */ +#define _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(_ni, _m) do { \ + _IF_DEQUEUE(&(_ni)->ni_savedq, m); \ +} while (0) +#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _m, _qlen, _age) do {\ + (_m)->m_nextpkt = NULL; \ + if ((_ni)->ni_savedq.ifq_tail != NULL) { \ + _age -= M_AGE_GET((_ni)->ni_savedq.ifq_tail); \ + (_ni)->ni_savedq.ifq_tail->m_nextpkt = (_m); \ + } else { \ + (_ni)->ni_savedq.ifq_head = (_m); \ + } \ + M_AGE_SET(_m, _age); \ + (_ni)->ni_savedq.ifq_tail = (_m); \ + (_qlen) = ++(_ni)->ni_savedq.ifq_len; \ +} while (0) + +/* + * 802.1x MAC ACL database locking definitions. + */ +typedef struct mtx acl_lock_t; +#define ACL_LOCK_INIT(_as, _name) \ + mtx_init(&(_as)->as_lock, _name, "802.11 ACL", MTX_DEF) +#define ACL_LOCK_DESTROY(_as) mtx_destroy(&(_as)->as_lock) +#define ACL_LOCK(_as) mtx_lock(&(_as)->as_lock) +#define ACL_UNLOCK(_as) mtx_unlock(&(_as)->as_lock) +#define ACL_LOCK_ASSERT(_as) \ + mtx_assert((&(_as)->as_lock), MA_OWNED) + +/* + * Node reference counting definitions. + * + * ieee80211_node_initref initialize the reference count to 1 + * ieee80211_node_incref add a reference + * ieee80211_node_decref remove a reference + * ieee80211_node_dectestref remove a reference and return 1 if this + * is the last reference, otherwise 0 + * ieee80211_node_refcnt reference count for printing (only) + */ +#include + +#define ieee80211_node_initref(_ni) \ + do { ((_ni)->ni_refcnt = 1); } while (0) +#define ieee80211_node_incref(_ni) \ + atomic_add_int(&(_ni)->ni_refcnt, 1) +#define ieee80211_node_decref(_ni) \ + atomic_subtract_int(&(_ni)->ni_refcnt, 1) +struct ieee80211_node; +extern int ieee80211_node_dectestref(struct ieee80211_node *ni); +#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt + +extern struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen); +#define M_LINK0 M_PROTO1 /* WEP requested */ +#define M_PWR_SAV M_PROTO4 /* bypass PS handling */ +/* + * Encode WME access control bits in the PROTO flags. + * This is safe since it's passed directly in to the + * driver and there's no chance someone else will clobber + * them on us. + */ +#define M_WME_AC_MASK (M_PROTO2|M_PROTO3) +/* XXX 5 is wrong if M_PROTO* are redefined */ +#define M_WME_AC_SHIFT 5 + +#define M_WME_SETAC(m, ac) \ + ((m)->m_flags = ((m)->m_flags &~ M_WME_AC_MASK) | \ + ((ac) << M_WME_AC_SHIFT)) +#define M_WME_GETAC(m) (((m)->m_flags >> M_WME_AC_SHIFT) & 0x3) + +/* + * Mbufs on the power save queue are tagged with an age and + * timed out. We reuse the hardware checksum field in the + * mbuf packet header to store this data. + */ +#define M_AGE_SET(m,v) (m->m_pkthdr.csum_data = v) +#define M_AGE_GET(m) (m->m_pkthdr.csum_data) +#define M_AGE_SUB(m,adj) (m->m_pkthdr.csum_data -= adj) + +extern void get_random_bytes(void *, size_t); + +struct ieee80211com; + +void ieee80211_sysctl_attach(struct ieee80211com *); +void ieee80211_sysctl_detach(struct ieee80211com *); + +void ieee80211_load_module(const char *); + +/* XXX this stuff belongs elsewhere */ +/* + * Message formats for messages from the net80211 layer to user + * applications via the routing socket. These messages are appended + * to an if_announcemsghdr structure. + */ +struct ieee80211_join_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_leave_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_replay_event { + uint8_t iev_src[6]; /* src MAC */ + uint8_t iev_dst[6]; /* dst MAC */ + uint8_t iev_cipher; /* cipher type */ + uint8_t iev_keyix; /* key id/index */ + uint64_t iev_keyrsc; /* RSC from key */ + uint64_t iev_rsc; /* RSC from frame */ +}; + +struct ieee80211_michael_event { + uint8_t iev_src[6]; /* src MAC */ + uint8_t iev_dst[6]; /* dst MAC */ + uint8_t iev_cipher; /* cipher type */ + uint8_t iev_keyix; /* key id/index */ +}; + +#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */ +#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */ +#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */ +#define RTM_IEEE80211_JOIN 103 /* station join (ap mode) */ +#define RTM_IEEE80211_LEAVE 104 /* station leave (ap mode) */ +#define RTM_IEEE80211_SCAN 105 /* scan complete, results available */ +#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */ +#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */ + +#endif /* _NET80211_IEEE80211_FREEBSD_H_ */ diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 74aa1c4..c3f8d03 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,38 +33,85 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_inet.h" - #include -#include +#include #include #include -#include -#include -#include #include -#include -#include -#include -#include - -#include +#include + #include -#include #include -#include #include #include +#include #include #include -#ifdef INET -#include -#include -#endif +#ifdef IEEE80211_DEBUG +#include + +/* + * Decide if a received management frame should be + * printed when debugging is enabled. This filters some + * of the less interesting frames that come frequently + * (e.g. beacons). + */ +static __inline int +doprint(struct ieee80211com *ic, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return (ic->ic_flags & IEEE80211_F_SCAN); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return (ic->ic_opmode == IEEE80211_M_IBSS); + } + return 1; +} + +/* + * Emit a debug message about discarding a frame or information + * element. One format is for extracting the mac address from + * the frame header; the other is for when a header is not + * available or otherwise appropriate. + */ +#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \ + if ((_ic)->ic_debug & (_m)) \ + ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\ +} while (0) +#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \ + if ((_ic)->ic_debug & (_m)) \ + ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\ +} while (0) +#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \ + if ((_ic)->ic_debug & (_m)) \ + ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\ +} while (0) + +static const u_int8_t *ieee80211_getbssid(struct ieee80211com *, + const struct ieee80211_frame *); +static void ieee80211_discard_frame(struct ieee80211com *, + const struct ieee80211_frame *, const char *type, const char *fmt, ...); +static void ieee80211_discard_ie(struct ieee80211com *, + const struct ieee80211_frame *, const char *type, const char *fmt, ...); +static void ieee80211_discard_mac(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type, + const char *fmt, ...); +#else +#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) +#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) +#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) +#endif /* IEEE80211_DEBUG */ + +static struct mbuf *ieee80211_defrag(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); +static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *); +static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); +static void ieee80211_recv_pspoll(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); /* * Process a received frame. The node associated with the sender @@ -77,19 +124,22 @@ __FBSDID("$FreeBSD$"); * by the 802.11 layer. */ void -ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, - int rssi, u_int32_t rstamp) +ieee80211_input(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int rssi, u_int32_t rstamp) { - struct ieee80211com *ic = (void *)ifp; +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_frame *wh; + struct ieee80211_key *key; struct ether_header *eh; - struct mbuf *m1; - int len; + int len, hdrsize, off; u_int8_t dir, type, subtype; u_int8_t *bssid; u_int16_t rxseq; KASSERT(ni != NULL, ("null node")); + ni->ni_inact = ni->ni_inact_reload; /* trim CRC here so WEP can find its own CRC at the end of packet. */ if (m->m_flags & M_HASFCS) { @@ -106,38 +156,40 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, if (ic->ic_opmode == IEEE80211_M_MONITOR) goto out; + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; + goto out; + } + /* + * Bit of a cheat here, we use a pointer for a 3-address + * frame format but don't reference fields past outside + * ieee80211_frame_min w/o first validating the data is + * present. + */ wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "receive packet with wrong version: %x\n", - wh->i_fc[0]); + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); ic->ic_stats.is_rx_badversion++; goto err; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - /* - * NB: We are not yet prepared to handle control frames, - * but permitting drivers to send them to us allows - * them to go through bpf tapping at the 802.11 layer. - */ - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { - /* XXX statistic */ - IEEE80211_DPRINTF2(("%s: frame too short, len %u\n", - __func__, m->m_pkthdr.len)); - ic->ic_stats.is_rx_tooshort++; - goto out; /* XXX */ - } - if (ic->ic_state != IEEE80211_S_SCAN) { + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { switch (ic->ic_opmode) { case IEEE80211_M_STA: - if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { + bssid = wh->i_addr2; + if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { /* not interested in */ - IEEE80211_DPRINTF2(("%s: discard frame from " - "bss %s\n", __func__, - ether_sprintf(wh->i_addr2))); + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); ic->ic_stats.is_rx_wrongbss++; goto out; } @@ -145,43 +197,119 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: case IEEE80211_M_HOSTAP: - if (dir == IEEE80211_FC1_DIR_NODS) - bssid = wh->i_addr3; - else + if (dir != IEEE80211_FC1_DIR_NODS) + bssid = wh->i_addr1; + else if (type == IEEE80211_FC0_TYPE_CTL) bssid = wh->i_addr1; + else { + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_ANY, ni->ni_macaddr, + NULL, "too short (2): len %u", + m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; + goto out; + } + bssid = wh->i_addr3; + } + if (type != IEEE80211_FC0_TYPE_DATA) + break; + /* + * Data frame, validate the bssid. + */ if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { /* not interested in */ - IEEE80211_DPRINTF2(("%s: discard frame from " - "bss %s\n", __func__, - ether_sprintf(bssid))); + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + bssid, NULL, "%s", "not to bss"); ic->ic_stats.is_rx_wrongbss++; goto out; } + /* + * For adhoc mode we cons up a node when it doesn't + * exist. This should probably done after an ACL check. + */ + if (ni == ic->ic_bss && + ic->ic_opmode != IEEE80211_M_HOSTAP) { + /* + * Fake up a node for this newly + * discovered member of the IBSS. + */ + ni = ieee80211_fakeup_adhoc_node(ic->ic_sta, + type == IEEE80211_FC0_TYPE_CTL ? + wh->i_addr1 : wh->i_addr2); + if (ni == NULL) { + /* NB: stat kept for alloc failure */ + goto err; + } + } break; - case IEEE80211_M_MONITOR: - goto out; default: - /* XXX catch bad values */ - break; + goto out; } ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; - rxseq = ni->ni_rxseq; - ni->ni_rxseq = - le16toh(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; - /* TODO: fragment */ - if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && - rxseq == ni->ni_rxseq) { - /* duplicate, silently discarded */ - ic->ic_stats.is_rx_dup++; /* XXX per-station stat */ - goto out; + if (HAS_SEQ(type)) { + u_int8_t tid; + if (IEEE80211_QOS_HAS_SEQ(wh)) { + tid = ((struct ieee80211_qosframe *)wh)-> + i_qos[0] & IEEE80211_QOS_TID; + if (tid >= WME_AC_VI) + ic->ic_wme.wme_hipri_traffic++; + tid++; + } else + tid = 0; + rxseq = le16toh(*(u_int16_t *)wh->i_seq); + if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { + /* duplicate, discard */ + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + bssid, "duplicate", + "seqno <%u,%u> fragno <%u,%u> tid %u", + rxseq >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseqs[tid] >> + IEEE80211_SEQ_SEQ_SHIFT, + rxseq & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxseqs[tid] & + IEEE80211_SEQ_FRAG_MASK, + tid); + ic->ic_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + goto out; + } + ni->ni_rxseqs[tid] = rxseq; } - ni->ni_inact = 0; } switch (type) { case IEEE80211_FC0_TYPE_DATA: + hdrsize = ieee80211_hdrsize(wh); + if (ic->ic_flags & IEEE80211_F_DATAPAD) + hdrsize = roundup(hdrsize, sizeof(u_int32_t)); + if (m->m_len < hdrsize && + (m = m_pullup(m, hdrsize)) == NULL) { + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, "data", "too short: len %u, expecting %u", + m->m_pkthdr.len, hdrsize); + ic->ic_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + if (subtype & IEEE80211_FC0_SUBTYPE_QOS) { + /* XXX discard if node w/o IEEE80211_NODE_QOS? */ + /* + * Strip QoS control and any padding so only a + * stock 802.11 header is at the front. + */ + /* XXX 4-address QoS frame */ + off = hdrsize - sizeof(struct ieee80211_frame); + ovbcopy(mtod(m, u_int8_t *), mtod(m, u_int8_t *) + off, + hdrsize - off); + m_adj(m, off); + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS; + } else { + /* XXX copy up for 4-address frames w/ padding */ + } switch (ic->ic_opmode) { case IEEE80211_M_STA: if (dir != IEEE80211_FC1_DIR_FROMDS) { @@ -197,6 +325,8 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, * It should be silently discarded for * SIMPLEX interface. */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "multicast echo"); ic->ic_stats.is_rx_mcastecho++; goto out; } @@ -207,6 +337,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, ic->ic_stats.is_rx_wrongdir++; goto out; } + /* XXX no power-save support */ break; case IEEE80211_M_HOSTAP: if (dir != IEEE80211_FC1_DIR_TODS) { @@ -215,62 +346,156 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, } /* check if source STA is associated */ if (ni == ic->ic_bss) { - IEEE80211_DPRINTF(("%s: data from unknown src " - "%s\n", __func__, - ether_sprintf(wh->i_addr2))); + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown src"); /* NB: caller deals with reference */ - ni = ieee80211_dup_bss(ic, wh->i_addr2); + ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2); if (ni != NULL) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_NOT_AUTHED); - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); } ic->ic_stats.is_rx_notassoc++; goto err; } if (ni->ni_associd == 0) { - IEEE80211_DPRINTF(("ieee80211_input: " - "data from unassoc src %s\n", - ether_sprintf(wh->i_addr2))); + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unassoc src"); IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_NOT_ASSOCED); - ieee80211_unref_node(&ni); ic->ic_stats.is_rx_notassoc++; goto err; } + + /* + * Check for power save state change. + */ + if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ + (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) + ieee80211_node_pwrsave(ni, + wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); break; - case IEEE80211_M_MONITOR: - break; + default: + /* XXX here to keep compiler happy */ + goto out; } + + /* + * Handle privacy requirements. Note that we + * must not be preempted from here until after + * we (potentially) call ieee80211_crypto_demic; + * otherwise we may violate assumptions in the + * crypto cipher modules used to do delayed update + * of replay sequence numbers. + */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - if (ic->ic_flags & IEEE80211_F_WEPON) { - m = ieee80211_wep_crypt(ifp, m, 0); - if (m == NULL) { - ic->ic_stats.is_rx_wepfail++; - goto err; - } - wh = mtod(m, struct ieee80211_frame *); - } else { - ic->ic_stats.is_rx_nowep++; + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "WEP", "%s", "PRIVACY off"); + ic->ic_stats.is_rx_noprivacy++; + IEEE80211_NODE_STAT(ni, rx_noprivacy); + goto out; + } + key = ieee80211_crypto_decap(ic, ni, m); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + IEEE80211_NODE_STAT(ni, rx_wepfail); + goto out; + } + wh = mtod(m, struct ieee80211_frame *); + } else { + key = NULL; + } + + /* + * Next up, any fragmentation. + */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + m = ieee80211_defrag(ic, ni, m); + if (m == NULL) { + /* Fragment dropped or frame not complete yet */ goto out; } } + wh = NULL; /* no longer valid, catch any uses */ + + /* + * Next strip any MSDU crypto bits. + */ + if (key != NULL && !ieee80211_crypto_demic(ic, key, m)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "demic error"); + IEEE80211_NODE_STAT(ni, rx_demicfail); + goto out; + } + /* copy to listener after decrypt */ if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); - m = ieee80211_decap(ifp, m); + + /* + * Finally, strip the 802.11 header. + */ + m = ieee80211_decap(ic, m); if (m == NULL) { + /* don't count Null data frames as errors */ + if (subtype == IEEE80211_FC0_SUBTYPE_NODATA) + goto out; + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + ni->ni_macaddr, "data", "%s", "decap error"); ic->ic_stats.is_rx_decap++; + IEEE80211_NODE_STAT(ni, rx_decap); goto err; } + eh = mtod(m, struct ether_header *); + if (!ieee80211_node_is_authorized(ni)) { + /* + * Deny any non-PAE frames received prior to + * authorization. For open/shared-key + * authentication the port is mark authorized + * after authentication completes. For 802.1x + * the port is not marked authorized by the + * authenticator until the handshake has completed. + */ + if (eh->ether_type != htons(ETHERTYPE_PAE)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT, + eh->ether_shost, "data", + "unauthorized port: ether type 0x%x len %u", + eh->ether_type, m->m_pkthdr.len); + ic->ic_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && + key == NULL && + eh->ether_type != htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + ic->ic_stats.is_rx_unencrypted++; + IEEE80211_NODE_STAT(ni, rx_unencrypted); + goto out; + } + } ifp->if_ipackets++; + IEEE80211_NODE_STAT(ni, rx_data); + IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); /* perform as a bridge within the AP */ - m1 = NULL; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - eh = mtod(m, struct ether_header *); + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) { + struct mbuf *m1 = NULL; + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { m1 = m_copypacket(m, M_DONTWAIT); if (m1 == NULL) @@ -278,13 +503,19 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, else m1->m_flags |= M_MCAST; } else { - ni = ieee80211_find_node(ic, eh->ether_dhost); - if (ni != NULL) { - if (ni->ni_associd != 0) { + /* XXX this dups work done in ieee80211_encap */ + /* check if destination is associated */ + struct ieee80211_node *ni1 = + ieee80211_find_node(ic->ic_sta, + eh->ether_dhost); + if (ni1 != NULL) { + /* XXX check if authorized */ + if (ni1->ni_associd != 0) { m1 = m; m = NULL; } - ieee80211_free_node(ic, ni); + /* XXX statistic? */ + ieee80211_free_node(ni1); } } if (m1 != NULL) { @@ -295,61 +526,65 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, ifp->if_obytes += len; } } - if (m != NULL) + if (m != NULL) { + if (ni->ni_vlan != 0) { + /* attach vlan tag */ + /* XXX goto err? */ + VLAN_INPUT_TAG(ifp, m, ni->ni_vlan, goto out); + } (*ifp->if_input)(ifp, m); + } return; case IEEE80211_FC0_TYPE_MGT: + IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { ic->ic_stats.is_rx_wrongdir++; goto err; } - if (ic->ic_opmode == IEEE80211_M_AHDEMO) { - ic->ic_stats.is_rx_ahdemo_mgt++; + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, + ni->ni_macaddr, "mgt", "too short: len %u", + m->m_pkthdr.len); + ic->ic_stats.is_rx_tooshort++; goto out; } - subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - /* drop frames without interest */ - if (ic->ic_state == IEEE80211_S_SCAN) { - if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && - subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) { - ic->ic_stats.is_rx_mgtdiscard++; +#ifdef IEEE80211_DEBUG + if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || + ieee80211_msg_dumppkts(ic)) { + if_printf(ic->ic_ifp, "received %s from %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; } - } else { - if (ic->ic_opmode != IEEE80211_M_IBSS && - subtype == IEEE80211_FC0_SUBTYPE_BEACON) { - ic->ic_stats.is_rx_mgtdiscard++; + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT, + wh, "mgt", "%s", "WEP set but PRIVACY off"); + ic->ic_stats.is_rx_noprivacy++; goto out; } - } - - if (ifp->if_flags & IFF_DEBUG) { - /* avoid to print too many frames */ - int doprint = 0; - - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_BEACON: - if (ic->ic_state == IEEE80211_S_SCAN) - doprint = 1; - break; - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - if (ic->ic_opmode == IEEE80211_M_IBSS) - doprint = 1; - break; - default: - doprint = 1; - break; + key = ieee80211_crypto_decap(ic, ni, m); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; } -#ifdef IEEE80211_DEBUG - doprint += ieee80211_debug; -#endif - if (doprint) - if_printf(ifp, "received %s from %s rssi %d\n", - ieee80211_mgt_subtype_name[subtype - >> IEEE80211_FC0_SUBTYPE_SHIFT], - ether_sprintf(wh->i_addr2), rssi); } if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m); @@ -358,10 +593,19 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, return; case IEEE80211_FC0_TYPE_CTL: + IEEE80211_NODE_STAT(ni, rx_ctrl); ic->ic_stats.is_rx_ctl++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PS_POLL: + ieee80211_recv_pspoll(ic, ni, m); + break; + } + } goto out; default: - IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, NULL, "bad frame type 0x%x", type); /* should not come here */ break; } @@ -373,19 +617,108 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, bpf_mtap(ic->ic_rawbpf, m); m_freem(m); } +#undef SEQ_LEQ } -struct mbuf * -ieee80211_decap(struct ifnet *ifp, struct mbuf *m) +/* + * This function reassemble fragments. + */ +static struct mbuf * +ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m) { + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + struct ieee80211_frame *lwh; + u_int16_t rxseq; + u_int8_t fragno; + u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + struct mbuf *mfrag; + + KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); + + rxseq = le16toh(*(u_int16_t *)wh->i_seq); + fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; + + /* Quick way out, if there's nothing to defragment */ + if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL) + return m; + + /* + * Remove frag to insure it doesn't get reaped by timer. + */ + if (ni->ni_table == NULL) { + /* + * Should never happen. If the node is orphaned (not in + * the table) then input packets should not reach here. + * Otherwise, a concurrent request that yanks the table + * should be blocked by other interlocking and/or by first + * shutting the driver down. Regardless, be defensive + * here and just bail + */ + /* XXX need msg+stat */ + m_freem(m); + return NULL; + } + IEEE80211_NODE_LOCK(ni->ni_table); + mfrag = ni->ni_rxfrag[0]; + ni->ni_rxfrag[0] = NULL; + IEEE80211_NODE_UNLOCK(ni->ni_table); + + /* + * Validate new fragment is in order and + * related to the previous ones. + */ + if (mfrag != NULL) { + u_int16_t last_rxseq; + + lwh = mtod(mfrag, struct ieee80211_frame *); + last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq); + /* NB: check seq # and frag together */ + if (rxseq != last_rxseq+1 || + !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || + !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) { + /* + * Unrelated fragment or no space for it, + * clear current fragments. + */ + m_freem(mfrag); + mfrag = NULL; + } + } + + if (mfrag == NULL) { + if (fragno != 0) { /* !first fragment, discard */ + IEEE80211_NODE_STAT(ni, rx_defrag); + m_freem(m); + return NULL; + } + mfrag = m; + } else { /* concatenate */ + m_cat(mfrag, m); + /* NB: m_cat doesn't update the packet header */ + mfrag->m_pkthdr.len += m->m_pkthdr.len; + /* track last seqnum and fragno */ + lwh = mtod(mfrag, struct ieee80211_frame *); + *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq; + } + if (more_frag) { /* more to come, save */ + ni->ni_rxfrag[0] = mfrag; + mfrag = NULL; + } + return mfrag; +} + +static struct mbuf * +ieee80211_decap(struct ieee80211com *ic, struct mbuf *m) +{ + struct ieee80211_frame wh; /* NB: QoS stripped above */ struct ether_header *eh; - struct ieee80211_frame wh; struct llc *llc; - if (m->m_len < sizeof(wh) + sizeof(*llc)) { - m = m_pullup(m, sizeof(wh) + sizeof(*llc)); - if (m == NULL) - return NULL; + if (m->m_len < sizeof(wh) + sizeof(*llc) && + (m = m_pullup(m, sizeof(wh) + sizeof(*llc))) == NULL) { + /* XXX stat, msg */ + return NULL; } memcpy(&wh, mtod(m, caddr_t), sizeof(wh)); llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh)); @@ -413,7 +746,8 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m) break; case IEEE80211_FC1_DIR_DSTODS: /* not yet supported */ - IEEE80211_DPRINTF(("%s: DS to DS\n", __func__)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + &wh, "data", "%s", "DS to DS not supported"); m_freem(m); return NULL; } @@ -482,72 +816,860 @@ static int ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *rates, u_int8_t *xrates, int flags) { - struct ieee80211_rateset *rs = &ni->ni_rates; + struct ieee80211_rateset *rs = &ni->ni_rates; + + memset(rs, 0, sizeof(*rs)); + rs->rs_nrates = rates[1]; + memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); + if (xrates != NULL) { + u_int8_t nxrates; + /* + * Tack on 11g extended supported rate element. + */ + 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++; + } + memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); + rs->rs_nrates += nxrates; + } + return ieee80211_fix_rate(ic, ni, flags); +} + +static void +ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, + struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, + u_int16_t status) +{ + + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: + if (ic->ic_state != IEEE80211_S_RUN || + seq != IEEE80211_AUTH_OPEN_REQUEST) { + ic->ic_stats.is_rx_bad_auth++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_AUTH, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + + case IEEE80211_M_AHDEMO: + /* should not come here */ + break; + + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN || + seq != 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 + (void) ieee80211_ref_node(ni); + ni->ni_inact_reload = ic->ic_inact_auth; + 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)); + 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++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_MONITOR: + break; + } +} + +static int +alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (ni->ni_challenge == NULL) + MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN, + M_DEVBUF, 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)); + /* XXX statistic */ + } + return (ni->ni_challenge != NULL); +} + +/* XXX TODO: add statistics */ +static void +ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, + u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, + u_int32_t rstamp, u_int16_t seq, u_int16_t status) +{ + u_int8_t *challenge; + int allocbs, estatus; + + /* + * NB: this can happen as we allow pre-shared key + * authentication to be enabled w/o wep being turned + * on so that configuration of these can be done + * in any order. It may be better to enforce the + * ordering in which case this check would just be + * for sanity/consistency. + */ + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", " PRIVACY is disabled"); + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + /* + * Pre-shared key authentication is evil; accept + * it only if explicitly configured (it is supported + * mainly for compatibility with clients like OS X). + */ + if (ni->ni_authmode != IEEE80211_AUTH_AUTO && + ni->ni_authmode != IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad sta auth mode %u", ni->ni_authmode); + ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + + challenge = NULL; + if (frm + 1 < efrm) { + if ((frm[1] + 2) > (efrm - frm)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "ie %d/%d too long", + frm[0], (frm[1] + 2) - (efrm - frm)); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (*frm == IEEE80211_ELEMID_CHALLENGE) + challenge = frm; + frm += frm[1] + 2; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_CHALLENGE: + case IEEE80211_AUTH_SHARED_RESPONSE: + if (challenge == NULL) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", "no challenge"); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad challenge len %d", challenge[1]); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + switch (ic->ic_opmode) { + case IEEE80211_M_MONITOR: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad operating mode %u", ic->ic_opmode); + return; + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad state %u", ic->ic_state); + estatus = IEEE80211_STATUS_ALG; /* XXX */ + goto bad; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_REQUEST: + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2); + if (ni == NULL) { + /* NB: no way to return an error */ + return; + } + allocbs = 1; + } else { + (void) ieee80211_ref_node(ni); + allocbs = 0; + } + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + if (!alloc_challenge(ic, ni)) { + /* NB: don't return error so they rexmit */ + return; + } + get_random_bytes(ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%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; + } + ni->ni_inact_reload = ic->ic_inact_auth; + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%s] station authenticated (shared key)\n", + ether_sprintf(ni->ni_macaddr)); + break; + default: + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad seq %d", seq); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_SEQUENCE; + goto bad; + } + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH) + return; + switch (seq) { + case IEEE80211_AUTH_SHARED_PASS: + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + if (status != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + "[%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, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_AUTH_SHARED_CHALLENGE: + if (!alloc_challenge(ic, ni)) + return; + /* XXX could optimize by passing recvd challenge */ + memcpy(ni->ni_challenge, &challenge[2], challenge[1]); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + default: + IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH, + wh, "shared key auth", "bad seq %d", seq); + ic->ic_stats.is_rx_bad_auth++; + return; + } + break; + } + return; +bad: + /* + * Send an error response; but only when operating as an AP. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX hack to workaround calling convention */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (estatus<<16)); + } +} + +/* Verify the existence and length of __elem or get out. */ +#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ + if ((__elem) == NULL) { \ + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ + wh, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + "%s", "no " #__elem ); \ + ic->ic_stats.is_rx_elem_missing++; \ + return; \ + } \ + if ((__elem)[1] > (__maxlen)) { \ + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ + wh, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + "bad " #__elem " len %d", (__elem)[1]); \ + ic->ic_stats.is_rx_elem_toobig++; \ + return; \ + } \ +} while (0) + +#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ + if ((_len) < (_minlen)) { \ + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ + wh, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + "%s", "ie too short"); \ + ic->ic_stats.is_rx_elem_toosmall++; \ + return; \ + } \ +} while (0) + +#ifdef IEEE80211_DEBUG +static void +ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, + u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_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) \ + ((u_int16_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8))) +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8) | \ + (((const u_int8_t *)(p))[2] << 16) | \ + (((const u_int8_t *)(p))[3] << 24))) + +static int __inline +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int __inline +iswmeoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); +} + +static int __inline +iswmeparam(const u_int8_t *frm) +{ + return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && + frm[6] == WME_PARAM_OUI_SUBTYPE; +} + +static int __inline +iswmeinfo(const u_int8_t *frm) +{ + return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && + frm[6] == WME_INFO_OUI_SUBTYPE; +} + +static int __inline +isatherosoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); +} + +/* + * Convert a WPA cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +wpa_cipher(u_int8_t *sel, u_int8_t *keylen) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_CSE_NULL): + return 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(u_int8_t *sel) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_ASE_8021X_UNSPEC): + return 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, u_int8_t *frm, + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) +{ + u_int8_t len = frm[1]; + u_int32_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. + */ + KASSERT(ic->ic_flags & IEEE80211_F_WPA1, + ("not WPA, flags 0x%x", ic->ic_flags)); + if (len < 14) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "too short, len %u", len); + return IEEE80211_REASON_IE_INVALID; + } + frm += 6, len -= 4; /* NB: len is payload only */ + /* NB: iswapoui already validated the OUI and type */ + w = LE_READ_2(frm); + if (w != WPA_VERSION) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + /* multicast/group cipher */ + w = wpa_cipher(frm, &rsn->rsn_mcastkeylen); + if (w != rsn->rsn_mcastcipher) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "mcast cipher mismatch; got %u, expected %u", + w, rsn->rsn_mcastcipher); + return IEEE80211_REASON_IE_INVALID; + } + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "ucast cipher data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<rsn_ucastkeylen); + frm += 4, len -= 4; + } + w &= rsn->rsn_ucastcipherset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "%s", "ucast cipher set empty"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & (1<rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "key mgmt alg data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= wpa_keymgmt(frm); + frm += 4, len -= 4; + } + w &= rsn->rsn_keymgmtset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "%s", "no acceptable key mgmt alg"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & WPA_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + if (len > 2) /* optional capabilities */ + rsn->rsn_caps = LE_READ_2(frm); + + return 0; +} + +/* + * Convert an RSN cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +rsn_cipher(u_int8_t *sel, u_int8_t *keylen) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_CSE_NULL): + return 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(u_int8_t *sel) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_ASE_8021X_UNSPEC): + return 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, u_int8_t *frm, + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) +{ + u_int8_t len = frm[1]; + u_int32_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. + */ + KASSERT(ic->ic_flags & IEEE80211_F_WPA2, + ("not RSN, flags 0x%x", ic->ic_flags)); + if (len < 10) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "too short, len %u", len); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2; + w = LE_READ_2(frm); + if (w != RSN_VERSION) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + /* multicast/group cipher */ + w = rsn_cipher(frm, &rsn->rsn_mcastkeylen); + if (w != rsn->rsn_mcastcipher) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "mcast cipher mismatch; got %u, expected %u", + w, rsn->rsn_mcastcipher); + return IEEE80211_REASON_IE_INVALID; + } + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "ucast cipher data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<rsn_ucastkeylen); + frm += 4, len -= 4; + } + w &= rsn->rsn_ucastcipherset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "%s", "ucast cipher set empty"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & (1<rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "key mgmt alg data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= rsn_keymgmt(frm); + frm += 4, len -= 4; + } + w &= rsn->rsn_keymgmtset; + if (w == 0) { + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "%s", "no acceptable key mgmt alg"); + return IEEE80211_REASON_IE_INVALID; + } + if (w & RSN_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; + + /* optional RSN capabilities */ + if (len > 2) + rsn->rsn_caps = LE_READ_2(frm); + /* XXXPMKID */ + + return 0; +} + +static int +ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_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 0; + } + 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 void +ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) +{ + u_int ielen = ie[1]+2; + /* + * Record information element for later use. + */ + if (*iep == NULL || (*iep)[1] != ie[1]) { + if (*iep != NULL) + FREE(*iep, M_DEVBUF); + MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); + } + if (*iep != NULL) + memcpy(*iep, ie, ielen); + /* XXX note failure */ +} + +#ifdef IEEE80211_DEBUG +static void +dump_probe_beacon(u_int8_t subtype, int isnew, + const u_int8_t mac[IEEE80211_ADDR_LEN], + u_int8_t chan, u_int8_t bchan, u_int16_t capinfo, u_int16_t bintval, + u_int8_t erp, u_int8_t *ssid, u_int8_t *country) +{ + printf("[%s] %s%s on chan %u (bss chan %u) ", + ether_sprintf(mac), isnew ? "new " : "", + ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], + chan, bchan); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf("\n"); - memset(rs, 0, sizeof(*rs)); - rs->rs_nrates = rates[1]; - memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); - if (xrates != NULL) { - u_int8_t nxrates; - /* - * Tack on 11g extended supported rate element. - */ - nxrates = xrates[1]; - if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { - nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; - IEEE80211_DPRINTF(("%s: extended rate set too large;" - " only using %u of %u rates\n", - __func__, nxrates, xrates[1])); - ic->ic_stats.is_rx_rstoobig++; + if (isnew) { + printf("[%s] caps 0x%x bintval %u erp 0x%x", + ether_sprintf(mac), capinfo, bintval, erp); + if (country) { +#ifdef __FreeBSD__ + printf(" country info %*D", country[1], country+2, " "); +#else + int i; + printf(" country info"); + for (i = 0; i < country[1]; i++) + printf(" %02x", country[i+2]); +#endif } - memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); - rs->rs_nrates += nxrates; + printf("\n"); } - return ieee80211_fix_rate(ic, ni, flags); } - -/* Verify the existence and length of __elem or get out. */ -#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ - if ((__elem) == NULL) { \ - IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n", \ - __func__, ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT])); \ - ic->ic_stats.is_rx_elem_missing++; \ - return; \ - } \ - if ((__elem)[1] > (__maxlen)) { \ - IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s " \ - "frame from %s\n", __func__, (__elem)[1], \ - ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - ether_sprintf(wh->i_addr2))); \ - ic->ic_stats.is_rx_elem_toobig++; \ - return; \ - } \ -} while (0) - -#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ - if ((_len) < (_minlen)) { \ - IEEE80211_DPRINTF(("%s: %s frame too short from %s\n", \ - __func__, \ - ieee80211_mgt_subtype_name[subtype >> \ - IEEE80211_FC0_SUBTYPE_SHIFT], \ - ether_sprintf(wh->i_addr2))); \ - ic->ic_stats.is_rx_elem_toosmall++; \ - return; \ - } \ -} while (0) +#endif /* IEEE80211_DEBUG */ void ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp) { - struct ifnet *ifp = &ic->ic_if; +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) struct ieee80211_frame *wh; u_int8_t *frm, *efrm; - u_int8_t *ssid, *rates, *xrates; - int reassoc, resp, newassoc, allocbs; + u_int8_t *ssid, *rates, *xrates, *wpa, *wme; + int reassoc, resp, allocbs; wh = mtod(m0, struct ieee80211_frame *); frm = (u_int8_t *)&wh[1]; @@ -555,18 +1677,34 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { - u_int8_t *tstamp, *bintval, *capinfo, *country; + u_int8_t *tstamp, *country; u_int8_t chan, bchan, fhindex, erp; + u_int16_t capinfo, bintval, timoff; u_int16_t fhdwell; - int isprobe; - if (ic->ic_opmode != IEEE80211_M_IBSS && - ic->ic_state != IEEE80211_S_SCAN) { - /* XXX: may be useful for background scan */ + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + /* + * Count beacon frames specially, some drivers + * use this info to do things like update LED's. + */ + ic->ic_stats.is_rx_beacon++; + IEEE80211_NODE_STAT(ni, rx_beacons); + } else + IEEE80211_NODE_STAT(ni, rx_proberesp); + /* + * 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; } - isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP); - /* * beacon/probe response frame format * [8] time stamp @@ -578,17 +1716,20 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] parameter set (FH/DS) * [tlv] erp information * [tlv] extended supported rates + * [tlv] WME + * [tlv] WPA or RSN */ IEEE80211_VERIFY_LENGTH(efrm - frm, 12); - tstamp = frm; frm += 8; - bintval = frm; frm += 2; - capinfo = frm; frm += 2; - ssid = rates = xrates = country = NULL; + tstamp = frm; frm += 8; + bintval = le16toh(*(u_int16_t *)frm); frm += 2; + capinfo = le16toh(*(u_int16_t *)frm); frm += 2; + ssid = rates = xrates = country = wpa = wme = NULL; bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); chan = bchan; fhdwell = 0; fhindex = 0; erp = 0; + timoff = 0; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_SSID: @@ -602,7 +1743,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, break; case IEEE80211_ELEMID_FHPARMS: if (ic->ic_phytype == IEEE80211_T_FH) { - fhdwell = (frm[3] << 8) | frm[2]; + fhdwell = LE_READ_2(&frm[2]); chan = IEEE80211_FH_CHAN(frm[4], frm[5]); fhindex = frm[6]; } @@ -616,6 +1757,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, chan = frm[2]; break; case IEEE80211_ELEMID_TIM: + /* XXX ATIM? */ + timoff = frm - mtod(m0, u_int8_t *); break; case IEEE80211_ELEMID_IBSSPARMS: break; @@ -624,17 +1767,28 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, break; case IEEE80211_ELEMID_ERP: if (frm[1] != 1) { - IEEE80211_DPRINTF(("%s: invalid ERP " - "element; length %u, expecting " - "1\n", __func__, frm[1])); + IEEE80211_DISCARD_IE(ic, + IEEE80211_MSG_ELEMID, wh, "ERP", + "bad len %u", frm[1]); ic->ic_stats.is_rx_elem_toobig++; break; } erp = frm[2]; break; + case IEEE80211_ELEMID_RSN: + wpa = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + wpa = frm; + else if (iswmeparam(frm) || iswmeinfo(frm)) + wme = frm; + /* XXX Atheros OUI support */ + break; default: - IEEE80211_DPRINTF2(("%s: element id %u/len %u " - "ignored\n", __func__, *frm, frm[1])); + IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, + wh, "unhandled", + "id %u, len %u", *frm, frm[1]); ic->ic_stats.is_rx_elem_unknown++; break; } @@ -647,10 +1801,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, chan > IEEE80211_CHAN_MAX || #endif isclr(ic->ic_chan_active, chan)) { - IEEE80211_DPRINTF(("%s: ignore %s with invalid channel " - "%u\n", __func__, - isprobe ? "probe response" : "beacon", - chan)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "invalid channel %u", chan); ic->ic_stats.is_rx_badchan++; return; } @@ -665,108 +1819,146 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * the rssi value should be correct even for * different hop pattern in FH. */ - IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked " - "for channel %u\n", __func__, - isprobe ? "probe response" : "beacon", - bchan, chan)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "for off-channel %u\n", chan); ic->ic_stats.is_rx_chanmismatch++; return; } /* - * Use mac and channel for lookup so we collect all - * potential AP's when scanning. Otherwise we may - * see the same AP on multiple channels and will only - * record the last one. We could filter APs here based - * on rssi, etc. but leave that to the end of the scan - * so we can keep the selection criteria in one spot. - * This may result in a bloat of the scanned AP list but - * it shouldn't be too much. + * 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. */ - ni = ieee80211_lookup_node(ic, wh->i_addr2, - &ic->ic_channels[chan]); -#ifdef IEEE80211_DEBUG - if (ieee80211_debug && - (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) { - printf("%s: %s%s on chan %u (bss chan %u) ", - __func__, (ni == NULL ? "new " : ""), - isprobe ? "probe response" : "beacon", - chan, bchan); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %s\n", ether_sprintf(wh->i_addr2)); - printf("%s: caps 0x%x bintval %u erp 0x%x\n", - __func__, le16toh(*(u_int16_t *)capinfo), - le16toh(*(u_int16_t *)bintval), erp); - if (country) - printf("%s: country info %*D\n", - __func__, country[1], country+2, " "); + 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))) { + if (ni->ni_erp != 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, erp); + if (erp & IEEE80211_ERP_USE_PROTECTION) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ni->ni_erp = erp; + /* XXX statistic */ + } + if ((ni->ni_capinfo ^ 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, capinfo); + /* + * NB: we assume short preamble doesn't + * change dynamically + */ + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + ni->ni_capinfo = capinfo; + /* XXX statistic */ + } + if (wme != NULL && + ieee80211_parse_wmeparams(ic, wme, wh)) + ieee80211_wme_updateparams(ic); + /* NB: don't need the rest of this */ + return; } + + if (ni == ic->ic_bss) { +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) + dump_probe_beacon(subtype, 1, + wh->i_addr2, chan, bchan, capinfo, + bintval, erp, ssid, country); #endif - if (ni == NULL) { - ni = ieee80211_alloc_node(ic, wh->i_addr2); + /* + * Create a new entry. If scanning the entry goes + * in the scan cache. Otherwise, be particular when + * operating in adhoc mode--only take nodes marked + * as ibss participants so we don't populate our + * neighbor table with unintersting sta's. + */ + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + if ((capinfo & IEEE80211_CAPINFO_IBSS) == 0) + return; + ni = ieee80211_fakeup_adhoc_node(ic->ic_sta, + wh->i_addr2); + } else + ni = ieee80211_dup_bss(&ic->ic_scan, wh->i_addr2); if (ni == NULL) return; ni->ni_esslen = ssid[1]; memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); memcpy(ni->ni_essid, ssid + 2, ssid[1]); - allocbs = 1; - } else if (ssid[1] != 0 && isprobe) { + } else if (ssid[1] != 0 && + (ISPROBE(subtype) || ni->ni_esslen == 0)) { /* - * Update ESSID at probe response to adopt hidden AP by - * Lucent/Cisco, which announces null ESSID in beacon. + * Update ESSID at probe response to adopt + * hidden AP by Lucent/Cisco, which announces + * null ESSID in beacon. */ +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic) || + ieee80211_msg_debug(ic)) + dump_probe_beacon(subtype, 0, + wh->i_addr2, chan, bchan, capinfo, + bintval, erp, ssid, country); +#endif ni->ni_esslen = ssid[1]; memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); memcpy(ni->ni_essid, ssid + 2, ssid[1]); - allocbs = 0; - } else - allocbs = 0; + } + ni->ni_scangen = ic->ic_scan.nt_scangen; IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; - memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp)); - ni->ni_intval = le16toh(*(u_int16_t *)bintval); - ni->ni_capinfo = le16toh(*(u_int16_t *)capinfo); - /* XXX validate channel # */ + memcpy(ni->ni_tstamp.data, tstamp, sizeof(ni->ni_tstamp)); + ni->ni_intval = bintval; + ni->ni_capinfo = capinfo; ni->ni_chan = &ic->ic_channels[chan]; ni->ni_fhdwell = fhdwell; ni->ni_fhindex = fhindex; ni->ni_erp = erp; - /* NB: must be after ni_chan is setup */ - ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); /* - * When scanning we record results (nodes) with a zero - * refcnt. Otherwise we want to hold the reference for - * ibss neighbors so the nodes don't get released prematurely. - * Anything else can be discarded (XXX and should be handled - * above so we don't do so much work). + * Record the byte offset from the mac header to + * the start of the TIM information element for + * use by hardware and/or to speedup software + * processing of beacon frames. */ - if (ic->ic_state == IEEE80211_S_SCAN) - ieee80211_unref_node(&ni); /* NB: do not free */ - else if (ic->ic_opmode == IEEE80211_M_IBSS && - allocbs && isprobe) { - /* - * Fake an association so the driver can setup it's - * private state. The rate set has been setup above; - * there is no handshake as in ap/station operation. - */ - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, 1); - /* NB: hold reference */ - } else { - /* XXX optimize to avoid work done above */ - ieee80211_free_node(ic, ni); - } + ni->ni_timoff = timoff; + /* + * Record optional information elements that might be + * used by applications or drivers. + */ + if (wme != NULL) + ieee80211_saveie(&ni->ni_wme_ie, wme); + if (wpa != NULL) + ieee80211_saveie(&ni->ni_wpa_ie, wpa); + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); break; } case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { u_int8_t rate; - if (ic->ic_opmode == IEEE80211_M_STA) + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_state != IEEE80211_S_RUN) { + ic->ic_stats.is_rx_mgtdiscard++; return; - if (ic->ic_state != IEEE80211_S_RUN) + } + if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { + /* frame must be directed */ + ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ return; + } /* * prreq frame format @@ -791,43 +1983,45 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - if (ssid[1] != 0 && - (ssid[1] != ic->ic_bss->ni_esslen || - memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("%s: ssid unmatch ", __func__); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %s\n", ether_sprintf(wh->i_addr2)); - } -#endif - ic->ic_stats.is_rx_ssidmismatch++; - return; - } + IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); if (ni == ic->ic_bss) { - ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ic->ic_opmode == IEEE80211_M_IBSS) { + /* + * XXX Cannot tell if the sender is operating + * in ibss mode. But we need a new node to + * send the response so blindly add them to the + * neighbor table. + */ + ni = ieee80211_fakeup_adhoc_node(ic->ic_sta, + wh->i_addr2); + } else + ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2); if (ni == NULL) return; - IEEE80211_DPRINTF(("%s: new req from %s\n", - __func__, ether_sprintf(wh->i_addr2))); allocbs = 1; } else allocbs = 0; + 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(ic, ni, rates, xrates, - IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE - | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE + | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (rate & IEEE80211_RATE_BASIC) { - IEEE80211_DPRINTF(("%s: rate negotiation failed: %s\n", - __func__,ether_sprintf(wh->i_addr2))); + IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "recv'd rate set invalid"); } else { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); } - if (allocbs) - ieee80211_free_node(ic, ni); + if (allocbs && ic->ic_opmode != IEEE80211_M_IBSS) { + /* reclaim immediately */ + ieee80211_free_node(ni); + } break; } @@ -844,94 +2038,63 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, algo = le16toh(*(u_int16_t *)frm); seq = le16toh(*(u_int16_t *)(frm + 2)); status = le16toh(*(u_int16_t *)(frm + 4)); - if (algo != IEEE80211_AUTH_ALG_OPEN) { - /* TODO: shared key auth */ - IEEE80211_DPRINTF(("%s: unsupported auth %d from %s\n", - __func__, algo, ether_sprintf(wh->i_addr2))); - ic->ic_stats.is_rx_auth_unsupported++; + 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++; return; } - switch (ic->ic_opmode) { - case IEEE80211_M_IBSS: - if (ic->ic_state != IEEE80211_S_RUN || seq != 1) { - IEEE80211_DPRINTF(("%s: discard auth from %s; " - "state %u, seq %u\n", __func__, - ether_sprintf(wh->i_addr2), - ic->ic_state, seq)); - ic->ic_stats.is_rx_bad_auth++; - break; - } - ieee80211_new_state(ic, IEEE80211_S_AUTH, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - - case IEEE80211_M_AHDEMO: - /* should not come here */ - break; - - case IEEE80211_M_HOSTAP: - if (ic->ic_state != IEEE80211_S_RUN || seq != 1) { - IEEE80211_DPRINTF(("%s: discard auth from %s; " - "state %u, seq %u\n", __func__, - ether_sprintf(wh->i_addr2), - ic->ic_state, seq)); - ic->ic_stats.is_rx_bad_auth++; - break; - } - if (ni == ic->ic_bss) { - ni = ieee80211_alloc_node(ic, wh->i_addr2); - if (ni == NULL) - return; - IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - ni->ni_chan = ic->ic_bss->ni_chan; - allocbs = 1; - } else - allocbs = 0; - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 2); - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %s %s authenticated\n", - (allocbs ? "newly" : "already"), - ether_sprintf(ni->ni_macaddr)); - break; - - case IEEE80211_M_STA: - if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) { - IEEE80211_DPRINTF(("%s: discard auth from %s; " - "state %u, seq %u\n", __func__, - ether_sprintf(wh->i_addr2), - ic->ic_state, seq)); - ic->ic_stats.is_rx_bad_auth++; - break; - } - if (status != 0) { - if_printf(&ic->ic_if, - "authentication failed (reason %d) for %s\n", - status, - ether_sprintf(wh->i_addr3)); - if (ni != ic->ic_bss) - ni->ni_fails++; - ic->ic_stats.is_rx_auth_fail++; - 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); } - ieee80211_new_state(ic, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - case IEEE80211_M_MONITOR: - break; + return; } + if (algo == IEEE80211_AUTH_ALG_SHARED) + ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, + rstamp, seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, + status); + else { + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, "auth", "unsupported alg %d", algo); + ic->ic_stats.is_rx_auth_unsupported++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX not right */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq+1) | (IEEE80211_STATUS_ALG<<16)); + } + return; + } break; } case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { u_int16_t capinfo, bintval; + struct ieee80211_rsnparms rsn; + u_int8_t reason; if (ic->ic_opmode != IEEE80211_M_HOSTAP || - (ic->ic_state != IEEE80211_S_RUN)) + ic->ic_state != IEEE80211_S_RUN) { + ic->ic_stats.is_rx_mgtdiscard++; return; + } if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { reassoc = 1; @@ -948,11 +2111,14 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WPA or RSN */ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { - IEEE80211_DPRINTF(("%s: ignore other bss from %s\n", - __func__, ether_sprintf(wh->i_addr2))); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "wrong bssid"); ic->ic_stats.is_rx_assoc_bss++; return; } @@ -960,7 +2126,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, bintval = le16toh(*(u_int16_t *)frm); frm += 2; if (reassoc) frm += 6; /* ignore current AP info */ - ssid = rates = xrates = NULL; + ssid = rates = xrates = wpa = wme = NULL; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_SSID: @@ -972,48 +2138,88 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + /* XXX verify only one of RSN and WPA ie's? */ + case IEEE80211_ELEMID_RSN: + wpa = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) { + if (ic->ic_flags & IEEE80211_F_WPA1) + wpa = frm; + } else if (iswmeinfo(frm)) + wme = frm; + /* XXX Atheros OUI support */ + break; } frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); - if (ssid[1] != ic->ic_bss->ni_esslen || - memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("%s: ssid unmatch ", __func__); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %s\n", ether_sprintf(wh->i_addr2)); - } -#endif - ic->ic_stats.is_rx_ssidmismatch++; - return; - } + IEEE80211_VERIFY_SSID(ic->ic_bss, ssid); + if (ni == ic->ic_bss) { - IEEE80211_DPRINTF(("%s: not authenticated for %s\n", - __func__, ether_sprintf(wh->i_addr2))); - ni = ieee80211_dup_bss(ic, wh->i_addr2); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%s] deny %s request, sta not authenticated\n", + ether_sprintf(wh->i_addr2), + reassoc ? "reassoc" : "assoc"); + ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2); if (ni != NULL) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_ASSOC_NOT_AUTHED); - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); } ic->ic_stats.is_rx_assoc_notauth++; return; } - /* XXX per-node cipher suite */ + if (wpa != NULL) { + /* + * Parse WPA information element. Note that + * we initialize the param block from the node + * state so that information in the IE overrides + * our defaults. The resulting parameters are + * installed below after the association is assured. + */ + rsn = ni->ni_rsn; + if (wpa[0] != IEEE80211_ELEMID_RSN) + reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); + else + reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); + if (reason != 0) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, 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[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN", + rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, + rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, + rsn.rsn_keymgmt, rsn.rsn_caps); + } + /* discard challenge after association */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } /* XXX some stations use the privacy bit for handling APs that suport both encrypted and unencrypted traffic */ + /* NB: PRIVACY flag bits are assumed to match */ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || - (capinfo & IEEE80211_CAPINFO_PRIVACY) != - ((ic->ic_flags & IEEE80211_F_WEPON) ? - IEEE80211_CAPINFO_PRIVACY : 0)) { - IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n", - __func__, capinfo, ether_sprintf(wh->i_addr2))); - ni->ni_associd = 0; + (capinfo & IEEE80211_CAPINFO_PRIVACY) ^ + (ic->ic_flags & IEEE80211_F_PRIVACY)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%s] deny %s request, capability mismatch 0x%x\n", + ether_sprintf(wh->i_addr2), + reassoc ? "reassoc" : "assoc", capinfo); IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO); + ieee80211_node_leave(ic, ni); ic->ic_stats.is_rx_assoc_capmismatch++; return; } @@ -1021,11 +2227,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (ni->ni_rates.rs_nrates == 0) { - IEEE80211_DPRINTF(("%s: rate unmatch for %s\n", - __func__, ether_sprintf(wh->i_addr2))); - ni->ni_associd = 0; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%s] deny %s request, rate set mismatch\n", + ether_sprintf(wh->i_addr2), + reassoc ? "reassoc" : "assoc"); IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); + ieee80211_node_leave(ic, ni); ic->ic_stats.is_rx_assoc_norate++; return; } @@ -1036,33 +2244,51 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ni->ni_chan = ic->ic_bss->ni_chan; ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; ni->ni_fhindex = ic->ic_bss->ni_fhindex; - if (ni->ni_associd == 0) { - /* XXX handle rollover at 2007 */ - /* XXX guarantee uniqueness */ - ni->ni_associd = 0xc000 | ic->ic_bss->ni_associd++; - newassoc = 1; - } else - newassoc = 0; - /* XXX for 11g must turn off short slot time if long - slot time sta associates */ - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %s %s associated\n", - (newassoc ? "newly" : "already"), - ether_sprintf(ni->ni_macaddr)); - /* give driver a chance to setup state like ni_txrate */ - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, newassoc); + if (wpa != NULL) { + /* + * Record WPA/RSN parameters for station, mark + * node as using WPA and record information element + * for applications that require it. + */ + ni->ni_rsn = rsn; + ieee80211_saveie(&ni->ni_wpa_ie, wpa); + } else if (ni->ni_wpa_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_wpa_ie, M_DEVBUF); + ni->ni_wpa_ie = NULL; + } + if (wme != NULL) { + /* + * Record WME parameters for station, mark node + * as capable of QoS and record information + * element for applications that require it. + */ + ieee80211_saveie(&ni->ni_wme_ie, wme); + ni->ni_flags |= IEEE80211_NODE_QOS; + } else if (ni->ni_wme_ie != NULL) { + /* + * Flush any state from a previous association. + */ + FREE(ni->ni_wme_ie, M_DEVBUF); + ni->ni_wme_ie = NULL; + ni->ni_flags &= ~IEEE80211_NODE_QOS; + } + ieee80211_node_join(ic, ni, resp); break; } case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { + u_int16_t capinfo, associd; u_int16_t status; if (ic->ic_opmode != IEEE80211_M_STA || - ic->ic_state != IEEE80211_S_ASSOC) + ic->ic_state != IEEE80211_S_ASSOC) { + ic->ic_stats.is_rx_mgtdiscard++; return; + } /* * asresp frame format @@ -1071,26 +2297,28 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, * [2] association ID * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME */ IEEE80211_VERIFY_LENGTH(efrm - frm, 6); ni = ic->ic_bss; - ni->ni_capinfo = le16toh(*(u_int16_t *)frm); + capinfo = le16toh(*(u_int16_t *)frm); frm += 2; - status = le16toh(*(u_int16_t *)frm); frm += 2; if (status != 0) { - if_printf(ifp, "association failed (reason %d) for %s\n", - status, ether_sprintf(wh->i_addr3)); - if (ni != ic->ic_bss) + 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++; + ic->ic_stats.is_rx_auth_fail++; /* XXX */ return; } - ni->ni_associd = le16toh(*(u_int16_t *)frm); + associd = le16toh(*(u_int16_t *)frm); frm += 2; - rates = xrates = NULL; + rates = xrates = wpa = wme = NULL; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_RATES: @@ -1099,6 +2327,11 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_ELEMID_XRATES: xrates = frm; break; + case IEEE80211_ELEMID_VENDOR: + if (iswmeoui(frm)) + wme = frm; + /* XXX Atheros OUI support */ + break; } frm += frm[1] + 2; } @@ -1107,14 +2340,70 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ni->ni_rates.rs_nrates != 0) - ieee80211_new_state(ic, IEEE80211_S_RUN, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + if (ni->ni_rates.rs_nrates == 0) { + 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++; + return; + } + + ni->ni_capinfo = capinfo; + ni->ni_associd = associd; + if (wme != NULL && ieee80211_parse_wmeparams(ic, wme, wh)) { + ni->ni_flags |= IEEE80211_NODE_QOS; + ieee80211_wme_updateparams(ic); + } else + ni->ni_flags &= ~IEEE80211_NODE_QOS; + /* + * Configure state now that we are associated. + * + * XXX may need different/additional driver callbacks? + */ + if (ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + /* + * Honor ERP protection. + * + * NB: ni_erp should zero for non-11g operation. + * XXX check ic_curmode anyway? + */ + if (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\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" : "" + ); + ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); break; } case IEEE80211_FC0_SUBTYPE_DEAUTH: { u_int16_t reason; + + if (ic->ic_state == IEEE80211_S_SCAN) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } /* * deauth frame format * [2] reason @@ -1122,6 +2411,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_VERIFY_LENGTH(efrm - frm, 2); reason = le16toh(*(u_int16_t *)frm); ic->ic_stats.is_rx_deauth++; + IEEE80211_NODE_STAT(ni, rx_deauth); switch (ic->ic_opmode) { case IEEE80211_M_STA: ieee80211_new_state(ic, IEEE80211_S_AUTH, @@ -1129,15 +2419,15 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, break; case IEEE80211_M_HOSTAP: if (ni != ic->ic_bss) { - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %s deauthenticated" - " by peer (reason %d)\n", - ether_sprintf(ni->ni_macaddr), reason); - /* node will be free'd on return */ - ieee80211_unref_node(&ni); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "station %s deauthenticated by peer " + "(reason %d)\n", + ether_sprintf(ni->ni_macaddr), reason); + ieee80211_node_leave(ic, ni); } break; default: + ic->ic_stats.is_rx_mgtdiscard++; break; } break; @@ -1145,6 +2435,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, case IEEE80211_FC0_SUBTYPE_DISASSOC: { u_int16_t reason; + + if (ic->ic_state != IEEE80211_S_RUN && + ic->ic_state != IEEE80211_S_AUTH) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } /* * disassoc frame format * [2] reason @@ -1152,6 +2448,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, IEEE80211_VERIFY_LENGTH(efrm - frm, 2); reason = le16toh(*(u_int16_t *)frm); ic->ic_stats.is_rx_disassoc++; + IEEE80211_NODE_STAT(ni, rx_disassoc); switch (ic->ic_opmode) { case IEEE80211_M_STA: ieee80211_new_state(ic, IEEE80211_S_ASSOC, @@ -1159,25 +2456,227 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, break; case IEEE80211_M_HOSTAP: if (ni != ic->ic_bss) { - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %s disassociated" - " by peer (reason %d)\n", - ether_sprintf(ni->ni_macaddr), reason); - ni->ni_associd = 0; - /* XXX node reclaimed how? */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] sta disassociated by peer (reason %d)\n", + ether_sprintf(ni->ni_macaddr), reason); + ieee80211_node_leave(ic, ni); } break; default: + ic->ic_stats.is_rx_mgtdiscard++; break; } break; } default: - IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not " - "handled\n", __func__, subtype)); + IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); ic->ic_stats.is_rx_badsubtype++; break; } +#undef ISREASSOC +#undef ISPROBE } #undef IEEE80211_VERIFY_LENGTH #undef IEEE80211_VERIFY_ELEMENT + +/* + * Handle station power-save state change. + */ +static void +ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + + if (enable) { + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) + ic->ic_ps_sta++; + ni->ni_flags |= IEEE80211_NODE_PWR_MGT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] power save mode on, %u sta's in ps mode\n", + ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); + return; + } + + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) + ic->ic_ps_sta--; + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] power save mode off, %u sta's in ps mode\n", + ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); + /* XXX if no stations in ps mode, flush mc frames */ + + /* + * Flush queued unicast frames. + */ + if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) { + ic->ic_set_tim(ic, ni, 0); /* just in case */ + return; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] flush ps queue, %u packets queued\n", + ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni)); + for (;;) { + int qlen; + + IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); + if (m == NULL) + break; + /* + * If this is the last packet, turn off the TIM bit. + * If there are more packets, set the more packets bit + * in the packet dispatched to the station. + */ + if (qlen != 0) { + struct ieee80211_frame_min *wh = + mtod(m, struct ieee80211_frame_min *); + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + } + /* XXX need different driver interface */ + /* XXX bypasses q max */ + IF_ENQUEUE(&ic->ic_ifp->if_snd, m); + } +} + +/* + * 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; + u_int16_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(*(u_int16_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(ic, ni); + ic->ic_stats.is_ps_qempty++; /* XXX node stat */ + ic->ic_set_tim(ic, 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); + wh = mtod(m, struct ieee80211_frame_min *); + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + } else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] recv ps-poll, send packet, queue empty\n", + ether_sprintf(ni->ni_macaddr)); + ic->ic_set_tim(ic, ni, 0); + } + m->m_flags |= M_PWR_SAV; /* bypass PS handling */ + IF_ENQUEUE(&ic->ic_ifp->if_snd, m); +} + +#ifdef IEEE80211_DEBUG +/* + * Debugging support. + */ + +/* + * Return the bssid of a frame. + */ +static const u_int8_t * +ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh) +{ + if (ic->ic_opmode == IEEE80211_M_STA) + return wh->i_addr2; + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) + return wh->i_addr1; + if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) + return wh->i_addr1; + return wh->i_addr3; +} + +static void +ieee80211_discard_frame(struct ieee80211com *ic, + const struct ieee80211_frame *wh, + const char *type, const char *fmt, ...) +{ + va_list ap; + + printf("[%s] discard ", ether_sprintf(ieee80211_getbssid(ic, wh))); + if (type != NULL) + printf(" %s frame, ", type); + else + printf(" frame, "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} + +static void +ieee80211_discard_ie(struct ieee80211com *ic, + const struct ieee80211_frame *wh, + const char *type, const char *fmt, ...) +{ + va_list ap; + + printf("[%s] discard ", ether_sprintf(ieee80211_getbssid(ic, wh))); + if (type != NULL) + printf(" %s information element, ", type); + else + printf(" information element, "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} + +static void +ieee80211_discard_mac(struct ieee80211com *ic, + const u_int8_t mac[IEEE80211_ADDR_LEN], + const char *type, const char *fmt, ...) +{ + va_list ap; + + printf("[%s] discard ", ether_sprintf(mac)); + if (type != NULL) + printf(" %s frame, ", type); + else + printf(" frame, "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} +#endif /* IEEE80211_DEBUG */ diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 7599591..059d215 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,26 +67,129 @@ __FBSDID("$FreeBSD$"); #include +#define IS_UP(_ic) \ + (((_ic)->ic_ifp->if_flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP)) +#define IS_UP_AUTO(_ic) \ + (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) + /* * XXX * Wireless LAN specific configuration interface, which is compatible * with wicontrol(8). */ +struct wi_read_ap_args { + int i; /* result count */ + struct wi_apinfo *ap; /* current entry in result buffer */ + caddr_t max; /* result buffer bound */ +}; + +static void +wi_read_ap_result(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_read_ap_args *sa = arg; + struct wi_apinfo *ap = sa->ap; + struct ieee80211_rateset *rs; + int j; + + if ((caddr_t)(ap + 1) > sa->max) + return; + memset(ap, 0, sizeof(struct wi_apinfo)); + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); + ap->namelen = ic->ic_des_esslen; + if (ic->ic_des_esslen) + memcpy(ap->name, ic->ic_des_essid, + ic->ic_des_esslen); + } else { + IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); + ap->namelen = ni->ni_esslen; + if (ni->ni_esslen) + memcpy(ap->name, ni->ni_essid, + ni->ni_esslen); + } + ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); + ap->signal = ic->ic_node_getrssi(ni); + ap->capinfo = ni->ni_capinfo; + ap->interval = ni->ni_intval; + rs = &ni->ni_rates; + for (j = 0; j < rs->rs_nrates; j++) { + if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { + ap->rate = (rs->rs_rates[j] & + IEEE80211_RATE_VAL) * 5; /* XXX */ + } + } + sa->i++; + sa->ap++; +} + +struct wi_read_prism2_args { + int i; /* result count */ + struct wi_scan_res *res;/* current entry in result buffer */ + caddr_t max; /* result buffer bound */ +}; + +static void +wi_read_prism2_result(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_read_prism2_args *sa = arg; + struct wi_scan_res *res = sa->res; + + if ((caddr_t)(res + 1) > sa->max) + return; + res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); + res->wi_noise = 0; + res->wi_signal = ic->ic_node_getrssi(ni); + IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); + res->wi_interval = ni->ni_intval; + res->wi_capinfo = ni->ni_capinfo; + res->wi_ssid_len = ni->ni_esslen; + memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); + /* NB: assumes wi_srates holds <= ni->ni_rates */ + memcpy(res->wi_srates, ni->ni_rates.rs_rates, + sizeof(res->wi_srates)); + if (ni->ni_rates.rs_nrates < 10) + res->wi_srates[ni->ni_rates.rs_nrates] = 0; + res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; + res->wi_rsvd = 0; + + sa->i++; + sa->res++; +} + +struct wi_read_sigcache_args { + int i; /* result count */ + struct wi_sigcache *wsc;/* current entry in result buffer */ + caddr_t max; /* result buffer bound */ +}; + +static void +wi_read_sigcache(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_read_sigcache_args *sa = arg; + struct wi_sigcache *wsc = sa->wsc; + + if ((caddr_t)(wsc + 1) > sa->max) + return; + memset(wsc, 0, sizeof(struct wi_sigcache)); + IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr); + wsc->signal = ic->ic_node_getrssi(ni); + + sa->wsc++; + sa->i++; +} + int -ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) +ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; int i, j, error; struct ifreq *ifr = (struct ifreq *)data; struct wi_req wreq; struct wi_ltv_keys *keys; - struct wi_apinfo *ap; - struct ieee80211_node *ni; - struct ieee80211_rateset *rs; - struct wi_sigcache wsc; - struct wi_scan_p2_hdr *p2; - struct wi_scan_res *res; error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); if (error) @@ -153,8 +256,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) break; case WI_RID_COMMS_QUALITY: wreq.wi_val[0] = 0; /* quality */ - wreq.wi_val[1] = - htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss)); + wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss)); wreq.wi_val[2] = 0; /* noise */ wreq.wi_len = 3; break; @@ -199,7 +301,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) wreq.wi_len = 1; break; case WI_RID_ROAMING_MODE: - wreq.wi_val[0] = htole16(1); /* enabled ... not supported */ + wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */ wreq.wi_len = 1; break; case WI_RID_SYSTEM_SCALE: @@ -220,8 +322,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) wreq.wi_len = 1; break; case WI_RID_WEP_AVAIL: - wreq.wi_val[0] = - htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0); + wreq.wi_val[0] = htole16(1); /* always available */ wreq.wi_len = 1; break; case WI_RID_CNFAUTHMODE: @@ -230,11 +331,11 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) break; case WI_RID_ENCRYPTION: wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0); + htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0); wreq.wi_len = 1; break; case WI_RID_TX_CRYPT_KEY: - wreq.wi_val[0] = htole16(ic->ic_wep_txkey); + wreq.wi_val[0] = htole16(ic->ic_def_txkey); wreq.wi_len = 1; break; case WI_RID_DEFLT_CRYPT_KEYS: @@ -248,124 +349,71 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) } for (i = 0; i < IEEE80211_WEP_NKID; i++) { keys->wi_keys[i].wi_keylen = - htole16(ic->ic_nw_keys[i].wk_len); + htole16(ic->ic_nw_keys[i].wk_keylen); memcpy(keys->wi_keys[i].wi_keydat, - ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); + ic->ic_nw_keys[i].wk_key, + ic->ic_nw_keys[i].wk_keylen); } wreq.wi_len = sizeof(*keys) / 2; break; case WI_RID_MAX_DATALEN: - wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ + wreq.wi_val[0] = htole16(ic->ic_fragthreshold); wreq.wi_len = 1; break; case WI_RID_IFACE_STATS: /* XXX: should be implemented in lower drivers */ break; case WI_RID_READ_APS: - if (ic->ic_opmode != IEEE80211_M_HOSTAP) { - /* - * Don't return results until active scan completes. - */ - if (ic->ic_state == IEEE80211_S_SCAN && - (ic->ic_flags & IEEE80211_F_ASCAN)) { - error = EINPROGRESS; - break; - } - } - i = 0; - ap = (void *)((char *)wreq.wi_val + sizeof(i)); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) - break; - memset(ap, 0, sizeof(*ap)); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); - ap->namelen = ic->ic_des_esslen; - if (ic->ic_des_esslen) - memcpy(ap->name, ic->ic_des_essid, - ic->ic_des_esslen); - } else { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); - ap->namelen = ni->ni_esslen; - if (ni->ni_esslen) - memcpy(ap->name, ni->ni_essid, - ni->ni_esslen); - } - ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); - ap->signal = (*ic->ic_node_getrssi)(ic, ni); - ap->capinfo = ni->ni_capinfo; - ap->interval = ni->ni_intval; - rs = &ni->ni_rates; - for (j = 0; j < rs->rs_nrates; j++) { - if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { - ap->rate = (rs->rs_rates[j] & - IEEE80211_RATE_VAL) * 5; /* XXX */ - } - } - i++; - ap++; - } - memcpy(wreq.wi_val, &i, sizeof(i)); - wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; + /* + * Don't return results until active scan completes. + */ + if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { + struct wi_read_ap_args args; + + args.i = 0; + args.ap = (void *)((char *)wreq.wi_val + sizeof(i)); + args.max = (void *)(&wreq + 1); + ieee80211_iterate_nodes(&ic->ic_scan, + wi_read_ap_result, &args); + memcpy(wreq.wi_val, &args.i, sizeof(args.i)); + wreq.wi_len = (sizeof(int) + + sizeof(struct wi_apinfo) * args.i) / 2; + } else + error = EINPROGRESS; break; case WI_RID_PRISM2: - wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */ + /* NB: we lie so WI_RID_SCAN_RES can include rates */ + wreq.wi_val[0] = 1; wreq.wi_len = sizeof(u_int16_t) / 2; break; case WI_RID_SCAN_RES: /* compatibility interface */ - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_state == IEEE80211_S_SCAN && - (ic->ic_flags & IEEE80211_F_ASCAN)) { + if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) { + struct wi_read_prism2_args args; + struct wi_scan_p2_hdr *p2; + + /* NB: use Prism2 format so we can include rate info */ + p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; + args.i = 0; + args.res = (void *)&p2[1]; + args.max = (void *)(&wreq + 1); + ieee80211_iterate_nodes(&ic->ic_scan, + wi_read_prism2_result, &args); + p2->wi_rsvd = 0; + p2->wi_reason = args.i; + wreq.wi_len = (sizeof(*p2) + + sizeof(struct wi_scan_res) * args.i) / 2; + } else error = EINPROGRESS; - break; - } - /* NB: we use the Prism2 format so we can return rate info */ - p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; - res = (void *)&p2[1]; - i = 0; - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1)) - break; - res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); - res->wi_noise = 0; - res->wi_signal = (*ic->ic_node_getrssi)(ic, ni); - IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); - res->wi_interval = ni->ni_intval; - res->wi_capinfo = ni->ni_capinfo; - res->wi_ssid_len = ni->ni_esslen; - memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); - /* NB: assumes wi_srates holds <= ni->ni_rates */ - memcpy(res->wi_srates, ni->ni_rates.rs_rates, - sizeof(res->wi_srates)); - if (ni->ni_rates.rs_nrates < 10) - res->wi_srates[ni->ni_rates.rs_nrates] = 0; - res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; - res->wi_rsvd = 0; - res++, i++; - } - p2->wi_rsvd = 0; - p2->wi_reason = i; - wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2; break; - case WI_RID_READ_CACHE: - i = 0; - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1) - break; - IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); - memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); - wsc.signal = (*ic->ic_node_getrssi)(ic, ni); - wsc.noise = 0; - wsc.quality = 0; - memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, - &wsc, sizeof(wsc)); - i++; - } - wreq.wi_len = sizeof(wsc) * i / 2; - break; - case WI_RID_SCAN_APS: - error = EINVAL; + case WI_RID_READ_CACHE: { + struct wi_read_sigcache_args args; + args.i = 0; + args.wsc = (struct wi_sigcache *) wreq.wi_val; + args.max = (void *)(&wreq + 1); + ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args); + wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2; break; + } default: error = EINVAL; break; @@ -397,11 +445,20 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) * the active list as the place to start the scan. */ static int -ieee80211_setupscan(struct ieee80211com *ic) +ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[]) { - u_char *chanlist = ic->ic_chan_active; int i; + /* + * XXX don't permit a scan to be started unless we + * know the device is ready. For the moment this means + * the device is marked up as this is the required to + * initialize the hardware. It would be better to permit + * scanning prior to being up but that'll require some + * changes to the infrastructure. + */ + if (!IS_UP(ic)) + return EINVAL; if (ic->ic_ibss_chan == NULL || isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { for (i = 0; i <= IEEE80211_CHAN_MAX; i++) @@ -416,21 +473,21 @@ found: if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) ic->ic_bss->ni_chan = ic->ic_ibss_chan; + memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); /* - * XXX don't permit a scan to be started unless we - * know the device is ready. For the moment this means - * the device is marked up as this is the required to - * initialize the hardware. It would be better to permit - * scanning prior to being up but that'll require some - * changes to the infrastructure. + * We force the state to INIT before calling ieee80211_new_state + * to get ieee80211_begin_scan called. We really want to scan w/o + * altering the current state but that's not possible right now. */ - return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET; + /* XXX handle proberequest case */ + ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */ + return 0; } int -ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) +ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; int i, j, len, error, rate; struct ifreq *ifr = (struct ifreq *)data; struct wi_ltv_keys *keys; @@ -470,7 +527,9 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) isclr(ic->ic_chan_active, i)) return EINVAL; ic->ic_ibss_chan = &ic->ic_channels[i]; - if (ic->ic_flags & IEEE80211_F_SIBSS) + if (ic->ic_opmode == IEEE80211_M_MONITOR) + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + else error = ENETRESET; break; case WI_RID_CURRENT_CHAN: @@ -516,7 +575,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) } if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { ic->ic_opmode = le16toh(wreq.wi_val[0]); - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } break; #if 0 @@ -564,7 +623,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; setrate: ic->ic_fixed_rate = i; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_CUR_TX_RATE: return EPERM; @@ -584,14 +643,14 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) ic->ic_flags |= IEEE80211_F_IBSSON; if (ic->ic_opmode == IEEE80211_M_IBSS && ic->ic_state == IEEE80211_S_SCAN) - error = ENETRESET; + error = IS_UP_AUTO(ic) ? ENETRESET : 0; } } else { if (ic->ic_flags & IEEE80211_F_IBSSON) { ic->ic_flags &= ~IEEE80211_F_IBSSON; if (ic->ic_flags & IEEE80211_F_SIBSS) { ic->ic_flags &= ~IEEE80211_F_SIBSS; - error = ENETRESET; + error = IS_UP_AUTO(ic) ? ENETRESET : 0; } } } @@ -605,8 +664,10 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_ROAMING_MODE: if (len != 2) return EINVAL; - if (le16toh(wreq.wi_val[0]) != 1) + i = le16toh(wreq.wi_val[0]); + if (i > IEEE80211_ROAMING_MANUAL) return EINVAL; /* not supported */ + ic->ic_roaming = i; break; case WI_RID_SYSTEM_SCALE: if (len != 2) @@ -622,12 +683,12 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { ic->ic_flags |= IEEE80211_F_PMGTON; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } } else { if (ic->ic_flags & IEEE80211_F_PMGTON) { ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; } } break; @@ -636,7 +697,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) return EINVAL; ic->ic_lintval = le16toh(wreq.wi_val[0]); if (ic->ic_flags & IEEE80211_F_PMGTON) - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_CUR_BEACON_INT: return EPERM; @@ -645,8 +706,11 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_CNFAUTHMODE: if (len != 2) return EINVAL; - if (le16toh(wreq.wi_val[0]) != 1) - return EINVAL; /* TODO: shared key auth */ + i = le16toh(wreq.wi_val[0]); + if (i > IEEE80211_AUTH_WPA) + return EINVAL; + ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */ + error = ENETRESET; break; case WI_RID_ENCRYPTION: if (len != 2) @@ -654,13 +718,13 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) if (wreq.wi_val[0] != 0) { if ((ic->ic_caps & IEEE80211_C_WEP) == 0) return EINVAL; - if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { - ic->ic_flags |= IEEE80211_F_WEPON; + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + ic->ic_flags |= IEEE80211_F_PRIVACY; error = ENETRESET; } } else { - if (ic->ic_flags & IEEE80211_F_WEPON) { - ic->ic_flags &= ~IEEE80211_F_WEPON; + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + ic->ic_flags &= ~IEEE80211_F_PRIVACY; error = ENETRESET; } } @@ -671,7 +735,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) i = le16toh(wreq.wi_val[0]); if (i >= IEEE80211_WEP_NKID) return EINVAL; - ic->ic_wep_txkey = i; + ic->ic_def_txkey = i; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_DEFLT_CRYPT_KEYS: if (len != sizeof(struct wi_ltv_keys)) @@ -681,15 +746,20 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) len = le16toh(keys->wi_keys[i].wi_keylen); if (len != 0 && len < IEEE80211_WEP_KEYLEN) return EINVAL; - if (len > sizeof(ic->ic_nw_keys[i].wk_key)) + if (len > IEEE80211_KEYBUF_SIZE) return EINVAL; } - memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); for (i = 0; i < IEEE80211_WEP_NKID; i++) { + struct ieee80211_key *k = &ic->ic_nw_keys[i]; + len = le16toh(keys->wi_keys[i].wi_keylen); - ic->ic_nw_keys[i].wk_len = len; - memcpy(ic->ic_nw_keys[i].wk_key, - keys->wi_keys[i].wi_keydat, len); + k->wk_keylen = len; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + memset(k->wk_key, 0, sizeof(k->wk_key)); + memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len); +#if 0 + k->wk_type = IEEE80211_CIPHER_WEP; +#endif } error = ENETRESET; break; @@ -699,10 +769,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) len = le16toh(wreq.wi_val[0]); if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) return EINVAL; - if (len != IEEE80211_MAX_LEN) - return EINVAL; /* TODO: fragment */ ic->ic_fragthreshold = len; - error = ENETRESET; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; break; case WI_RID_IFACE_STATS: error = EPERM; @@ -710,7 +778,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) case WI_RID_SCAN_REQ: /* XXX wicontrol */ if (ic->ic_opmode == IEEE80211_M_HOSTAP) break; - error = ieee80211_setupscan(ic); + error = ieee80211_setupscan(ic, ic->ic_chan_avail); if (error == 0) error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; @@ -742,9 +810,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) } setbit(chanlist, i); } - memcpy(ic->ic_chan_active, chanlist, - sizeof(ic->ic_chan_active)); - error = ieee80211_setupscan(ic); + error = ieee80211_setupscan(ic, chanlist); if (wreq.wi_type == WI_RID_CHANNEL_LIST) { /* NB: ignore error from ieee80211_setupscan */ error = ENETRESET; @@ -755,311 +821,1532 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) error = EINVAL; break; } + if (error == ENETRESET && !IS_UP_AUTO(ic)) + error = 0; return error; } -int -ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +static struct ieee80211_channel * +getcurchan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - int error = 0; - u_int kid, len; - struct ieee80211req *ireq; - struct ifreq *ifr; - u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; - char tmpssid[IEEE80211_NWID_LEN]; - struct ieee80211_channel *chan; - struct ifaddr *ifa; /* XXX */ + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + return ic->ic_des_chan; + default: + return ic->ic_ibss_chan; + } +} - switch (cmd) { - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, (struct ifreq *) data, - &ic->ic_media, cmd); - break; - case SIOCG80211: - ireq = (struct ieee80211req *) data; - switch (ireq->i_type) { - case IEEE80211_IOC_SSID: - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - ireq->i_len = ic->ic_des_esslen; - memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); - break; - default: - ireq->i_len = ic->ic_bss->ni_esslen; - memcpy(tmpssid, ic->ic_bss->ni_essid, - ireq->i_len); - break; - } - error = copyout(tmpssid, ireq->i_data, ireq->i_len); - break; - case IEEE80211_IOC_NUMSSIDS: - ireq->i_val = 1; - break; - case IEEE80211_IOC_WEP: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { - ireq->i_val = IEEE80211_WEP_NOSUP; - } else { - if (ic->ic_flags & IEEE80211_F_WEPON) { - ireq->i_val = - IEEE80211_WEP_MIXED; - } else { - ireq->i_val = - IEEE80211_WEP_OFF; - } - } - break; - case IEEE80211_IOC_WEPKEY: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { - error = EINVAL; - break; - } - kid = (u_int) ireq->i_val; - if (kid >= IEEE80211_WEP_NKID) { - error = EINVAL; - break; - } - len = (u_int) ic->ic_nw_keys[kid].wk_len; - /* NB: only root can read WEP keys */ - if (suser(curthread) == 0) { - bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); - } else { - bzero(tmpkey, len); - } - ireq->i_len = len; - error = copyout(tmpkey, ireq->i_data, len); - break; - case IEEE80211_IOC_NUMWEPKEYS: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) - error = EINVAL; - else - ireq->i_val = IEEE80211_WEP_NKID; - break; - case IEEE80211_IOC_WEPTXKEY: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) - error = EINVAL; - else - ireq->i_val = ic->ic_wep_txkey; - break; - case IEEE80211_IOC_AUTHMODE: - ireq->i_val = IEEE80211_AUTH_OPEN; - break; - case IEEE80211_IOC_CHANNEL: - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - if (ic->ic_opmode == IEEE80211_M_STA) - chan = ic->ic_des_chan; - else - chan = ic->ic_ibss_chan; - break; - default: - chan = ic->ic_bss->ni_chan; - break; - } - ireq->i_val = ieee80211_chan2ieee(ic, chan); - break; - case IEEE80211_IOC_POWERSAVE: - if (ic->ic_flags & IEEE80211_F_PMGTON) - ireq->i_val = IEEE80211_POWERSAVE_ON; - else - ireq->i_val = IEEE80211_POWERSAVE_OFF; +static int +cap2cipher(int flag) +{ + switch (flag) { + case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; + case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; + case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; + case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; + case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; + } + return -1; +} + +static int +ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_key ik; + struct ieee80211_key *wk; + const struct ieee80211_cipher *cip; + u_int kid; + int error; + + if (ireq->i_len != sizeof(ik)) + return EINVAL; + error = copyin(ireq->i_data, &ik, sizeof(ik)); + if (error) + return error; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + wk = &ic->ic_nw_keys[kid]; + IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); + ni = NULL; + } + cip = wk->wk_cipher; + ik.ik_type = cip->ic_cipher; + ik.ik_keylen = wk->wk_keylen; + ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); + if (wk->wk_keyix == ic->ic_def_txkey) + ik.ik_flags |= IEEE80211_KEY_DEFAULT; + if (suser(curthread) == 0) { + /* NB: only root can read key data */ + ik.ik_keyrsc = wk->wk_keyrsc; + ik.ik_keytsc = wk->wk_keytsc; + memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { + memcpy(ik.ik_keydata+wk->wk_keylen, + wk->wk_key + IEEE80211_KEYBUF_SIZE, + IEEE80211_MICBUF_SIZE); + ik.ik_keylen += IEEE80211_MICBUF_SIZE; + } + } else { + ik.ik_keyrsc = 0; + ik.ik_keytsc = 0; + memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); + } + if (ni != NULL) + ieee80211_free_node(ni); + return copyout(&ik, ireq->i_data, sizeof(ik)); +} + +static int +ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + + if (sizeof(ic->ic_chan_active) > ireq->i_len) + ireq->i_len = sizeof(ic->ic_chan_active); + return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); +} + +static int +ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_chaninfo chans; /* XXX off stack? */ + int i, space; + + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + memset(&chans, 0, sizeof(chans)); + for (; i <= IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_avail, i)) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq; + chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags; + chans.ic_nchans++; + } + space = __offsetof(struct ieee80211req_chaninfo, + ic_chans[chans.ic_nchans]); + if (space > ireq->i_len) + space = ireq->i_len; + return copyout(&chans, ireq->i_data, space); +} + +static int +ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_wpaie wpaie; + int error; + + if (ireq->i_len < IEEE80211_ADDR_LEN) + return EINVAL; + error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, wpaie.wpa_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); + if (ni->ni_wpa_ie != NULL) { + int ielen = ni->ni_wpa_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); + } + ieee80211_free_node(ni); + if (ireq->i_len > sizeof(wpaie)) + ireq->i_len = sizeof(wpaie); + return copyout(&wpaie, ireq->i_data, ireq->i_len); +} + +static int +ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + u_int8_t macaddr[IEEE80211_ADDR_LEN]; + const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); + int error; + + if (ireq->i_len < off) + return EINVAL; + error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) + ireq->i_len = sizeof(struct ieee80211req_sta_stats); + /* NB: copy out only the statistics */ + error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off, + ireq->i_len - off); + ieee80211_free_node(ni); + return error; +} + +static void +get_scan_result(struct ieee80211req_scan_result *sr, + const struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + memset(sr, 0, sizeof(*sr)); + sr->isr_ssid_len = ni->ni_esslen; + if (ni->ni_wpa_ie != NULL) + sr->isr_ie_len += 2+ni->ni_wpa_ie[1]; + if (ni->ni_wme_ie != NULL) + sr->isr_ie_len += 2+ni->ni_wme_ie[1]; + sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len; + sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t)); + if (ni->ni_chan != IEEE80211_CHAN_ANYC) { + sr->isr_freq = ni->ni_chan->ic_freq; + sr->isr_flags = ni->ni_chan->ic_flags; + } + sr->isr_rssi = ic->ic_node_getrssi(ni); + sr->isr_intval = ni->ni_intval; + sr->isr_capinfo = ni->ni_capinfo; + sr->isr_erp = ni->ni_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid); + sr->isr_nrates = ni->ni_rates.rs_nrates; + if (sr->isr_nrates > 15) + sr->isr_nrates = 15; + memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates); +} + +static int +ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + union { + struct ieee80211req_scan_result res; + char data[512]; /* XXX shrink? */ + } u; + struct ieee80211req_scan_result *sr = &u.res; + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + int error, space; + u_int8_t *p, *cp; + + p = ireq->i_data; + space = ireq->i_len; + error = 0; + /* XXX locking */ + nt = &ic->ic_scan; + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + /* NB: skip pre-scan node state */ + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + continue; + get_scan_result(sr, ni); + if (sr->isr_len > sizeof(u)) + continue; /* XXX */ + if (space < sr->isr_len) break; - case IEEE80211_IOC_POWERSAVESLEEP: - ireq->i_val = ic->ic_lintval; + cp = (u_int8_t *)(sr+1); + memcpy(cp, ni->ni_essid, ni->ni_esslen); + cp += ni->ni_esslen; + if (ni->ni_wpa_ie != NULL) { + memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); + cp += 2+ni->ni_wpa_ie[1]; + } + if (ni->ni_wme_ie != NULL) { + memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); + cp += 2+ni->ni_wme_ie[1]; + } + error = copyout(sr, p, sr->isr_len); + if (error) break; - case IEEE80211_IOC_RTSTHRESHOLD: - ireq->i_val = ic->ic_rtsthreshold; + p += sr->isr_len; + space -= sr->isr_len; + } + ireq->i_len -= space; + return error; +} + +static void +get_sta_info(struct ieee80211req_sta_info *si, const struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + si->isi_ie_len = 0; + if (ni->ni_wpa_ie != NULL) + si->isi_ie_len += 2+ni->ni_wpa_ie[1]; + if (ni->ni_wme_ie != NULL) + si->isi_ie_len += 2+ni->ni_wme_ie[1]; + si->isi_len = sizeof(*si) + si->isi_ie_len, sizeof(u_int32_t); + si->isi_len = roundup(si->isi_len, sizeof(u_int32_t)); + si->isi_freq = ni->ni_chan->ic_freq; + si->isi_flags = ni->ni_chan->ic_flags; + si->isi_state = ni->ni_flags; + si->isi_authmode = ni->ni_authmode; + si->isi_rssi = ic->ic_node_getrssi(ni); + si->isi_capinfo = ni->ni_capinfo; + si->isi_erp = ni->ni_erp; + IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); + si->isi_nrates = ni->ni_rates.rs_nrates; + if (si->isi_nrates > 15) + si->isi_nrates = 15; + memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); + si->isi_txrate = ni->ni_txrate; + si->isi_associd = ni->ni_associd; + si->isi_txpower = ni->ni_txpower; + si->isi_vlan = ni->ni_vlan; + if (ni->ni_flags & IEEE80211_NODE_QOS) { + memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); + memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); + } else { + si->isi_txseqs[0] = ni->ni_txseqs[0]; + si->isi_rxseqs[0] = ni->ni_rxseqs[0]; + } + if (ic->ic_opmode == IEEE80211_M_IBSS || ni->ni_associd != 0) + si->isi_inact = ic->ic_inact_run; + else if (ieee80211_node_is_authorized(ni)) + si->isi_inact = ic->ic_inact_auth; + else + si->isi_inact = ic->ic_inact_init; + si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; +} + +static int +ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + union { + struct ieee80211req_sta_info info; + char data[512]; /* XXX shrink? */ + } u; + struct ieee80211req_sta_info *si = &u.info; + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + int error, space; + u_int8_t *p, *cp; + + nt = ic->ic_sta; + if (nt == NULL) + return EINVAL; + p = ireq->i_data; + space = ireq->i_len; + error = 0; + /* XXX locking */ + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + get_sta_info(si, ni); + if (si->isi_len > sizeof(u)) + continue; /* XXX */ + if (space < si->isi_len) break; - case IEEE80211_IOC_PROTMODE: - ireq->i_val = ic->ic_protmode; + cp = (u_int8_t *)(si+1); + if (ni->ni_wpa_ie != NULL) { + memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]); + cp += 2+ni->ni_wpa_ie[1]; + } + if (ni->ni_wme_ie != NULL) { + memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]); + cp += 2+ni->ni_wme_ie[1]; + } + error = copyout(si, p, si->isi_len); + if (error) break; - case IEEE80211_IOC_TXPOWER: - if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) - error = EINVAL; - else - ireq->i_val = ic->ic_txpower; + p += si->isi_len; + space -= si->isi_len; + } + ireq->i_len -= space; + return error; +} + +static int +ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_txpow txpow; + int error; + + if (ireq->i_len != sizeof(txpow)) + return EINVAL; + error = copyin(ireq->i_data, &txpow, sizeof(txpow)); + if (error != 0) + return error; + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + txpow.it_txpow = ni->ni_txpower; + error = copyout(&txpow, ireq->i_data, sizeof(txpow)); + ieee80211_free_node(ni); + return error; +} + +static int +ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_wme_state *wme = &ic->ic_wme; + struct wmeParams *wmep; + int ac; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + + ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); + if (ac >= WME_NUM_AC) + ac = WME_AC_BE; + if (ireq->i_len & IEEE80211_WMEPARAM_BSS) + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + else + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + switch (ireq->i_type) { + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + ireq->i_val = wmep->wmep_logcwmin; + break; + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + ireq->i_val = wmep->wmep_logcwmax; + break; + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + ireq->i_val = wmep->wmep_aifsn; + break; + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + ireq->i_val = wmep->wmep_txopLimit; + break; + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + ireq->i_val = wmep->wmep_acm; + break; + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + ireq->i_val = !wmep->wmep_noackPolicy; + break; + } + return 0; +} + +static int +ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) +{ + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + int error = 0; + u_int kid, len, m; + u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + char tmpssid[IEEE80211_NWID_LEN]; + + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + ireq->i_len = ic->ic_des_esslen; + memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); break; default: - error = EINVAL; + ireq->i_len = ic->ic_bss->ni_esslen; + memcpy(tmpssid, ic->ic_bss->ni_essid, + ireq->i_len); break; } + error = copyout(tmpssid, ireq->i_data, ireq->i_len); break; - case SIOCS80211: - error = suser(curthread); - if (error) - break; - ireq = (struct ieee80211req *) data; - switch (ireq->i_type) { - case IEEE80211_IOC_SSID: - if (ireq->i_val != 0 || - ireq->i_len > IEEE80211_NWID_LEN) { - error = EINVAL; - break; - } - error = copyin(ireq->i_data, tmpssid, ireq->i_len); - if (error) - break; - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = ireq->i_len; - memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); - error = ENETRESET; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + case IEEE80211_IOC_WEP: + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) + ireq->i_val = IEEE80211_WEP_OFF; + else if (ic->ic_flags & IEEE80211_F_DROPUNENC) + ireq->i_val = IEEE80211_WEP_ON; + else + ireq->i_val = IEEE80211_WEP_MIXED; + break; + case IEEE80211_IOC_WEPKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + len = (u_int) ic->ic_nw_keys[kid].wk_keylen; + /* NB: only root can read WEP keys */ + if (suser(curthread) == 0) { + bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); + } else { + bzero(tmpkey, len); + } + ireq->i_len = len; + error = copyout(tmpkey, ireq->i_data, len); + break; + case IEEE80211_IOC_NUMWEPKEYS: + ireq->i_val = IEEE80211_WEP_NKID; + break; + case IEEE80211_IOC_WEPTXKEY: + ireq->i_val = ic->ic_def_txkey; + break; + case IEEE80211_IOC_AUTHMODE: + if (ic->ic_flags & IEEE80211_F_WPA) + ireq->i_val = IEEE80211_AUTH_WPA; + else + ireq->i_val = ic->ic_bss->ni_authmode; + break; + case IEEE80211_IOC_CHANNEL: + ireq->i_val = ieee80211_chan2ieee(ic, getcurchan(ic)); + break; + case IEEE80211_IOC_POWERSAVE: + if (ic->ic_flags & IEEE80211_F_PMGTON) + ireq->i_val = IEEE80211_POWERSAVE_ON; + else + ireq->i_val = IEEE80211_POWERSAVE_OFF; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ireq->i_val = ic->ic_lintval; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + ireq->i_val = ic->ic_rtsthreshold; + break; + case IEEE80211_IOC_PROTMODE: + ireq->i_val = ic->ic_protmode; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return EINVAL; + ireq->i_val = ic->ic_txpowlimit; + break; + case IEEE80211_IOC_MCASTCIPHER: + ireq->i_val = rsn->rsn_mcastcipher; + break; + case IEEE80211_IOC_MCASTKEYLEN: + ireq->i_val = rsn->rsn_mcastkeylen; + break; + case IEEE80211_IOC_UCASTCIPHERS: + ireq->i_val = 0; + for (m = 0x1; m != 0; m <<= 1) + if (rsn->rsn_ucastcipherset & m) + ireq->i_val |= 1<i_val = rsn->rsn_ucastcipher; + break; + case IEEE80211_IOC_UCASTKEYLEN: + ireq->i_val = rsn->rsn_ucastkeylen; + break; + case IEEE80211_IOC_KEYMGTALGS: + ireq->i_val = rsn->rsn_keymgmtset; + break; + case IEEE80211_IOC_RSNCAPS: + ireq->i_val = rsn->rsn_caps; + break; + case IEEE80211_IOC_WPA: + switch (ic->ic_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + ireq->i_val = 1; + break; + case IEEE80211_F_WPA2: + ireq->i_val = 2; + break; + case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: + ireq->i_val = 3; break; - case IEEE80211_IOC_WEP: + default: + ireq->i_val = 0; + break; + } + break; + case IEEE80211_IOC_CHANLIST: + error = ieee80211_ioctl_getchanlist(ic, ireq); + break; + case IEEE80211_IOC_ROAMING: + ireq->i_val = ic->ic_roaming; + break; + case IEEE80211_IOC_PRIVACY: + ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; + break; + case IEEE80211_IOC_DROPUNENCRYPTED: + ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; + break; + case IEEE80211_IOC_COUNTERMEASURES: + ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; + break; + case IEEE80211_IOC_DRIVER_CAPS: + ireq->i_val = ic->ic_caps>>16; + ireq->i_len = ic->ic_caps&0xffff; + break; + case IEEE80211_IOC_WME: + ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0; + break; + case IEEE80211_IOC_HIDESSID: + ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; + break; + case IEEE80211_IOC_APBRIDGE: + ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; + break; + case IEEE80211_IOC_OPTIE: + if (ic->ic_opt_ie == NULL) + return EINVAL; + /* NB: truncate, caller can check length */ + if (ireq->i_len > ic->ic_opt_ie_len) + ireq->i_len = ic->ic_opt_ie_len; + error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_WPAKEY: + error = ieee80211_ioctl_getkey(ic, ireq); + break; + case IEEE80211_IOC_CHANINFO: + error = ieee80211_ioctl_getchaninfo(ic, ireq); + break; + case IEEE80211_IOC_BSSID: + if (ireq->i_len != IEEE80211_ADDR_LEN) + return EINVAL; + error = copyout(ic->ic_state == IEEE80211_S_RUN ? + ic->ic_bss->ni_bssid : + ic->ic_des_bssid, + ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_WPAIE: + error = ieee80211_ioctl_getwpaie(ic, ireq); + break; + case IEEE80211_IOC_SCAN_RESULTS: + error = ieee80211_ioctl_getscanresults(ic, ireq); + break; + case IEEE80211_IOC_STA_STATS: + error = ieee80211_ioctl_getstastats(ic, ireq); + break; + case IEEE80211_IOC_TXPOWMAX: + ireq->i_val = ic->ic_bss->ni_txpower; + break; + case IEEE80211_IOC_STA_TXPOW: + error = ieee80211_ioctl_getstatxpow(ic, ireq); + break; + case IEEE80211_IOC_STA_INFO: + error = ieee80211_ioctl_getstainfo(ic, ireq); + break; + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ + error = ieee80211_ioctl_getwmeparam(ic, ireq); + break; + case IEEE80211_IOC_DTIM_PERIOD: + ireq->i_val = ic->ic_dtim_period; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + /* NB: get from ic_bss for station mode */ + ireq->i_val = ic->ic_bss->ni_intval; + break; + default: + error = EINVAL; + break; + } + return error; +} + +static int +ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + int error; + void *ie; + + /* + * 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 */ + MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK); + if (ie == NULL) + return ENOMEM; + error = copyin(ireq->i_data, ie, ireq->i_len); + /* XXX sanity check data? */ + if (ic->ic_opt_ie != NULL) + FREE(ic->ic_opt_ie, M_DEVBUF); + ic->ic_opt_ie = ie; + ic->ic_opt_ie_len = ireq->i_len; + return 0; +} + +static int +ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_key ik; + struct ieee80211_node *ni; + struct ieee80211_key *wk; + u_int16_t kid; + int error; + + if (ireq->i_len != sizeof(ik)) + return EINVAL; + error = copyin(ireq->i_data, &ik, sizeof(ik)); + if (error) + return error; + /* NB: cipher support is verified by ieee80211_crypt_newkey */ + /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ + if (ik.ik_keylen > sizeof(ik.ik_keydata)) + return E2BIG; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + /* XXX unicast keys currently must be tx/rx */ + if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) + return EINVAL; + if (ic->ic_opmode == IEEE80211_M_STA) { + ni = ic->ic_bss; + if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) + return EADDRNOTAVAIL; + } else { + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr); + if (ni == NULL) + return ENOENT; + } + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + wk = &ic->ic_nw_keys[kid]; + ni = NULL; + } + error = 0; + ieee80211_key_update_begin(ic); + if (ieee80211_crypto_newkey(ic, ik.ik_type, wk)) { + wk->wk_keylen = ik.ik_keylen; + /* NB: MIC presence is implied by cipher type */ + if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) + wk->wk_keylen = IEEE80211_KEYBUF_SIZE; + wk->wk_keyrsc = ik.ik_keyrsc; + wk->wk_keytsc = 0; /* new key, reset */ + wk->wk_flags |= + ik.ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV); + memset(wk->wk_key, 0, sizeof(wk->wk_key)); + memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); + if (!ieee80211_crypto_setkey(ic, wk, + ni != NULL ? ni->ni_macaddr : ik.ik_macaddr)) + error = EIO; + else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) + ic->ic_def_txkey = kid; + } else + error = ENXIO; + ieee80211_key_update_end(ic); + if (ni != NULL) + ieee80211_free_node(ni); + return error; +} + +static int +ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_del_key dk; + int kid, error; + + if (ireq->i_len != sizeof(dk)) + return EINVAL; + error = copyin(ireq->i_data, &dk, sizeof(dk)); + if (error) + return error; + kid = dk.idk_keyix; + /* XXX u_int8_t -> u_int16_t */ + if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { + struct ieee80211_node *ni; + + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, dk.idk_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + /* XXX error return */ + ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); + ieee80211_free_node(ni); + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + /* XXX error return */ + ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); + } + return 0; +} + +static void +domlme(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211req_mlme *mlme = arg; + + if (ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ic, ni, + mlme->im_op == IEEE80211_MLME_DEAUTH ? + IEEE80211_FC0_SUBTYPE_DEAUTH : + IEEE80211_FC0_SUBTYPE_DISASSOC, + mlme->im_reason); + } + ieee80211_node_leave(ic, ni); +} + +static int +ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_mlme mlme; + struct ieee80211_node *ni; + int error; + + if (ireq->i_len != sizeof(mlme)) + return EINVAL; + error = copyin(ireq->i_data, &mlme, sizeof(mlme)); + if (error) + return error; + switch (mlme.im_op) { + case IEEE80211_MLME_ASSOC: + if (ic->ic_opmode != IEEE80211_M_STA) + return EINVAL; + /* XXX must be in S_SCAN state? */ + + if (ic->ic_des_esslen != 0) { /* - * These cards only support one mode so - * we just turn wep on if what ever is - * passed in is not OFF. + * Desired ssid specified; must match both bssid and + * ssid to distinguish ap advertising multiple ssid's. */ - if (ireq->i_val == IEEE80211_WEP_OFF) { - ic->ic_flags &= ~IEEE80211_F_WEPON; + ni = ieee80211_find_node_with_ssid(&ic->ic_scan, + mlme.im_macaddr, + ic->ic_des_esslen, ic->ic_des_essid); + } else { + /* + * Normal case; just match bssid. + */ + ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr); + } + if (ni == NULL) + return EINVAL; + if (!ieee80211_sta_join(ic, ni)) { + ieee80211_free_node(ni); + return EINVAL; + } + break; + case IEEE80211_MLME_DISASSOC: + case IEEE80211_MLME_DEAUTH: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + /* XXX not quite right */ + ieee80211_new_state(ic, IEEE80211_S_INIT, + mlme.im_reason); + break; + case IEEE80211_M_HOSTAP: + /* NB: the broadcast address means do 'em all */ + if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) { + if (ic->ic_sta == NULL || + (ni = ieee80211_find_node(ic->ic_sta, + mlme.im_macaddr)) == NULL) + return EINVAL; + domlme(&mlme, ni); + ieee80211_free_node(ni); } else { - ic->ic_flags |= IEEE80211_F_WEPON; + if (ic->ic_sta != NULL) + ieee80211_iterate_nodes(ic->ic_sta, + domlme, &mlme); } - error = ENETRESET; break; - case IEEE80211_IOC_WEPKEY: - if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { - error = EINVAL; - break; - } - kid = (u_int) ireq->i_val; - if (kid >= IEEE80211_WEP_NKID) { - error = EINVAL; - break; - } - if (ireq->i_len > sizeof(tmpkey)) { - error = EINVAL; - break; + default: + return EINVAL; + } + break; + case IEEE80211_MLME_AUTHORIZE: + case IEEE80211_MLME_UNAUTHORIZE: + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + return EINVAL; + if (ic->ic_sta == NULL) + 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(ic, ni); + else + ieee80211_node_unauthorize(ic, ni); + ieee80211_free_node(ni); + break; + default: + return EINVAL; + } + return 0; +} + +static int +ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + u_int8_t mac[IEEE80211_ADDR_LEN]; + const struct ieee80211_aclator *acl = ic->ic_acl; + int error; + + if (ireq->i_len != sizeof(mac)) + return EINVAL; + error = copyin(ireq->i_data, mac, ireq->i_len); + if (error) + return error; + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return EINVAL; + ic->ic_acl = acl; + } + if (ireq->i_type == IEEE80211_IOC_ADDMAC) + acl->iac_add(ic, mac); + else + acl->iac_remove(ic, mac); + return 0; +} + +static int +ieee80211_ioctl_maccmd(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + const struct ieee80211_aclator *acl = ic->ic_acl; + + switch (ireq->i_val) { + case IEEE80211_MACCMD_POLICY_OPEN: + case IEEE80211_MACCMD_POLICY_ALLOW: + case IEEE80211_MACCMD_POLICY_DENY: + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return EINVAL; + ic->ic_acl = acl; + } + acl->iac_setpolicy(ic, ireq->i_val); + break; + case IEEE80211_MACCMD_FLUSH: + if (acl != NULL) + acl->iac_flush(ic); + /* NB: silently ignore when not in use */ + break; + case IEEE80211_MACCMD_DETACH: + if (acl != NULL) { + ic->ic_acl = NULL; + acl->iac_detach(ic); + } + break; + default: + return EINVAL; + } + return 0; +} + +static int +ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211req_chanlist list; + u_char chanlist[IEEE80211_CHAN_BYTES]; + int i, j, error; + + if (ireq->i_len != sizeof(list)) + return EINVAL; + error = copyin(ireq->i_data, &list, sizeof(list)); + if (error) + return error; + memset(chanlist, 0, sizeof(chanlist)); + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { + /* + * NB: silently discard unavailable channels so users + * can specify 1-255 to get all available channels. + */ + if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) + setbit(chanlist, i); + } + if (ic->ic_ibss_chan == NULL || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(chanlist, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + goto found; } - memset(tmpkey, 0, sizeof(tmpkey)); - error = copyin(ireq->i_data, tmpkey, ireq->i_len); - if (error) - break; - memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey, - sizeof(tmpkey)); - ic->ic_nw_keys[kid].wk_len = ireq->i_len; - error = ENETRESET; + return EINVAL; /* no active channels */ +found: + ; + } + memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + return IS_UP_AUTO(ic) ? ENETRESET : 0; +} + +static int +ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_txpow txpow; + int error; + + if (ireq->i_len != sizeof(txpow)) + return EINVAL; + error = copyin(ireq->i_data, &txpow, sizeof(txpow)); + if (error != 0) + return error; + if (ic->ic_sta == NULL) + return EINVAL; + ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr); + if (ni == NULL) + return EINVAL; /* XXX */ + ni->ni_txpower = txpow.it_txpow; + ieee80211_free_node(ni); + return error; +} + +static int +ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq) +{ + struct ieee80211_wme_state *wme = &ic->ic_wme; + struct wmeParams *wmep, *chanp; + int isbss, ac; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + + isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); + ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); + if (ac >= WME_NUM_AC) + ac = WME_AC_BE; + if (isbss) { + chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + } else { + chanp = &wme->wme_chanParams.cap_wmeParams[ac]; + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + } + switch (ireq->i_type) { + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + if (isbss) { + wmep->wmep_logcwmin = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_logcwmin = ireq->i_val; + } else { + wmep->wmep_logcwmin = chanp->wmep_logcwmin = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + if (isbss) { + wmep->wmep_logcwmax = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_logcwmax = ireq->i_val; + } else { + wmep->wmep_logcwmax = chanp->wmep_logcwmax = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + if (isbss) { + wmep->wmep_aifsn = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_aifsn = ireq->i_val; + } else { + wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; + } + break; + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + if (isbss) { + wmep->wmep_txopLimit = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_txopLimit = ireq->i_val; + } else { + wmep->wmep_txopLimit = chanp->wmep_txopLimit = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + wmep->wmep_acm = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_acm = ireq->i_val; + break; + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ + wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = + (ireq->i_val) == 0; + break; + } + ieee80211_wme_updateparams(ic); + return 0; +} + +static int +cipher2cap(int cipher) +{ + switch (cipher) { + case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; + case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; + case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; + case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; + case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; + } + return 0; +} + +static int +ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq) +{ + static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; + struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + int error; + const struct ieee80211_authenticator *auth; + u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + char tmpssid[IEEE80211_NWID_LEN]; + u_int8_t tmpbssid[IEEE80211_ADDR_LEN]; + struct ieee80211_key *k; + int j, caps; + u_int kid; + + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) + return EINVAL; + error = copyin(ireq->i_data, tmpssid, ireq->i_len); + if (error) break; - case IEEE80211_IOC_WEPTXKEY: - kid = (u_int) ireq->i_val; - if (kid >= IEEE80211_WEP_NKID) { - error = EINVAL; - break; - } - ic->ic_wep_txkey = kid; - error = ENETRESET; + memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); + ic->ic_des_esslen = ireq->i_len; + memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); + error = ENETRESET; + break; + case IEEE80211_IOC_WEP: + switch (ireq->i_val) { + case IEEE80211_WEP_OFF: + ic->ic_flags &= ~IEEE80211_F_PRIVACY; + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; -#if 0 - case IEEE80211_IOC_AUTHMODE: - sc->wi_authmode = ireq->i_val; + case IEEE80211_WEP_ON: + ic->ic_flags |= IEEE80211_F_PRIVACY; + ic->ic_flags |= IEEE80211_F_DROPUNENC; break; -#endif - case IEEE80211_IOC_CHANNEL: - /* XXX 0xffff overflows 16-bit signed */ - if (ireq->i_val == 0 || - ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) - ic->ic_des_chan = IEEE80211_CHAN_ANYC; - else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, ireq->i_val)) { - error = EINVAL; - break; - } else - ic->ic_ibss_chan = ic->ic_des_chan = - &ic->ic_channels[ireq->i_val]; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - error = ENETRESET; - break; - default: - if (ic->ic_opmode == IEEE80211_M_STA) { - if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && - ic->ic_bss->ni_chan != ic->ic_des_chan) - error = ENETRESET; - } else { - if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) - error = ENETRESET; - } - break; - } + case IEEE80211_WEP_MIXED: + ic->ic_flags |= IEEE80211_F_PRIVACY; + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; break; - case IEEE80211_IOC_POWERSAVE: - switch (ireq->i_val) { - case IEEE80211_POWERSAVE_OFF: - if (ic->ic_flags & IEEE80211_F_PMGTON) { - ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = ENETRESET; - } - break; - case IEEE80211_POWERSAVE_ON: - if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) - error = EINVAL; - else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { - ic->ic_flags |= IEEE80211_F_PMGTON; - error = ENETRESET; - } - break; - default: - error = EINVAL; - break; - } + } + 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]; + if (ireq->i_len == 0) { + /* zero-len =>'s delete any existing key */ + (void) ieee80211_crypto_delkey(ic, k); break; - case IEEE80211_IOC_POWERSAVESLEEP: - if (ireq->i_val < 0) { - error = EINVAL; - break; - } - ic->ic_lintval = ireq->i_val; - error = ENETRESET; + } + if (ireq->i_len > sizeof(tmpkey)) + return EINVAL; + memset(tmpkey, 0, sizeof(tmpkey)); + error = copyin(ireq->i_data, tmpkey, ireq->i_len); + if (error) break; - case IEEE80211_IOC_RTSTHRESHOLD: - if (!(IEEE80211_RTS_MIN < ireq->i_val && - ireq->i_val < IEEE80211_RTS_MAX)) { + ieee80211_key_update_begin(ic); + if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) { + k->wk_keylen = ireq->i_len; + k->wk_flags |= + IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); + if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) error = EINVAL; - break; - } - ic->ic_rtsthreshold = ireq->i_val; + } else + error = EINVAL; + ieee80211_key_update_end(ic); + break; + case IEEE80211_IOC_WEPTXKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + ic->ic_def_txkey = kid; + error = ERESTART; /* push to hardware */ + break; + case IEEE80211_IOC_AUTHMODE: + switch (ireq->i_val) { + case IEEE80211_AUTH_WPA: + case IEEE80211_AUTH_8021X: /* 802.1x */ + case IEEE80211_AUTH_OPEN: /* open */ + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_AUTO: /* auto */ + auth = ieee80211_authenticator_get(ireq->i_val); + if (auth == NULL) + return EINVAL; + break; + default: + return EINVAL; + } + switch (ireq->i_val) { + case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + ireq->i_val = IEEE80211_AUTH_8021X; + break; + case IEEE80211_AUTH_OPEN: /* open */ + ic->ic_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; + /* both require a key so mark the PRIVACY capability */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + break; + case IEEE80211_AUTH_AUTO: /* auto */ + ic->ic_flags &= ~IEEE80211_F_WPA; + /* XXX PRIVACY handling? */ + /* XXX what's the right way to do this? */ + break; + } + /* NB: authenticator attach/detach happens on state change */ + ic->ic_bss->ni_authmode = ireq->i_val; + /* XXX mixed/mode/usage? */ + ic->ic_auth = auth; + error = ENETRESET; + break; + case IEEE80211_IOC_CHANNEL: + /* XXX 0xffff overflows 16-bit signed */ + if (ireq->i_val == 0 || + ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || + isclr(ic->ic_chan_active, ireq->i_val)) { + return EINVAL; + } else + ic->ic_ibss_chan = ic->ic_des_chan = + &ic->ic_channels[ireq->i_val]; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: error = ENETRESET; break; - case IEEE80211_IOC_PROTMODE: - if (ireq->i_val > IEEE80211_PROT_RTSCTS) { - error = EINVAL; - break; - } - ic->ic_protmode = ireq->i_val; - /* NB: if not operating in 11g this can wait */ - if (ic->ic_curmode == IEEE80211_MODE_11G) + default: + /* + * If the desired channel has changed (to something + * other than any) and we're not already scanning, + * then kick the state machine. + */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + ic->ic_bss->ni_chan != ic->ic_des_chan && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) error = ENETRESET; break; - case IEEE80211_IOC_TXPOWER: - if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) { - error = EINVAL; - break; + } + if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR) + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_POWERSAVE: + switch (ireq->i_val) { + case IEEE80211_POWERSAVE_OFF: + if (ic->ic_flags & IEEE80211_F_PMGTON) { + ic->ic_flags &= ~IEEE80211_F_PMGTON; + error = ENETRESET; } - if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && - ireq->i_val < IEEE80211_TXPOWER_MAX)) { + break; + case IEEE80211_POWERSAVE_ON: + if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) error = EINVAL; - break; + else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { + ic->ic_flags |= IEEE80211_F_PMGTON; + error = ENETRESET; } - ic->ic_txpower = ireq->i_val; - error = ENETRESET; break; default: error = EINVAL; break; } break; + case IEEE80211_IOC_POWERSAVESLEEP: + if (ireq->i_val < 0) + return EINVAL; + ic->ic_lintval = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + if (!(IEEE80211_RTS_MIN < ireq->i_val && + ireq->i_val < IEEE80211_RTS_MAX)) + return EINVAL; + ic->ic_rtsthreshold = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_PROTMODE: + if (ireq->i_val > IEEE80211_PROT_RTSCTS) + return EINVAL; + ic->ic_protmode = ireq->i_val; + /* NB: if not operating in 11g this can wait */ + if (ic->ic_curmode == IEEE80211_MODE_11G) + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return EINVAL; + if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && + ireq->i_val < IEEE80211_TXPOWER_MAX)) + return EINVAL; + ic->ic_txpowlimit = ireq->i_val; + error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0; + break; + case IEEE80211_IOC_ROAMING: + if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && + ireq->i_val <= IEEE80211_ROAMING_MANUAL)) + return EINVAL; + ic->ic_roaming = ireq->i_val; + /* XXXX reset? */ + break; + case IEEE80211_IOC_PRIVACY: + if (ireq->i_val) { + /* XXX check for key state? */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + } else + ic->ic_flags &= ~IEEE80211_F_PRIVACY; + break; + case IEEE80211_IOC_DROPUNENCRYPTED: + if (ireq->i_val) + ic->ic_flags |= IEEE80211_F_DROPUNENC; + else + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + break; + case IEEE80211_IOC_WPAKEY: + error = ieee80211_ioctl_setkey(ic, ireq); + break; + case IEEE80211_IOC_DELKEY: + error = ieee80211_ioctl_delkey(ic, ireq); + break; + case IEEE80211_IOC_MLME: + error = ieee80211_ioctl_setmlme(ic, ireq); + break; + case IEEE80211_IOC_OPTIE: + error = ieee80211_ioctl_setoptie(ic, ireq); + break; + case IEEE80211_IOC_COUNTERMEASURES: + if (ireq->i_val) { + if ((ic->ic_flags & IEEE80211_F_WPA) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_COUNTERM; + } else + ic->ic_flags &= ~IEEE80211_F_COUNTERM; + break; + case IEEE80211_IOC_WPA: + if (ireq->i_val > 3) + return EINVAL; + /* XXX verify ciphers available */ + ic->ic_flags &= ~IEEE80211_F_WPA; + switch (ireq->i_val) { + case 1: + ic->ic_flags |= IEEE80211_F_WPA1; + break; + case 2: + ic->ic_flags |= IEEE80211_F_WPA2; + break; + case 3: + ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; + break; + } + error = ENETRESET; /* XXX? */ + break; + case IEEE80211_IOC_WME: + if (ireq->i_val) { + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + ic->ic_flags |= IEEE80211_F_WME; + } else + ic->ic_flags &= ~IEEE80211_F_WME; + error = ENETRESET; /* XXX maybe not for station? */ + break; + case IEEE80211_IOC_HIDESSID: + if (ireq->i_val) + ic->ic_flags |= IEEE80211_F_HIDESSID; + else + ic->ic_flags &= ~IEEE80211_F_HIDESSID; + error = ENETRESET; + break; + case IEEE80211_IOC_APBRIDGE: + if (ireq->i_val == 0) + ic->ic_flags |= IEEE80211_F_NOBRIDGE; + else + ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; + break; + case IEEE80211_IOC_MCASTCIPHER: + if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 && + !ieee80211_crypto_available(ireq->i_val)) + return EINVAL; + rsn->rsn_mcastcipher = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_MCASTKEYLEN: + if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) + return EINVAL; + /* XXX no way to verify driver capability */ + rsn->rsn_mcastkeylen = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_UCASTCIPHERS: + /* + * Convert user-specified cipher set to the set + * we can support (via hardware or software). + * NB: this logic intentionally ignores unknown and + * unsupported ciphers so folks can specify 0xff or + * similar and get all available ciphers. + */ + caps = 0; + for (j = 1; j < 32; j++) /* NB: skip WEP */ + if ((ireq->i_val & (1<ic_caps & cipher2cap(j)) || + ieee80211_crypto_available(j))) + caps |= 1<rsn_ucastcipherset = caps; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_UCASTCIPHER: + if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0) + return EINVAL; + rsn->rsn_ucastcipher = ireq->i_val; + break; + case IEEE80211_IOC_UCASTKEYLEN: + if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE)) + return EINVAL; + /* XXX no way to verify driver capability */ + rsn->rsn_ucastkeylen = ireq->i_val; + break; + case IEEE80211_IOC_DRIVER_CAPS: + /* NB: for testing */ + ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) | + ((u_int16_t) ireq->i_len); + break; + case IEEE80211_IOC_KEYMGTALGS: + /* XXX check */ + rsn->rsn_keymgmtset = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_RSNCAPS: + /* XXX check */ + rsn->rsn_caps = ireq->i_val; + error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0; + break; + case IEEE80211_IOC_BSSID: + /* NB: should only be set when in STA mode */ + if (ic->ic_opmode != IEEE80211_M_STA) + return EINVAL; + if (ireq->i_len != sizeof(tmpbssid)) + return EINVAL; + error = copyin(ireq->i_data, tmpbssid, ireq->i_len); + if (error) + break; + IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid); + if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid)) + ic->ic_flags &= ~IEEE80211_F_DESBSSID; + else + ic->ic_flags |= IEEE80211_F_DESBSSID; + error = ENETRESET; + break; + case IEEE80211_IOC_CHANLIST: + error = ieee80211_ioctl_setchanlist(ic, ireq); + break; + case IEEE80211_IOC_SCAN_REQ: + if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */ + break; + error = ieee80211_setupscan(ic, ic->ic_chan_avail); + if (error == 0) /* XXX background scan */ + error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case IEEE80211_IOC_ADDMAC: + case IEEE80211_IOC_DELMAC: + error = ieee80211_ioctl_macmac(ic, ireq); + break; + case IEEE80211_IOC_MACCMD: + error = ieee80211_ioctl_maccmd(ic, ireq); + break; + case IEEE80211_IOC_STA_TXPOW: + error = ieee80211_ioctl_setstatxpow(ic, ireq); + break; + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ + error = ieee80211_ioctl_setwmeparam(ic, ireq); + break; + case IEEE80211_IOC_DTIM_PERIOD: + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return EINVAL; + if (IEEE80211_DTIM_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_DTIM_MAX) { + ic->ic_dtim_period = ireq->i_val; + error = ENETRESET; /* requires restart */ + } else + error = EINVAL; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return EINVAL; + if (IEEE80211_BINTVAL_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_BINTVAL_MAX) { + ic->ic_lintval = ireq->i_val; + error = ENETRESET; /* requires restart */ + } else + error = EINVAL; + break; + default: + error = EINVAL; + break; + } + if (error == ENETRESET && !IS_UP_AUTO(ic)) + error = 0; + return error; +} + +int +ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data) +{ + struct ifnet *ifp = ic->ic_ifp; + int error = 0; + struct ifreq *ifr; + struct ifaddr *ifa; /* XXX */ + + switch (cmd) { + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, (struct ifreq *) data, + &ic->ic_media, cmd); + break; + case SIOCG80211: + error = ieee80211_ioctl_get80211(ic, cmd, + (struct ieee80211req *) data); + break; + case SIOCS80211: + error = suser(curthread); + if (error == 0) + error = ieee80211_ioctl_set80211(ic, cmd, + (struct ieee80211req *) data); + break; case SIOCGIFGENERIC: - error = ieee80211_cfgget(ifp, cmd, data); + error = ieee80211_cfgget(ic, cmd, data); break; case SIOCSIFGENERIC: error = suser(curthread); if (error) break; - error = ieee80211_cfgset(ifp, cmd, data); + error = ieee80211_cfgset(ic, cmd, data); break; case SIOCG80211STATS: ifr = (struct ifreq *)data; diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index d8c2b02..3c63e13 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -37,7 +37,61 @@ /* * IEEE 802.11 ioctls. */ +#include +#include +#include +/* + * Per/node (station) statistics available when operating as an AP. + */ +struct ieee80211_nodestats { + u_int32_t ns_rx_data; /* rx data frames */ + u_int32_t ns_rx_mgmt; /* rx management frames */ + u_int32_t ns_rx_ctrl; /* rx control frames */ + u_int32_t ns_rx_ucast; /* rx unicast frames */ + u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */ + u_int64_t ns_rx_bytes; /* rx data count (bytes) */ + u_int64_t ns_rx_beacons; /* rx beacon frames */ + u_int32_t ns_rx_proberesp; /* rx probe response frames */ + + u_int32_t ns_rx_dup; /* rx discard 'cuz dup */ + u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ + u_int32_t ns_rx_wepfail; /* rx wep processing failed */ + u_int32_t ns_rx_demicfail; /* rx demic failed */ + u_int32_t ns_rx_decap; /* rx decapsulation failed */ + u_int32_t ns_rx_defrag; /* rx defragmentation failed */ + u_int32_t ns_rx_disassoc; /* rx disassociation */ + u_int32_t ns_rx_deauth; /* rx deauthentication */ + u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ + u_int32_t ns_rx_unauth; /* rx on unauthorized port */ + u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ + + u_int32_t ns_tx_data; /* tx data frames */ + u_int32_t ns_tx_mgmt; /* tx management frames */ + u_int32_t ns_tx_ucast; /* tx unicast frames */ + u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */ + u_int64_t ns_tx_bytes; /* tx data count (bytes) */ + u_int32_t ns_tx_probereq; /* tx probe request frames */ + + u_int32_t ns_tx_novlantag; /* tx discard 'cuz no tag */ + u_int32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */ + + u_int32_t ns_ps_discard; /* ps discard 'cuz of age */ + + /* MIB-related state */ + u_int32_t ns_tx_assoc; /* [re]associations */ + u_int32_t ns_tx_assoc_fail; /* [re]association failures */ + u_int32_t ns_tx_auth; /* [re]authentications */ + u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/ + u_int32_t ns_tx_deauth; /* deauthentications */ + u_int32_t ns_tx_deauth_code; /* last deauth reason */ + u_int32_t ns_tx_disassoc; /* disassociations */ + u_int32_t ns_tx_disassoc_code; /* last disassociation reason */ +}; + +/* + * Summary statistics. + */ struct ieee80211_stats { u_int32_t is_rx_badversion; /* rx frame with bad version */ u_int32_t is_rx_tooshort; /* rx frame too short */ @@ -46,11 +100,13 @@ struct ieee80211_stats { u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ u_int32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ u_int32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ - u_int32_t is_rx_nowep; /* rx w/ wep but wep !config */ + u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ + u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ u_int32_t is_rx_wepfail; /* rx wep processing failed */ u_int32_t is_rx_decap; /* rx decapsulation failed */ u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ u_int32_t is_rx_ctl; /* rx discard ctrl frames */ + u_int32_t is_rx_beacon; /* rx beacon frames */ u_int32_t is_rx_rstoobig; /* rx rate set truncated */ u_int32_t is_rx_elem_missing; /* rx required element missing*/ u_int32_t is_rx_elem_toobig; /* rx element too big */ @@ -62,26 +118,228 @@ struct ieee80211_stats { u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ u_int32_t is_rx_auth_fail; /* rx sta auth failure */ + u_int32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */ u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ + u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ u_int32_t is_rx_deauth; /* rx deauthentication */ u_int32_t is_rx_disassoc; /* rx disassociation */ u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ - u_int32_t is_rx_nombuf; /* rx failed for lack of mbuf */ + u_int32_t is_rx_nobuf; /* rx failed for lack of buf */ u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ u_int32_t is_rx_bad_auth; /* rx bad auth request */ - u_int32_t is_tx_nombuf; /* tx failed for lack of mbuf */ + u_int32_t is_rx_unauth; /* rx on unauthorized port */ + u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ + u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ + u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ + u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ + u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ + u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */ + u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ + u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ + u_int32_t is_rx_badcipher; /* rx failed 'cuz key type */ + u_int32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */ + u_int32_t is_rx_acl; /* rx discard 'cuz acl policy */ + u_int32_t is_tx_nobuf; /* tx failed for lack of buf */ u_int32_t is_tx_nonode; /* tx failed for no node */ u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ + u_int32_t is_tx_badcipher; /* tx failed 'cuz key type */ + u_int32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */ + u_int32_t is_tx_noheadroom; /* tx failed 'cuz no space */ u_int32_t is_scan_active; /* active scans started */ u_int32_t is_scan_passive; /* passive scans started */ u_int32_t is_node_timeout; /* nodes timed out inactivity */ u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ + u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */ + u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ + u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ + u_int32_t is_crypto_tkipcm; /* tkip counter measures */ + u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ + u_int32_t is_crypto_wep; /* wep crypto done in s/w */ + u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */ + u_int32_t is_crypto_setkey_nokey; /* no key index for setkey */ + u_int32_t is_crypto_delkey; /* driver key delete failed */ + u_int32_t is_crypto_badcipher; /* unknown cipher */ + u_int32_t is_crypto_nocipher; /* cipher not available */ + u_int32_t is_crypto_attachfail; /* cipher attach failed */ + u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */ + u_int32_t is_crypto_keyfail; /* driver key alloc failed */ + u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ + u_int32_t is_ibss_norate; /* merge failed-rate mismatch */ + u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ + u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ + u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */ +}; + +/* + * Max size of optional information elements. We artificially + * constrain this; it's limited only by the max frame size (and + * the max parameter size of the wireless extensions). + */ +#define IEEE80211_MAX_OPT_IE 256 + +/* + * WPA/RSN get/set key request. Specify the key/cipher + * type and whether the key is to be used for sending and/or + * receiving. The key index should be set only when working + * with global keys (use IEEE80211_KEYIX_NONE for ``no index''). + * Otherwise a unicast/pairwise key is specified by the bssid + * (on a station) or mac address (on an ap). They key length + * must include any MIC key data; otherwise it should be no + more than IEEE80211_KEYBUF_SIZE. + */ +struct ieee80211req_key { + u_int8_t ik_type; /* key/cipher type */ + u_int8_t ik_pad; + u_int16_t ik_keyix; /* key index */ + u_int8_t ik_keylen; /* key length in bytes */ + u_int8_t ik_flags; +/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */ +#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */ + u_int8_t ik_macaddr[IEEE80211_ADDR_LEN]; + u_int64_t ik_keyrsc; /* key receive sequence counter */ + u_int64_t ik_keytsc; /* key transmit sequence counter */ + u_int8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; +}; + +/* + * Delete a key either by index or address. Set the index + * to IEEE80211_KEYIX_NONE when deleting a unicast key. + */ +struct ieee80211req_del_key { + u_int8_t idk_keyix; /* key index */ + u_int8_t idk_macaddr[IEEE80211_ADDR_LEN]; +}; + +/* + * MLME state manipulation request. IEEE80211_MLME_ASSOC + * only makes sense when operating as a station. The other + * requests can be used when operating as a station or an + * ap (to effect a station). + */ +struct ieee80211req_mlme { + u_int8_t im_op; /* operation to perform */ +#define IEEE80211_MLME_ASSOC 1 /* associate station */ +#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */ +#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */ +#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */ +#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */ + u_int16_t im_reason; /* 802.11 reason code */ + u_int8_t im_macaddr[IEEE80211_ADDR_LEN]; +}; + +/* + * MAC ACL operations. + */ +enum { + IEEE80211_MACCMD_POLICY_OPEN = 0, /* set policy: no ACL's */ + IEEE80211_MACCMD_POLICY_ALLOW = 1, /* set policy: allow traffic */ + IEEE80211_MACCMD_POLICY_DENY = 2, /* set policy: deny traffic */ + IEEE80211_MACCMD_FLUSH = 3, /* flush ACL database */ + IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */ }; +/* + * Set the active channel list. Note this list is + * intersected with the available channel list in + * calculating the set of channels actually used in + * scanning. + */ +struct ieee80211req_chanlist { + u_int8_t ic_channels[IEEE80211_CHAN_BYTES]; +}; + +/* + * Get the active channel list info. + */ +struct ieee80211req_chaninfo { + u_int ic_nchans; + struct ieee80211_channel ic_chans[IEEE80211_CHAN_MAX]; +}; + +/* + * Retrieve the WPA/RSN information element for an associated station. + */ +struct ieee80211req_wpaie { + u_int8_t wpa_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t wpa_ie[IEEE80211_MAX_OPT_IE]; +}; + +/* + * Retrieve per-node statistics. + */ +struct ieee80211req_sta_stats { + union { + /* NB: explicitly force 64-bit alignment */ + u_int8_t macaddr[IEEE80211_ADDR_LEN]; + u_int64_t pad; + } is_u; + struct ieee80211_nodestats is_stats; +}; + +/* + * Station information block; the mac address is used + * to retrieve other data like stats, unicast key, etc. + */ +struct ieee80211req_sta_info { + u_int16_t isi_len; /* length (mult of 4) */ + u_int16_t isi_freq; /* MHz */ + u_int16_t isi_flags; /* channel flags */ + u_int16_t isi_state; /* state flags */ + u_int8_t isi_authmode; /* authentication algorithm */ + u_int8_t isi_rssi; + u_int8_t isi_capinfo; /* capabilities */ + u_int8_t isi_erp; /* ERP element */ + u_int8_t isi_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t isi_nrates; + /* negotiated rates */ + u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE]; + u_int8_t isi_txrate; /* index to isi_rates[] */ + u_int16_t isi_ie_len; /* IE length */ + u_int16_t isi_associd; /* assoc response */ + u_int16_t isi_txpower; /* current tx power */ + u_int16_t isi_vlan; /* vlan tag */ + u_int16_t isi_txseqs[17]; /* seq to be transmitted */ + u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/ + u_int16_t isi_inact; /* inactivity timer */ + /* XXX frag state? */ + /* variable length IE data */ +}; + +/* + * Retrieve per-station information; to retrieve all + * specify a mac address of ff:ff:ff:ff:ff:ff. + */ +struct ieee80211req_sta_req { + union { + /* NB: explicitly force 64-bit alignment */ + u_int8_t macaddr[IEEE80211_ADDR_LEN]; + u_int64_t pad; + } is_u; + struct ieee80211req_sta_info info[1]; /* variable length */ +}; + +/* + * Get/set per-station tx power cap. + */ +struct ieee80211req_sta_txpow { + u_int8_t it_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t it_txpow; +}; + +/* + * WME parameters are set and return using i_val and i_len. + * i_val holds the value itself. i_len specifies the AC + * and, as appropriate, then high bit specifies whether the + * operation is to be applied to the BSS or ourself. + */ +#define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */ +#define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */ +#define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */ + #ifdef __FreeBSD__ /* * FreeBSD-style ioctls. @@ -123,11 +381,69 @@ struct ieee80211req { #define IEEE80211_PROTMODE_OFF 0 #define IEEE80211_PROTMODE_CTS 1 #define IEEE80211_PROTMODE_RTSCTS 2 -#define IEEE80211_IOC_TXPOWER 14 +#define IEEE80211_IOC_TXPOWER 14 /* global tx power limit */ +#define IEEE80211_IOC_BSSID 15 +#define IEEE80211_IOC_ROAMING 16 /* roaming mode */ +#define IEEE80211_IOC_PRIVACY 17 /* privacy invoked */ +#define IEEE80211_IOC_DROPUNENCRYPTED 18 /* discard unencrypted frames */ +#define IEEE80211_IOC_WPAKEY 19 +#define IEEE80211_IOC_DELKEY 20 +#define IEEE80211_IOC_MLME 21 +#define IEEE80211_IOC_OPTIE 22 /* optional info. element */ +#define IEEE80211_IOC_SCAN_REQ 23 +#define IEEE80211_IOC_SCAN_RESULTS 24 +#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */ +#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */ +#define IEEE80211_IOC_CHANLIST 27 /* channel list */ +#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */ +#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */ +#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */ +#define IEEE80211_IOC_MCASTCIPHER 31 /* multicast/default cipher */ +#define IEEE80211_IOC_MCASTKEYLEN 32 /* multicast key length */ +#define IEEE80211_IOC_UCASTCIPHERS 33 /* unicast cipher suites */ +#define IEEE80211_IOC_UCASTCIPHER 34 /* unicast cipher */ +#define IEEE80211_IOC_UCASTKEYLEN 35 /* unicast key length */ +#define IEEE80211_IOC_DRIVER_CAPS 36 /* driver capabilities */ +#define IEEE80211_IOC_KEYMGTALGS 37 /* key management algorithms */ +#define IEEE80211_IOC_RSNCAPS 38 /* RSN capabilities */ +#define IEEE80211_IOC_WPAIE 39 /* WPA information element */ +#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */ +#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */ +#define IEEE80211_IOC_CHANINFO 42 /* channel info list */ +#define IEEE80211_IOC_TXPOWMAX 43 /* max tx power for channel */ +#define IEEE80211_IOC_STA_TXPOW 44 /* per-station tx power limit */ +#define IEEE80211_IOC_STA_INFO 45 /* station/neighbor info */ +#define IEEE80211_IOC_WME_CWMIN 46 /* WME: ECWmin */ +#define IEEE80211_IOC_WME_CWMAX 47 /* WME: ECWmax */ +#define IEEE80211_IOC_WME_AIFS 48 /* WME: AIFSN */ +#define IEEE80211_IOC_WME_TXOPLIMIT 49 /* WME: txops limit */ +#define IEEE80211_IOC_WME_ACM 50 /* WME: ACM (bss only) */ +#define IEEE80211_IOC_WME_ACKPOLICY 51 /* WME: ACK policy (!bss only)*/ +#define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */ +#define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */ +#define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */ +#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */ -#ifndef IEEE80211_CHAN_ANY -#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ -#endif +/* + * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS. + */ +struct ieee80211req_scan_result { + u_int16_t isr_len; /* length (mult of 4) */ + u_int16_t isr_freq; /* MHz */ + u_int16_t isr_flags; /* channel flags */ + u_int8_t isr_noise; + u_int8_t isr_rssi; + u_int8_t isr_intval; /* beacon interval */ + u_int8_t isr_capinfo; /* capabilities */ + u_int8_t isr_erp; /* ERP element */ + u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; + u_int8_t isr_nrates; + u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE]; + u_int8_t isr_ssid_len; /* SSID length */ + u_int8_t isr_ie_len; /* IE length */ + u_int8_t isr_pad[5]; + /* variable length SSID followed by IE data */ +}; #define SIOCG80211STATS _IOWR('i', 236, struct ifreq) #endif /* __FreeBSD__ */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 5d4f0f5..708e7fa 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,120 +33,249 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_inet.h" - #include #include #include #include #include + #include -#include -#include -#include -#include -#include -#include - -#include #include -#include #include -#include #include -#include #include #include -#ifdef INET -#include -#include -#endif +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 u_int8_t node_getrssi(const struct ieee80211_node *); + +static void ieee80211_setup_node(struct ieee80211_node_table *, + struct ieee80211_node *, const u_int8_t *); +static void _ieee80211_free_node(struct ieee80211_node *); +static void ieee80211_free_allnodes(struct ieee80211_node_table *); -static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); -static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *); -static void ieee80211_node_copy(struct ieee80211com *, - struct ieee80211_node *, const struct ieee80211_node *); -static u_int8_t ieee80211_node_getrssi(struct ieee80211com *, - struct ieee80211_node *); +static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *); +static void ieee80211_timeout_stations(struct ieee80211_node_table *); -static void ieee80211_setup_node(struct ieee80211com *ic, - struct ieee80211_node *ni, u_int8_t *macaddr); -static void _ieee80211_free_node(struct ieee80211com *, - struct ieee80211_node *); +static void ieee80211_set_tim(struct ieee80211com *, + struct ieee80211_node *, int set); + +static void ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, const char *name, int inact, + void (*timeout)(struct ieee80211_node_table *)); +static struct ieee80211_node_table *ieee80211_node_table_alloc( + struct ieee80211com *ic, const char *name, int inact, + void (*timeout)(struct ieee80211_node_table *)); +static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt); MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); void -ieee80211_node_attach(struct ifnet *ifp) +ieee80211_node_attach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - /* XXX need unit */ - IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname); - TAILQ_INIT(&ic->ic_node); - ic->ic_node_alloc = ieee80211_node_alloc; - ic->ic_node_free = ieee80211_node_free; - ic->ic_node_copy = ieee80211_node_copy; - ic->ic_node_getrssi = ieee80211_node_getrssi; - ic->ic_scangen = 1; + ic->ic_sta = NULL; /* defer to when we need it */ + ieee80211_node_table_init(ic, &ic->ic_scan, "scan", + IEEE80211_INACT_SCAN, ieee80211_timeout_scan_candidates); + + ic->ic_node_alloc = node_alloc; + ic->ic_node_free = node_free; + ic->ic_node_cleanup = node_cleanup; + ic->ic_node_getrssi = node_getrssi; + + /* 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; + + /* XXX defer */ + if (ic->ic_max_aid == 0) + ic->ic_max_aid = IEEE80211_AID_DEF; + else if (ic->ic_max_aid > IEEE80211_AID_MAX) + ic->ic_max_aid = IEEE80211_AID_MAX; + MALLOC(ic->ic_aid_bitmap, u_int32_t *, + howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t), + M_DEVBUF, 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; + } + + /* XXX defer until using hostap/ibss mode */ + ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(u_int8_t); + MALLOC(ic->ic_tim_bitmap, u_int8_t *, ic->ic_tim_len, + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ic->ic_tim_bitmap == NULL) { + /* XXX no way to recover */ + printf("%s: no memory for TIM bitmap!\n", __func__); + } + ic->ic_set_tim = ieee80211_set_tim; /* NB: driver should override */ } void -ieee80211_node_lateattach(struct ifnet *ifp) +ieee80211_node_lateattach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; struct ieee80211_node *ni; + struct ieee80211_rsnparms *rsn; - ni = (*ic->ic_node_alloc)(ic); + ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr); KASSERT(ni != NULL, ("unable to setup inital BSS node")); - ni->ni_chan = IEEE80211_CHAN_ANYC; - ic->ic_bss = ni; - ic->ic_txpower = IEEE80211_TXPOWER_MAX; + /* + * Setup "global settings" in the bss node so that + * each new station automatically inherits them. + */ + rsn = &ni->ni_rsn; + /* WEP, TKIP, and AES-CCM are always supported */ + rsn->rsn_ucastcipherset |= 1<rsn_ucastcipherset |= 1<rsn_ucastcipherset |= 1<ic_caps & IEEE80211_C_AES) + rsn->rsn_ucastcipherset |= 1<ic_caps & IEEE80211_C_CKIP) + rsn->rsn_ucastcipherset |= 1<rsn_ucastcipher = IEEE80211_CIPHER_WEP; + rsn->rsn_ucastkeylen = 104 / NBBY; + /* + * WPA says the multicast cipher is the lowest unicast + * cipher supported. But we skip WEP which would + * otherwise be used based on this criteria. + */ + rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP; + rsn->rsn_mcastkeylen = 128 / NBBY; + + /* + * We support both WPA-PSK and 802.1x; the one used + * is determined by the authentication mode and the + * setting of the PSK state. + */ + rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK; + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + ic->ic_bss = ieee80211_ref_node(ni); /* hold reference */ + ic->ic_auth = ieee80211_authenticator_get(ni->ni_authmode); +} + +void +ieee80211_node_detach(struct ieee80211com *ic) +{ + + if (ic->ic_bss != NULL) { + ieee80211_free_node(ic->ic_bss); + ic->ic_bss = NULL; + } + ieee80211_node_table_cleanup(&ic->ic_scan); + if (ic->ic_sta != NULL) { + ieee80211_node_table_free(ic->ic_sta); + ic->ic_sta = NULL; + } + if (ic->ic_aid_bitmap != NULL) { + FREE(ic->ic_aid_bitmap, M_DEVBUF); + ic->ic_aid_bitmap = NULL; + } + if (ic->ic_tim_bitmap != NULL) { + FREE(ic->ic_tim_bitmap, M_DEVBUF); + ic->ic_tim_bitmap = NULL; + } } +/* + * Port authorize/unauthorize interfaces for use by an authenticator. + */ + void -ieee80211_node_detach(struct ifnet *ifp) +ieee80211_node_authorize(struct ieee80211com *ic, struct ieee80211_node *ni) { - struct ieee80211com *ic = (void *)ifp; + ni->ni_flags |= IEEE80211_NODE_AUTH; +} - if (ic->ic_bss != NULL) - (*ic->ic_node_free)(ic, ic->ic_bss); - ieee80211_free_allnodes(ic); - IEEE80211_NODE_LOCK_DESTROY(ic); +void +ieee80211_node_unauthorize(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ni->ni_flags &= ~IEEE80211_NODE_AUTH; +} + +/* + * Set/change the channel. The rate set is also updated as + * to insure a consistent view by drivers. + */ +static __inline void +ieee80211_set_chan(struct ieee80211com *ic, + struct ieee80211_node *ni, struct ieee80211_channel *chan) +{ + ni->ni_chan = chan; + ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; } /* * AP scanning support. */ +#ifdef IEEE80211_DEBUG +static void +dump_chanlist(const u_char chans[]) +{ + const char *sep; + int i; + + sep = " "; + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(chans, i)) { + printf("%s%u", sep, i); + sep = ", "; + } +} +#endif /* IEEE80211_DEBUG */ + /* - * Initialize the active channel set based on the set + * Initialize the channel set to scan based on the * of available channels and the current PHY mode. */ static void -ieee80211_reset_scan(struct ifnet *ifp) +ieee80211_reset_scan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; - memcpy(ic->ic_chan_scan, ic->ic_chan_active, - sizeof(ic->ic_chan_active)); + /* XXX ic_des_chan should be handled with ic_chan_active */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan)); + setbit(ic->ic_chan_scan, + ieee80211_chan2ieee(ic, ic->ic_des_chan)); + } else + memcpy(ic->ic_chan_scan, ic->ic_chan_active, + sizeof(ic->ic_chan_active)); /* NB: hack, setup so next_scan starts with the first channel */ if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) - ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; + ieee80211_set_chan(ic, ic->ic_bss, + &ic->ic_channels[IEEE80211_CHAN_MAX]); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + printf("%s: scan set:", __func__); + dump_chanlist(ic->ic_chan_scan); + printf(" start chan %u\n", + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); + } +#endif /* IEEE80211_DEBUG */ } /* * Begin an active scan. */ void -ieee80211_begin_scan(struct ifnet *ifp) +ieee80211_begin_scan(struct ieee80211com *ic, int reset) { - struct ieee80211com *ic = (void *)ifp; + ic->ic_scan.nt_scangen++; /* * In all but hostap mode scanning starts off in * an active mode before switching to passive. @@ -156,95 +285,174 @@ ieee80211_begin_scan(struct ifnet *ifp) ic->ic_stats.is_scan_active++; } else ic->ic_stats.is_scan_passive++; - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "begin %s scan\n", - (ic->ic_flags & IEEE80211_F_ASCAN) ? - "active" : "passive"); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "begin %s scan, scangen %u\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive", + ic->ic_scan.nt_scangen); /* - * Clear scan state and flush any previously seen - * AP's. Note that the latter assumes we don't act - * as both an AP and a station, otherwise we'll - * potentially flush state of stations associated - * with us. + * Clear scan state and flush any previously seen AP's. */ - ieee80211_reset_scan(ifp); - ieee80211_free_allnodes(ic); + ieee80211_reset_scan(ic); + if (reset) + ieee80211_free_allnodes(&ic->ic_scan); + + ic->ic_flags |= IEEE80211_F_SCAN; /* Scan the next channel. */ - ieee80211_next_scan(ifp); + ieee80211_next_scan(ic); } /* * Switch to the next channel marked for scanning. */ -void -ieee80211_next_scan(struct ifnet *ifp) +int +ieee80211_next_scan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; struct ieee80211_channel *chan; + /* + * Insure any previous mgt frame timeouts don't fire. + * This assumes the driver does the right thing in + * flushing anything queued in the driver and below. + */ + ic->ic_mgt_timer = 0; + chan = ic->ic_bss->ni_chan; - for (;;) { + do { if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) chan = &ic->ic_channels[0]; if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { + clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: chan %d->%d\n", __func__, + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), + ieee80211_chan2ieee(ic, chan)); + ieee80211_set_chan(ic, ic->ic_bss, chan); +#ifdef notyet + /* XXX driver state change */ /* - * Honor channels marked passive-only - * during an active scan. + * Scan next channel. If doing an active scan + * and the channel is not marked passive-only + * then send a probe request. Otherwise just + * listen for beacons on the channel. */ - if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || - (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) - break; - } - if (chan == ic->ic_bss->ni_chan) { - ieee80211_end_scan(ifp); - return; + if ((ic->ic_flags & IEEE80211_F_ASCAN) && + (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); + } +#else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); +#endif + return 1; } - } - clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); - IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", - ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), - ieee80211_chan2ieee(ic, chan))); - ic->ic_bss->ni_chan = chan; - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } while (chan != ic->ic_bss->ni_chan); + ieee80211_end_scan(ic); + return 0; } void ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) { struct ieee80211_node *ni; - struct ifnet *ifp = &ic->ic_if; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: creating ibss\n", __func__); + + /* + * Create the station/neighbor table. Note that for adhoc + * mode we make the initial inactivity timer longer since + * we create nodes only through discovery and they typically + * are long-lived associations. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + ic->ic_sta = ieee80211_node_table_alloc(ic, + "station", ic->ic_inact_init, + ieee80211_timeout_stations); + else + ic->ic_sta = ieee80211_node_table_alloc(ic, + "neighbor", ic->ic_inact_run, + ieee80211_timeout_stations); + if (ic->ic_sta == NULL) { + /* + * Should remain in SCAN state and retry. + */ + /* XXX stat+msg */ + return; + } ni = ic->ic_bss; - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "creating ibss\n"); - ic->ic_flags |= IEEE80211_F_SIBSS; - ni->ni_chan = chan; - ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); - if (ic->ic_opmode == IEEE80211_M_IBSS) - ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ ni->ni_esslen = ic->ic_des_esslen; memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); ni->ni_rssi = 0; ni->ni_rstamp = 0; - memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); + ni->ni_tstamp.tsf = 0; ni->ni_intval = ic->ic_lintval; - ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; - if (ic->ic_flags & IEEE80211_F_WEPON) + ni->ni_capinfo = 0; + ni->ni_erp = 0; + if (ic->ic_flags & IEEE80211_F_PRIVACY) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ ni->ni_fhindex = 1; } + if (ic->ic_opmode == IEEE80211_M_IBSS) { + ic->ic_flags |= IEEE80211_F_SIBSS; + ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */ + ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ + } + /* + * Fix the channel and related attributes. + */ + ieee80211_set_chan(ic, ni, chan); + ic->ic_curmode = ieee80211_chan2mode(ic, chan); + /* + * Do mode-specific rate setup. + */ + if (ic->ic_curmode == IEEE80211_MODE_11G) { + /* + * Use a mixed 11b/11g rate set. + */ + ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11G); + } else if (ic->ic_curmode == IEEE80211_MODE_11B) { + /* + * Force pure 11b rate set. + */ + ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11B); + } + /* + * 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_new_state(ic, IEEE80211_S_RUN, -1); } +void +ieee80211_reset_bss(struct ieee80211com *ic) +{ + struct ieee80211_node *ni, *obss; + + ieee80211_node_table_reset(&ic->ic_scan); + ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr); + KASSERT(ni != NULL, ("unable to setup inital BSS node")); + obss = ic->ic_bss; + ic->ic_bss = ieee80211_ref_node(ni); + if (obss != NULL) + ieee80211_free_node(obss); + if (ic->ic_sta != NULL) { + ieee80211_node_table_free(ic->ic_sta); + ic->ic_sta = NULL; + } +} + static int -ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) +ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) { - struct ieee80211com *ic = (void *)ifp; u_int8_t rate; int fail; @@ -261,7 +469,7 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } - if (ic->ic_flags & IEEE80211_F_WEPON) { + if (ic->ic_flags & IEEE80211_F_PRIVACY) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { @@ -280,7 +488,7 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; #ifdef IEEE80211_DEBUG - if (ifp->if_flags & IFF_DEBUG) { + if (ieee80211_msg_scan(ic)) { printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr)); printf(" %s%c", ether_sprintf(ni->ni_bssid), @@ -306,49 +514,137 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni) return fail; } +static __inline u_int8_t +maxrate(const struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs = &ni->ni_rates; + /* NB: assumes rate set is sorted (happens on frame receive) */ + return rs->rs_rates[rs->rs_nrates-1] & IEEE80211_RATE_VAL; +} + +/* + * Compare the capabilities of two nodes and decide which is + * more desirable (return >0 if a is considered better). Note + * that we assume compatibility/usability has already been checked + * so we don't need to (e.g. validate whether privacy is supported). + * Used to select the best scan candidate for association in a BSS. + */ +static int +ieee80211_node_compare(struct ieee80211com *ic, + const struct ieee80211_node *a, + const struct ieee80211_node *b) +{ + u_int8_t maxa, maxb; + u_int8_t rssia, rssib; + + /* privacy support preferred */ + if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) && + (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + return 1; + if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 && + (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + maxa = maxrate(a); + maxb = maxrate(b); + rssia = ic->ic_node_getrssi(a); + rssib = ic->ic_node_getrssi(b); + if (maxa != maxb && abs(rssib - rssia) < 5) + return maxa - maxb; + + /* XXX use freq for channel preference */ + /* for now just prefer 5Ghz band to all other bands */ + if (IEEE80211_IS_CHAN_5GHZ(a->ni_chan) && + !IEEE80211_IS_CHAN_5GHZ(b->ni_chan)) + return 1; + if (!IEEE80211_IS_CHAN_5GHZ(a->ni_chan) && + IEEE80211_IS_CHAN_5GHZ(b->ni_chan)) + return -1; + + /* all things being equal, use signal level */ + return rssia - rssib; +} + /* * Complete a scan of potential channels. */ void -ieee80211_end_scan(struct ifnet *ifp) +ieee80211_end_scan(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; struct ieee80211_node *ni, *nextbs, *selbs; - int i, fail; + struct ieee80211_node_table *nt; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "end %s scan\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); - ic->ic_flags &= ~IEEE80211_F_ASCAN; - ni = TAILQ_FIRST(&ic->ic_node); + ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN); + nt = &ic->ic_scan; + ni = TAILQ_FIRST(&nt->nt_node); + + ieee80211_notify_scan_done(ic); if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - /* XXX off stack? */ - u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; + u_int8_t maxrssi[IEEE80211_CHAN_MAX]; /* XXX off stack? */ + int i, bestchan; + u_int8_t rssi; + /* * The passive scan to look for existing AP's completed, * select a channel to camp on. Identify the channels * that already have one or more AP's and try to locate - * an unnoccupied one. If that fails, pick a random - * channel from the active set. + * an unnoccupied one. If that fails, pick a channel that + * looks to be quietest. */ + memset(maxrssi, 0, sizeof(maxrssi)); for (; ni != NULL; ni = nextbs) { ieee80211_ref_node(ni); nextbs = TAILQ_NEXT(ni, ni_list); - setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); - ieee80211_free_node(ic, ni); + rssi = ic->ic_node_getrssi(ni); + i = ieee80211_chan2ieee(ic, ni->ni_chan); + if (rssi > maxrssi[i]) + maxrssi[i] = rssi; + ieee80211_unref_node(&ni); } + /* XXX select channel more intelligently */ + bestchan = -1; for (i = 0; i < IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) - break; - if (i == IEEE80211_CHAN_MAX) { - fail = arc4random() & 3; /* random 0-3 */ - for (i = 0; i < IEEE80211_CHAN_MAX; i++) - if (isset(ic->ic_chan_active, i) && fail-- == 0) + if (isset(ic->ic_chan_active, i)) { + /* + * If the channel is unoccupied the max rssi + * should be zero; just take it. Otherwise + * track the channel with the lowest rssi and + * use that when all channels appear occupied. + */ + if (maxrssi[i] == 0) { + bestchan = i; break; + } + if (maxrssi[i] < maxrssi[bestchan]) + bestchan = i; + } + if (bestchan != -1) { + ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]); + return; } - ieee80211_create_ibss(ic, &ic->ic_channels[i]); - return; + /* no suitable channel, should not happen */ } + + /* + * When manually sequencing the state machine; scan just once + * regardless of whether we have a candidate or not. The + * controlling application is expected to setup state and + * initiate an association. + */ + if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL) + return; + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ if (ni == NULL) { - IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); notfound: if (ic->ic_opmode == IEEE80211_M_IBSS && (ic->ic_flags & IEEE80211_F_IBSSON) && @@ -359,13 +655,14 @@ ieee80211_end_scan(struct ifnet *ifp) /* * Reset the list of channels to scan and start again. */ - ieee80211_reset_scan(ifp); - ieee80211_next_scan(ifp); + ieee80211_reset_scan(ic); + ic->ic_flags |= IEEE80211_F_SCAN; + ieee80211_next_scan(ic); return; } selbs = NULL; - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n"); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n", + "macaddr bssid chan rssi rate flag wep essid"); for (; ni != NULL; ni = nextbs) { ieee80211_ref_node(ni); nextbs = TAILQ_NEXT(ni, ni_list); @@ -375,14 +672,18 @@ ieee80211_end_scan(struct ifnet *ifp) * during my scan. So delete the entry for the AP * and retry to associate if there is another beacon. */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + "%s: skip scan candidate %s, fails %u\n", + __func__, ether_sprintf(ni->ni_macaddr), + ni->ni_fails); if (ni->ni_fails++ > 2) - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); continue; } - if (ieee80211_match_bss(ifp, ni) == 0) { + if (ieee80211_match_bss(ic, ni) == 0) { if (selbs == NULL) selbs = ni; - else if (ni->ni_rssi > selbs->ni_rssi) { + else if (ieee80211_node_compare(ic, ni, selbs) > 0) { ieee80211_unref_node(&selbs); selbs = ni; } else @@ -393,148 +694,401 @@ ieee80211_end_scan(struct ifnet *ifp) } if (selbs == NULL) goto notfound; - (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); + if (!ieee80211_sta_join(ic, selbs)) { + ieee80211_unref_node(&selbs); + goto notfound; + } +} + +/* + * Handle 802.11 ad hoc network merge. The + * convention, set by the Wireless Ethernet Compatibility Alliance + * (WECA), is that an 802.11 station will change its BSSID to match + * the "oldest" 802.11 ad hoc network, on the same channel, that + * has the station's desired SSID. The "oldest" 802.11 network + * sends beacons with the greatest TSF timestamp. + * + * The caller is assumed to validate TSF's before attempting a merge. + * + * Return !0 if the BSSID changed, 0 otherwise. + */ +int +ieee80211_ibss_merge(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + if (IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) { + /* unchanged, nothing to do */ + return 0; + } + if (ieee80211_match_bss(ic, ni) != 0) { /* capabilities mismatch */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: merge failed, capabilities mismatch\n", __func__); + ic->ic_stats.is_ibss_capmismatch++; + return 0; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%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", + ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "" + ); + return ieee80211_sta_join(ic, ni); +} + +/* + * Join the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +int +ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs) +{ + struct ieee80211_node *obss; + if (ic->ic_opmode == IEEE80211_M_IBSS) { - ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | + /* + * Check rate set before committing to this node. + */ + ieee80211_fix_rate(ic, selbs, IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ic->ic_bss->ni_rates.rs_nrates == 0) { + if (selbs->ni_rates.rs_nrates == 0) { selbs->ni_fails++; - ieee80211_unref_node(&selbs); - goto notfound; + ic->ic_stats.is_ibss_norate++; + return 0; } - ieee80211_unref_node(&selbs); /* - * Discard scan set; the nodes have a refcnt of zero - * and have not asked the driver to setup private - * node state. Let them be repopulated on demand either - * through transmission (ieee80211_find_txnode) or receipt - * of a probe response (to be added). + * Create the neighbor table. */ - ieee80211_free_allnodes(ic); + ic->ic_sta = ieee80211_node_table_alloc(ic, + "neighbor", ic->ic_inact_run, + ieee80211_timeout_stations); + if (ic->ic_sta == NULL) { + /* + * Should remain in SCAN state and retry. + */ + /* XXX stat+msg */ + return 0; + } + } + + /* + * Committed to selbs, setup state. + */ + obss = ic->ic_bss; + ic->ic_bss = selbs; + if (obss != NULL) + ieee80211_free_node(obss); + /* + * Set the erp state (mostly the slot time) to deal with + * the auto-select case; this should be redundant if the + * mode is locked. + */ + ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan); + ieee80211_reset_erp(ic); + ieee80211_wme_initparams(ic); + if (ic->ic_opmode == IEEE80211_M_IBSS) ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - } else { - ieee80211_unref_node(&selbs); + else ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); - } + return 1; +} + +/* + * Leave the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +void +ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ic->ic_node_cleanup(ni); + ieee80211_notify_node_leave(ic, ni); } static struct ieee80211_node * -ieee80211_node_alloc(struct ieee80211com *ic) +node_alloc(struct ieee80211_node_table *nt) { struct ieee80211_node *ni; + MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ni; } +/* + * Reclaim any resources in a node and reset any critical + * state. Typically nodes are free'd immediately after, + * but in some cases the storage may be reused so we need + * to insure consistent state (should probably fix that). + */ static void -ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) +node_cleanup(struct ieee80211_node *ni) { - FREE(ni, M_80211_NODE); +#define N(a) (sizeof(a)/sizeof(a[0])) + struct ieee80211com *ic = ni->ni_ic; + int i, qlen; + + /* NB: preserve ni_table */ + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { + ic->ic_ps_sta--; + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] power save mode off, %u sta's in ps mode\n", + ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta); + } + + /* + * Drain power save queue and, if needed, clear TIM. + */ + IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen); + if (qlen != 0 && ic->ic_set_tim != NULL) + ic->ic_set_tim(ic, ni, 0); + + ni->ni_associd = 0; + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + /* + * Preserve SSID, WPA, and WME ie's so the bss node is + * reusable during a re-auth/re-assoc state transition. + * If we remove these data they will not be recreated + * because they come from a probe-response or beacon frame + * which cannot be expected prior to the association-response. + * This should not be an issue when operating in other modes + * as stations leaving always go through a full state transition + * which will rebuild this state. + * + * XXX does this leave us open to inheriting old state? + */ + for (i = 0; i < N(ni->ni_rxfrag); i++) + if (ni->ni_rxfrag[i] != NULL) { + m_freem(ni->ni_rxfrag[i]); + ni->ni_rxfrag[i] = NULL; + } + ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); +#undef N } static void -ieee80211_node_copy(struct ieee80211com *ic, - struct ieee80211_node *dst, const struct ieee80211_node *src) +node_free(struct ieee80211_node *ni) { - *dst = *src; + struct ieee80211com *ic = ni->ni_ic; + + ic->ic_node_cleanup(ni); + if (ni->ni_wpa_ie != NULL) + FREE(ni->ni_wpa_ie, M_DEVBUF); + if (ni->ni_wme_ie != NULL) + FREE(ni->ni_wme_ie, M_DEVBUF); + IEEE80211_NODE_SAVEQ_DESTROY(ni); + FREE(ni, M_80211_NODE); } static u_int8_t -ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) +node_getrssi(const struct ieee80211_node *ni) { return ni->ni_rssi; } static void -ieee80211_setup_node(struct ieee80211com *ic, - struct ieee80211_node *ni, u_int8_t *macaddr) +ieee80211_setup_node(struct ieee80211_node_table *nt, + struct ieee80211_node *ni, const u_int8_t *macaddr) { + struct ieee80211com *ic = nt->nt_ic; int hash; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s %s in %s table\n", __func__, + ether_sprintf(macaddr), nt->nt_name); + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); hash = IEEE80211_NODE_HASH(macaddr); - ni->ni_refcnt = 1; /* mark referenced */ - IEEE80211_NODE_LOCK(ic); - TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); - LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); - /* - * Note we don't enable the inactive timer when acting - * as a station. Nodes created in this mode represent - * AP's identified while scanning. If we time them out - * then several things happen: we can't return the data - * to users to show the list of AP's we encountered, and - * more importantly, we'll incorrectly deauthenticate - * ourself because the inactivity timer will kick us off. - */ - if (ic->ic_opmode != IEEE80211_M_STA) - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - IEEE80211_NODE_UNLOCK(ic); + ieee80211_node_initref(ni); /* mark referenced */ + ni->ni_chan = IEEE80211_CHAN_ANYC; + ni->ni_authmode = IEEE80211_AUTH_OPEN; + ni->ni_txpower = ic->ic_txpowlimit; /* max power */ + ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); + ni->ni_inact = ni->ni_inact_reload = nt->nt_inact_init; + IEEE80211_NODE_SAVEQ_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_ic = ic; + IEEE80211_NODE_UNLOCK(nt); } struct ieee80211_node * -ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) +ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr) { - struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + + ni = ic->ic_node_alloc(nt); if (ni != NULL) - ieee80211_setup_node(ic, ni, macaddr); + ieee80211_setup_node(nt, ni, macaddr); else ic->ic_stats.is_rx_nodealloc++; return ni; } struct ieee80211_node * -ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) +ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr) { - struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + + ni = ic->ic_node_alloc(nt); if (ni != NULL) { - ieee80211_setup_node(ic, ni, macaddr); + ieee80211_setup_node(nt, ni, macaddr); /* * Inherit from ic_bss. */ + ni->ni_authmode = ic->ic_bss->ni_authmode; + ni->ni_txpower = ic->ic_bss->ni_txpower; + ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); - ni->ni_chan = ic->ic_bss->ni_chan; + ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan); + ni->ni_rsn = ic->ic_bss->ni_rsn; } else ic->ic_stats.is_rx_nodealloc++; return ni; } static struct ieee80211_node * -_ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) +#ifdef IEEE80211_DEBUG_REFCNT +_ieee80211_find_node_debug(struct ieee80211_node_table *nt, + const u_int8_t *macaddr, const char *func, int line) +#else +_ieee80211_find_node(struct ieee80211_node_table *nt, + const u_int8_t *macaddr) +#endif { struct ieee80211_node *ni; int hash; - IEEE80211_NODE_LOCK_ASSERT(ic); + IEEE80211_NODE_LOCK_ASSERT(nt); hash = IEEE80211_NODE_HASH(macaddr); - LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { - atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ + ieee80211_ref_node(ni); /* mark referenced */ +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s (%s:%u) %s refcnt %d\n", __func__, func, line, + ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); +#endif return ni; } } return NULL; } +#ifdef IEEE80211_DEBUG_REFCNT +#define _ieee80211_find_node(nt, mac) \ + _ieee80211_find_node_debug(nt, mac, func, line) +#endif struct ieee80211_node * -ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_debug(struct ieee80211_node_table *nt, + const u_int8_t *macaddr, const char *func, int line) +#else +ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr) +#endif { struct ieee80211_node *ni; - IEEE80211_NODE_LOCK(ic); - ni = _ieee80211_find_node(ic, macaddr); - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_NODE_LOCK(nt); + ni = _ieee80211_find_node(nt, macaddr); + IEEE80211_NODE_UNLOCK(nt); + return ni; +} + +/* + * Fake up a node; this handles node discovery in adhoc mode. + * Note that for the driver's benefit we we treat this like + * an association so the driver has an opportunity to setup + * it's private state. + */ +struct ieee80211_node * +ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt, + const u_int8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + + ni = ieee80211_dup_bss(nt, macaddr); + if (ni != NULL) { + /* XXX no rate negotiation; just dup */ + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc) + ic->ic_newassoc(ic, ni, 1); + /* XXX not right for 802.1x/WPA */ + ieee80211_node_authorize(ic, ni); + ieee80211_ref_node(ni); /* hold reference */ + } return ni; } /* + * Locate the node for sender, track state, and then pass the + * (referenced) node up to the 802.11 layer for its use. We + * are required to pass some node so we fall back to ic_bss + * when this frame is from an unknown sender. The 802.11 layer + * knows this means the sender wasn't in the node table and + * acts accordingly. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_rxnode_debug(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, const char *func, int line) +#else +ieee80211_find_rxnode(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh) +#endif +{ +#define IS_CTL(wh) \ + ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) +#define IS_PSPOLL(wh) \ + ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + + /* XXX may want scanned nodes in the neighbor table for adhoc */ + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_MONITOR || + (ic->ic_flags & IEEE80211_F_SCAN)) + nt = &ic->ic_scan; + else + nt = ic->ic_sta; + /* XXX check ic_bss first in station mode */ + /* XXX 4-address frames? */ + IEEE80211_NODE_LOCK(nt); + if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) + ni = _ieee80211_find_node(nt, wh->i_addr1); + else + ni = _ieee80211_find_node(nt, wh->i_addr2); + IEEE80211_NODE_UNLOCK(nt); + + return (ni != NULL ? ni : ieee80211_ref_node(ic->ic_bss)); +#undef IS_PSPOLL +#undef IS_CTL +} + +/* * Return a reference to the appropriate node for sending * a data frame. This handles node discovery in adhoc networks. */ struct ieee80211_node * -ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr, + const char *func, int line) +#else +ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr) +#endif { + struct ieee80211_node_table *nt = ic->ic_sta; struct ieee80211_node *ni; /* @@ -542,31 +1096,23 @@ ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) * unless we are operating in station mode or this is a * multicast/broadcast frame. */ - if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) - return ic->ic_bss; + if (nt == NULL || IEEE80211_IS_MULTICAST(macaddr)) + return ieee80211_ref_node(ic->ic_bss); /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ - IEEE80211_NODE_LOCK(ic); - ni = _ieee80211_find_node(ic, macaddr); - IEEE80211_NODE_UNLOCK(ic); - if (ni == NULL && - (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO)) { - /* - * Fake up a node; this handles node discovery in - * adhoc mode. Note that for the driver's benefit - * we we treat this like an association so the driver - * has an opportunity to setup it's private state. - * - * XXX need better way to handle this; issue probe - * request so we can deduce rate set, etc. - */ - ni = ieee80211_dup_bss(ic, macaddr); - if (ni != NULL) { - /* XXX no rate negotiation; just dup */ - ni->ni_rates = ic->ic_bss->ni_rates; - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, 1); + IEEE80211_NODE_LOCK(nt); + ni = _ieee80211_find_node(nt, macaddr); + IEEE80211_NODE_UNLOCK(nt); + + if (ni == NULL) { + if (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO) + ni = ieee80211_fakeup_adhoc_node(nt, macaddr); + 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; @@ -576,117 +1122,775 @@ ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) * Like find but search based on the channel too. */ struct ieee80211_node * -ieee80211_lookup_node(struct ieee80211com *ic, - u_int8_t *macaddr, struct ieee80211_channel *chan) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_with_channel_debug(struct ieee80211_node_table *nt, + const u_int8_t *macaddr, struct ieee80211_channel *chan, + const char *func, int line) +#else +ieee80211_find_node_with_channel(struct ieee80211_node_table *nt, + const u_int8_t *macaddr, struct ieee80211_channel *chan) +#endif { struct ieee80211_node *ni; int hash; hash = IEEE80211_NODE_HASH(macaddr); - IEEE80211_NODE_LOCK(ic); - LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + IEEE80211_NODE_LOCK(nt); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) { - atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ + ieee80211_ref_node(ni); /* mark referenced */ + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, +#ifdef IEEE80211_DEBUG_REFCNT + "%s (%s:%u) %s refcnt %d\n", __func__, func, line, +#else + "%s %s refcnt %d\n", __func__, +#endif + ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); break; } } - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_NODE_UNLOCK(nt); 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 u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid, + const char *func, int line) +#else +ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt, + const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid) +#endif +{ + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + int hash; + + hash = IEEE80211_NODE_HASH(macaddr); + IEEE80211_NODE_LOCK(nt); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && + ni->ni_esslen == ic->ic_des_esslen && + memcmp(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen) == 0) { + ieee80211_ref_node(ni); /* mark referenced */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, +#ifdef IEEE80211_DEBUG_REFCNT + "%s (%s:%u) %s refcnt %d\n", __func__, func, line, +#else + "%s %s refcnt %d\n", __func__, +#endif + ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); + break; + } + } + IEEE80211_NODE_UNLOCK(nt); + return ni; +} + + static void -_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +_ieee80211_free_node(struct ieee80211_node *ni) { - KASSERT(ni != ic->ic_bss, ("freeing bss node")); + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node_table *nt = ni->ni_table; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s %s in %s table\n", __func__, ether_sprintf(ni->ni_macaddr), + nt != NULL ? nt->nt_name : ""); - TAILQ_REMOVE(&ic->ic_node, ni, ni_list); - LIST_REMOVE(ni, ni_hash); - if (TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = 0; - (*ic->ic_node_free)(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + if (nt != NULL) { + TAILQ_REMOVE(&nt->nt_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + } + ic->ic_node_free(ni); } void -ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line) +#else +ieee80211_free_node(struct ieee80211_node *ni) +#endif { - KASSERT(ni != ic->ic_bss, ("freeing ic_bss")); + struct ieee80211_node_table *nt = ni->ni_table; - /* XXX need equivalent of atomic_dec_and_test */ - atomic_subtract_int(&ni->ni_refcnt, 1); - if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) { - IEEE80211_NODE_LOCK(ic); - _ieee80211_free_node(ic, ni); - IEEE80211_NODE_UNLOCK(ic); +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s (%s:%u) %s refcnt %d\n", __func__, func, line, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1); +#endif + if (ieee80211_node_dectestref(ni)) { + /* + * Beware; if the node is marked gone then it's already + * been removed from the table and we cannot assume the + * table still exists. Regardless, there's no need to lock + * the table. + */ + if (ni->ni_table != NULL) { + IEEE80211_NODE_LOCK(nt); + _ieee80211_free_node(ni); + IEEE80211_NODE_UNLOCK(nt); + } else + _ieee80211_free_node(ni); } } -void -ieee80211_free_allnodes(struct ieee80211com *ic) +/* + * Reclaim a node. If this is the last reference count then + * do the normal free work. Otherwise remove it from the node + * table and mark it gone by clearing the back-reference. + */ +static void +node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) { + + if (!ieee80211_node_dectestref(ni)) { + /* + * Other references are present, just remove the + * node from the table so it cannot be found. When + * the references are dropped storage will be + * reclaimed. This normally only happens for ic_bss. + */ + TAILQ_REMOVE(&nt->nt_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + ni->ni_table = NULL; /* clear reference */ + } else + _ieee80211_free_node(ni); +} + +static void +ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt) +{ + struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; - IEEE80211_NODE_LOCK(ic); - while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) - _ieee80211_free_node(ic, ni); - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s: free all nodes in %s table\n", __func__, nt->nt_name); + + while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) { + if (ni->ni_associd != 0) { + if (ic->ic_auth->ia_node_leave != NULL) + ic->ic_auth->ia_node_leave(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + } + node_reclaim(nt, ni); + } + ieee80211_reset_erp(ic); +} + +static void +ieee80211_free_allnodes(struct ieee80211_node_table *nt) +{ + + IEEE80211_NODE_LOCK(nt); + ieee80211_free_allnodes_locked(nt); + IEEE80211_NODE_UNLOCK(nt); +} + +/* + * Timeout entries in the scan cache. + */ +static void +ieee80211_timeout_scan_candidates(struct ieee80211_node_table *nt) +{ + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni, *tni; + + IEEE80211_NODE_LOCK(nt); + ni = ic->ic_bss; + /* XXX belongs elsewhere */ + if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, tni) { + if (ni->ni_inact && --ni->ni_inact == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "[%s] scan candidate purged from cache " + "(refcnt %u)\n", ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)-1); + node_reclaim(nt, ni); + } + } + IEEE80211_NODE_UNLOCK(nt); + + nt->nt_inact_timer = IEEE80211_INACT_WAIT; } /* - * Timeout inactive nodes. Note that we cannot hold the node - * lock while sending a frame as this would lead to a LOR. - * Instead we use a generation number to mark nodes that we've - * scanned and drop the lock and restart a scan if we have to - * time out a node. Since we are single-threaded by virtue of + * Timeout inactive stations and do related housekeeping. + * Note that we cannot hold the node lock while sending a + * frame as this would lead to a LOR. Instead we use a + * generation number to mark nodes that we've scanned and + * drop the lock and restart a scan if we have to time out + * a node. Since we are single-threaded by virtue of * controlling the inactivity timer we can be sure this will * process each node only once. */ -void -ieee80211_timeout_nodes(struct ieee80211com *ic) +static void +ieee80211_timeout_stations(struct ieee80211_node_table *nt) { + struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; - u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/ + u_int gen; + IEEE80211_SCAN_LOCK(nt); + gen = nt->nt_scangen++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s: sta scangen %u\n", __func__, gen); restart: - IEEE80211_NODE_LOCK(ic); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { if (ni->ni_scangen == gen) /* previously handled */ continue; ni->ni_scangen = gen; - if (++ni->ni_inact > IEEE80211_INACT_MAX) { - IEEE80211_DPRINTF(("station %s timed out " - "due to inactivity (%u secs)\n", - ether_sprintf(ni->ni_macaddr), - ni->ni_inact)); + /* + * Free fragment if not needed anymore + * (last fragment older than 1s). + * XXX doesn't belong here + */ + if (ni->ni_rxfrag[0] != NULL && + ticks > ni->ni_rxfragstamp + hz) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + ni->ni_inact--; + if (ni->ni_associd != 0) { /* - * Send a deauthenticate frame. + * Age frames on the power save queue. The + * aging interval is 4 times the listen + * interval specified by the station. This + * number is factored into the age calculations + * when the frame is placed on the queue. We + * store ages as time differences we can check + * and/or adjust only the head of the list. + */ + if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) { + struct mbuf *m; + int discard = 0; + + IEEE80211_NODE_SAVEQ_LOCK(ni); + while (IF_POLL(&ni->ni_savedq, m) != NULL && + M_AGE_GET(m) < IEEE80211_INACT_WAIT) { +IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/ + _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m); + m_freem(m); + discard++; + } + if (m != NULL) + M_AGE_SUB(m, IEEE80211_INACT_WAIT); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + + if (discard != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_POWER, + "[%s] discard %u frames for age\n", + ether_sprintf(ni->ni_macaddr), + discard); + IEEE80211_NODE_STAT_ADD(ni, + ps_discard, discard); + if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) + ic->ic_set_tim(ic, ni, 0); + } + } + /* + * Probe the station before time it out. We + * send a null data frame which may not be + * universally supported by drivers (need it + * for ps-poll support so it should be...). + */ + if (ni->ni_inact == ic->ic_inact_probe) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "[%s] probe station due to inactivity\n", + ether_sprintf(ni->ni_macaddr)); + IEEE80211_NODE_UNLOCK(nt); + ieee80211_send_nulldata(ic, ni); + /* XXX stat? */ + goto restart; + } + } + if (ni->ni_inact <= 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "[%s] station timed out due to inactivity " + "(refcnt %u)\n", ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); + /* + * Send a deauthenticate frame and drop the station. + * This is somewhat complicated due to reference counts + * and locking. At this point a station will typically + * have a reference count of 1. ieee80211_node_leave + * will do a "free" of the node which will drop the + * reference count. But in the meantime a reference + * wil be held by the deauth frame. The actual reclaim + * of the node will happen either after the tx is + * completed or by ieee80211_node_leave. * - * Drop the node lock before sending the - * deauthentication frame in case the driver takes - * a lock, as this will result in a LOR between the - * node lock and the driver lock. + * Separately we must drop the node lock before sending + * in case the driver takes a lock, as this will result + * in LOR between the node lock and the driver lock. */ - IEEE80211_NODE_UNLOCK(ic); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_EXPIRE); - ieee80211_free_node(ic, ni); + IEEE80211_NODE_UNLOCK(nt); + if (ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + } + ieee80211_node_leave(ic, ni); ic->ic_stats.is_node_timeout++; goto restart; } } - if (!TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_NODE_UNLOCK(nt); + + IEEE80211_SCAN_UNLOCK(nt); + + nt->nt_inact_timer = IEEE80211_INACT_WAIT; } void -ieee80211_iterate_nodes(struct ieee80211com *ic, 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); + gen = nt->nt_scangen++; + + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s: sta scangen %u\n", __func__, gen); +restart: + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + if (ni->ni_scangen != gen) { + ni->ni_scangen = gen; + (void) ieee80211_ref_node(ni); + IEEE80211_NODE_UNLOCK(nt); + (*f)(arg, ni); + ieee80211_free_node(ni); + goto restart; + } + } + IEEE80211_NODE_UNLOCK(nt); + + IEEE80211_SCAN_UNLOCK(nt); +} + +void +ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) +{ + printf("0x%p: mac %s refcnt %d\n", ni, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); + printf("\tscangen %u authmode %u flags 0x%x\n", + ni->ni_scangen, ni->ni_authmode, ni->ni_flags); + printf("\tassocid 0x%x txpower %u vlan %u\n", + ni->ni_associd, ni->ni_txpower, ni->ni_vlan); + printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n", + ni->ni_txseqs[0], + ni->ni_rxseqs[0] >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseqs[0] & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxfragstamp); + printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n", + ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo); + printf("\tbssid %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); +} + +void +ieee80211_dump_nodes(struct ieee80211_node_table *nt) +{ + ieee80211_iterate_nodes(nt, + (ieee80211_iter_func *) ieee80211_dump_node, nt); +} + +/* + * Handle a station joining an 11g network. + */ +static void +ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + /* + * Station isn't capable of short slot time. Bump + * the count of long slot time stations and disable + * use of short slot time. Note that the actual switch + * over to long slot time use may not occur until the + * next beacon transmission (per sec. 7.3.1.4 of 11g). + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + ic->ic_longslotsta++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] station needs long slot time, count %d\n", + ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta); + /* XXX vap's w/ conflicting needs won't work */ + ieee80211_set_shortslottime(ic, 0); + } + /* + * If the new station is not an ERP station + * then bump the counter and enable protection + * if configured. + */ + if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) { + ic->ic_nonerpsta++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] station is !ERP, %d non-ERP stations associated\n", + ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta); + /* + * If protection is configured, enable it. + */ + if (ic->ic_protmode != IEEE80211_PROT_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: enable use of protection\n", __func__); + ic->ic_flags |= IEEE80211_F_USEPROT; + } + /* + * If station does not support short preamble + * then we must enable use of Barker preamble. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] station needs long preamble\n", + ether_sprintf(ni->ni_macaddr)); + ic->ic_flags |= IEEE80211_F_USEBARKER; + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + } + } else + ni->ni_flags |= IEEE80211_NODE_ERP; +} + +void +ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp) +{ + int newassoc; + + if (ni->ni_associd == 0) { + u_int16_t aid; + + /* + * It would be good to search the bitmap + * more efficiently, but this will do for now. + */ + for (aid = 1; aid < ic->ic_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(aid, + ic->ic_aid_bitmap)) + break; + } + if (aid >= ic->ic_max_aid) { + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_REASON_ASSOC_TOOMANY); + ieee80211_node_leave(ic, ni); + return; + } + ni->ni_associd = aid | 0xc000; + IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); + ic->ic_sta_assoc++; + newassoc = 1; + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_node_join_11g(ic, ni); + } else + newassoc = 0; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, + "[%s] station %s associated at aid %d\n", + ether_sprintf(ni->ni_macaddr), newassoc ? "newly" : "already", + IEEE80211_NODE_AID(ni)); + + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc) + ic->ic_newassoc(ic, ni, newassoc); + ni->ni_inact_reload = ic->ic_inact_run; + IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); + /* tell the authenticator about new station */ + if (ic->ic_auth->ia_node_join != NULL) + ic->ic_auth->ia_node_join(ic, ni); + ieee80211_notify_node_join(ic, ni, newassoc); +} + +/* + * Handle a station leaving an 11g network. + */ +static void +ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + KASSERT(ic->ic_curmode == IEEE80211_MODE_11G, + ("not in 11g, curmode %x", ic->ic_curmode)); + + /* + * If a long slot station do the slot time bookkeeping. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + KASSERT(ic->ic_longslotsta > 0, + ("bogus long slot station count %d", ic->ic_longslotsta)); + ic->ic_longslotsta--; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] long slot time station leaves, count now %d\n", + ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta); + if (ic->ic_longslotsta == 0) { + /* + * Re-enable use of short slot time if supported + * and not operating in IBSS mode (per spec). + */ + if ((ic->ic_caps & IEEE80211_C_SHSLOT) && + ic->ic_opmode != IEEE80211_M_IBSS) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: re-enable use of short slot time\n", + __func__); + ieee80211_set_shortslottime(ic, 1); + } + } + } + /* + * If a non-ERP station do the protection-related bookkeeping. + */ + if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) { + KASSERT(ic->ic_nonerpsta > 0, + ("bogus non-ERP station count %d", ic->ic_nonerpsta)); + ic->ic_nonerpsta--; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] non-ERP station leaves, count now %d\n", + ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta); + if (ic->ic_nonerpsta == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: disable use of protection\n", __func__); + ic->ic_flags &= ~IEEE80211_F_USEPROT; + /* XXX verify mode? */ + if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "%s: re-enable use of short preamble\n", + __func__); + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } + } + } +} + +/* + * Handle bookkeeping for station deauthentication/disassociation + * when operating as an ap. + */ +void +ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, + "[%s] station with aid %d leaves\n", + ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_AID(ni)); + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO, + ("unexpected operating mode %u", ic->ic_opmode)); + /* + * If node wasn't previously associated all + * we need to do is reclaim the reference. + */ + /* XXX ibss mode bypasses 11g and notification */ + if (ni->ni_associd == 0) + goto done; + /* + * Tell the authenticator the station is leaving. + * Note that we must do this before yanking the + * association id as the authenticator uses the + * associd to locate it's state block. + */ + if (ic->ic_auth->ia_node_leave != NULL) + ic->ic_auth->ia_node_leave(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + ni->ni_associd = 0; + ic->ic_sta_assoc--; + + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_node_leave_11g(ic, ni); + /* + * Cleanup station state. In particular clear various + * state that might otherwise be reused if the node + * is reused before the reference count goes to zero + * (and memory is reclaimed). + */ + ieee80211_sta_leave(ic, ni); +done: + ni->ni_inact_reload = ic->ic_inact_init; /* just in case */ + ieee80211_free_node(ni); +} + +u_int8_t +ieee80211_getrssi(struct ieee80211com *ic) +{ +#define NZ(x) ((x) == 0 ? 1 : (x)) + struct ieee80211_node_table *nt = ic->ic_sta; + u_int32_t rssi_samples, rssi_total; + struct ieee80211_node *ni; + + rssi_total = 0; + rssi_samples = 0; + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: /* average of all ibss neighbors */ + nt = ic->ic_sta; + if (nt == NULL) + break; + /* XXX locking */ + TAILQ_FOREACH(ni, &ic->ic_sta->nt_node, ni_list) + if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ni); + } + break; + case IEEE80211_M_AHDEMO: /* average of all neighbors */ + nt = ic->ic_sta; + if (nt == NULL) + break; + /* XXX locking */ + TAILQ_FOREACH(ni, &ic->ic_sta->nt_node, ni_list) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ni); + } + break; + case IEEE80211_M_HOSTAP: /* average of all associated stations */ + nt = ic->ic_sta; + if (nt == NULL) + break; + /* XXX locking */ + TAILQ_FOREACH(ni, &ic->ic_sta->nt_node, ni_list) + if (IEEE80211_AID(ni->ni_associd) != 0) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ni); + } + break; + case IEEE80211_M_MONITOR: /* XXX */ + case IEEE80211_M_STA: /* use stats from associated ap */ + default: + if (ic->ic_bss != NULL) + rssi_total = ic->ic_node_getrssi(ic->ic_bss); + rssi_samples = 1; + break; + } + return rssi_total / NZ(rssi_samples); +#undef NZ +} + +/* + * Indicate whether there are frames queued for a station in power-save mode. + */ +static void +ieee80211_set_tim(struct ieee80211com *ic, struct ieee80211_node *ni, int set) +{ + u_int16_t aid; + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS, + ("operating mode %u", ic->ic_opmode)); + + aid = IEEE80211_AID(ni->ni_associd); + KASSERT(aid < ic->ic_max_aid, + ("bogus aid %u, max %u", aid, ic->ic_max_aid)); + + IEEE80211_BEACON_LOCK(ic); + if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) { + if (set) { + setbit(ic->ic_tim_bitmap, aid); + ic->ic_ps_pending++; + } else { + clrbit(ic->ic_tim_bitmap, aid); + ic->ic_ps_pending--; + } + ic->ic_flags |= IEEE80211_F_TIMUPDATE; + } + IEEE80211_BEACON_UNLOCK(ic); +} + +/* + * Node table support. + */ + +static void +ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, + const char *name, int inact, + void (*timeout)(struct ieee80211_node_table *)) +{ + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "%s %s table, inact %u\n", __func__, name, inact); + + nt->nt_ic = ic; + /* 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_timeout = timeout; +} + +static struct ieee80211_node_table * +ieee80211_node_table_alloc(struct ieee80211com *ic, + const char *name, int inact, + void (*timeout)(struct ieee80211_node_table *)) +{ + struct ieee80211_node_table *nt; + + MALLOC(nt, struct ieee80211_node_table *, + sizeof(struct ieee80211_node_table), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (nt == NULL) { + printf("%s: no memory node table!\n", __func__); + return NULL; + } + ieee80211_node_table_init(ic, nt, name, inact, timeout); + return nt; +} + +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); + nt->nt_inact_timer = 0; + 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_free_allnodes_locked(nt); + IEEE80211_SCAN_LOCK_DESTROY(nt); + IEEE80211_NODE_LOCK_DESTROY(nt); +} + +/* + * NB: public for use in ieee80211_proto.c + */ +void +ieee80211_node_table_free(struct ieee80211_node_table *nt) +{ + + IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE, + "%s %s table\n", __func__, nt->nt_name); - IEEE80211_NODE_LOCK(ic); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) - (*f)(arg, ni); - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_NODE_LOCK(nt); + nt->nt_inact_timer = 0; + ieee80211_node_table_cleanup(nt); + FREE(nt, M_DEVBUF); } diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index dfb8d4e..abd8cd2 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -34,24 +34,52 @@ #ifndef _NET80211_IEEE80211_NODE_H_ #define _NET80211_IEEE80211_NODE_H_ -#define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */ -#define IEEE80211_TRANS_WAIT 5 /* transition wait */ -#define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */ -#define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT) +#include /* for ieee80211_nodestats */ + +/* + * Each ieee80211com instance has a single timer that fires once a + * second. This is used to initiate various work depending on the + * state of the instance: scanning (passive or active), ``transition'' + * (waiting for a response to a management frame when operating + * as a station), and node inactivity processing (when operating + * as an AP). For inactivity processing each node has a timeout + * set in it's ni_inact field that is decremented on each timeout + * and the node is reclaimed when the counter goes to zero. We + * use different inactivity timeout values depending on whether + * the node is associated and authorized (either by 802.1x or + * open/shared key authentication) or associated but yet to be + * authorized. The latter timeout is shorter to more aggressively + * reclaim nodes that leave part way through the 802.1x exchange. + */ +#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */ +#define IEEE80211_INACT_INIT (30/IEEE80211_INACT_WAIT) /* initial */ +#define IEEE80211_INACT_AUTH (180/IEEE80211_INACT_WAIT) /* associated but not authorized */ +#define IEEE80211_INACT_RUN (300/IEEE80211_INACT_WAIT) /* authorized */ +#define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */ +#define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */ + +#define IEEE80211_TRANS_WAIT 5 /* mgt frame tx timer (secs) */ #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ #define IEEE80211_NODE_HASH(addr) \ - (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE) - -#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ -#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ + (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \ + IEEE80211_NODE_HASHSIZE) -struct ieee80211_rateset { - u_int8_t rs_nrates; - u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; +struct ieee80211_rsnparms { + u_int8_t rsn_mcastcipher; /* mcast/group cipher */ + u_int8_t rsn_mcastkeylen; /* mcast key length */ + u_int8_t rsn_ucastcipherset; /* unicast cipher set */ + u_int8_t rsn_ucastcipher; /* selected unicast cipher */ + u_int8_t rsn_ucastkeylen; /* unicast key length */ + u_int8_t rsn_keymgmtset; /* key mangement algorithms */ + u_int8_t rsn_keymgmt; /* selected key mgmt algo */ + u_int16_t rsn_caps; /* capabilities */ }; +struct ieee80211_node_table; +struct ieee80211com; + /* * Node specific information. Note that drivers are expected * to derive from this structure to add device-specific per-node @@ -59,10 +87,31 @@ struct ieee80211_rateset { * the ieee80211com structure. */ struct ieee80211_node { + struct ieee80211com *ni_ic; + struct ieee80211_node_table *ni_table; TAILQ_ENTRY(ieee80211_node) ni_list; LIST_ENTRY(ieee80211_node) ni_hash; u_int ni_refcnt; u_int ni_scangen; /* gen# for timeout scan */ + u_int8_t ni_authmode; /* authentication algorithm */ + u_int16_t ni_flags; /* special-purpose state */ +#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ +#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ +#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */ +/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */ +#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */ + u_int16_t ni_associd; /* assoc response */ + u_int16_t ni_txpower; /* current transmit power */ + u_int16_t ni_vlan; /* vlan tag */ + u_int32_t *ni_challenge; /* shared-key challenge */ + u_int8_t *ni_wpa_ie; /* captured WPA/RSN ie */ + u_int8_t *ni_wme_ie; /* captured WME ie */ + u_int16_t ni_txseqs[17]; /* tx seq per-tid */ + u_int16_t ni_rxseqs[17]; /* rx seq previous per-tid*/ + u_int32_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 */ u_int32_t ni_rstamp; /* recv timestamp */ @@ -73,90 +122,168 @@ struct ieee80211_node { u_int8_t ni_bssid[IEEE80211_ADDR_LEN]; /* beacon, probe response */ - u_int8_t ni_tstamp[8]; /* from last rcv'd beacon */ + union { + u_int8_t data[8]; + u_int64_t tsf; + } ni_tstamp; /* from last rcv'd beacon */ u_int16_t ni_intval; /* beacon interval */ u_int16_t ni_capinfo; /* capabilities */ u_int8_t ni_esslen; u_int8_t ni_essid[IEEE80211_NWID_LEN]; struct ieee80211_rateset ni_rates; /* negotiated rate set */ - u_int8_t *ni_country; /* country information XXX */ struct ieee80211_channel *ni_chan; u_int16_t ni_fhdwell; /* FH only */ u_int8_t ni_fhindex; /* FH only */ - u_int8_t ni_erp; /* 11g only */ - -#ifdef notyet - /* DTIM and contention free period (CFP) */ - u_int8_t ni_dtimperiod; - u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */ - u_int16_t ni_cfpduremain; /* remaining cfp duration */ - u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */ - u_int16_t ni_nextdtim; /* time to next DTIM */ - u_int16_t ni_timoffset; -#endif + u_int8_t ni_erp; /* ERP from beacon/probe resp */ + u_int16_t ni_timoff; /* byte offset to TIM ie */ /* others */ - u_int16_t ni_associd; /* assoc response */ - u_int16_t ni_txseq; /* seq to be transmitted */ - u_int16_t ni_rxseq; /* seq previous received */ int ni_fails; /* failure count to associate */ - int ni_inact; /* inactivity mark count */ + short ni_inact; /* inactivity mark count */ + short ni_inact_reload;/* inactivity reload value */ int ni_txrate; /* index to ni_rates[] */ + struct ifqueue ni_savedq; /* ps-poll queue */ + struct ieee80211_nodestats ni_stats; /* per-node statistics */ }; +MALLOC_DECLARE(M_80211_NODE); + +#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd) + +#define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++) +#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v) +#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v) static __inline struct ieee80211_node * ieee80211_ref_node(struct ieee80211_node *ni) { - atomic_add_int(&ni->ni_refcnt, 1); + ieee80211_node_incref(ni); return ni; } static __inline void ieee80211_unref_node(struct ieee80211_node **ni) { - atomic_subtract_int(&(*ni)->ni_refcnt, 1); + ieee80211_node_decref(*ni); *ni = NULL; /* guard against use */ } -#define IEEE80211_NODE_LOCK_INIT(_ic, _name) \ - mtx_init(&(_ic)->ic_nodelock, _name, "802.11 node table", MTX_DEF) -#define IEEE80211_NODE_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_nodelock) -#define IEEE80211_NODE_LOCK(_ic) mtx_lock(&(_ic)->ic_nodelock) -#define IEEE80211_NODE_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_nodelock) -#define IEEE80211_NODE_LOCK_ASSERT(_ic) \ - mtx_assert(&(_ic)->ic_nodelock, MA_OWNED) - struct ieee80211com; -#ifdef MALLOC_DECLARE -MALLOC_DECLARE(M_80211_NODE); -#endif +extern void ieee80211_node_attach(struct ieee80211com *); +extern void ieee80211_node_lateattach(struct ieee80211com *); +extern void ieee80211_node_detach(struct ieee80211com *); + +static __inline int +ieee80211_node_is_authorized(const struct ieee80211_node *ni) +{ + return (ni->ni_flags & IEEE80211_NODE_AUTH); +} -extern void ieee80211_node_attach(struct ifnet *); -extern void ieee80211_node_lateattach(struct ifnet *); -extern void ieee80211_node_detach(struct ifnet *); - -extern void ieee80211_begin_scan(struct ifnet *); -extern void ieee80211_next_scan(struct ifnet *); -extern void ieee80211_end_scan(struct ifnet *); -extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, - u_int8_t *); -extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *, - u_int8_t *); -extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, - u_int8_t *); -extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *, - u_int8_t *); -extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *, - u_int8_t *macaddr, struct ieee80211_channel *); -extern void ieee80211_free_node(struct ieee80211com *, +extern void ieee80211_node_authorize(struct ieee80211com *, struct ieee80211_node *); -extern void ieee80211_free_allnodes(struct ieee80211com *); +extern void ieee80211_node_unauthorize(struct ieee80211com *, + struct ieee80211_node *); + +extern void ieee80211_begin_scan(struct ieee80211com *, int); +extern int ieee80211_next_scan(struct ieee80211com *); +extern void ieee80211_create_ibss(struct ieee80211com*, + struct ieee80211_channel *); +extern void ieee80211_reset_bss(struct ieee80211com *); +extern void ieee80211_end_scan(struct ieee80211com *); +extern int ieee80211_ibss_merge(struct ieee80211com *, + struct ieee80211_node *); +extern int ieee80211_sta_join(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_sta_leave(struct ieee80211com *, + struct ieee80211_node *); + +/* + * Table of ieee80211_node instances. Each ieee80211com + * has at least one for holding the scan candidates. + * When operating as an access point or in ibss mode there + * is a second table for associated stations or neighbors. + */ +struct ieee80211_node_table { + struct ieee80211com *nt_ic; /* back reference */ + ieee80211_node_lock_t nt_nodelock; /* on node table */ + TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */ + LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE]; + const char *nt_name; /* for debugging */ + ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */ + u_int nt_scangen; /* gen# for timeout scan */ + int nt_inact_timer; /* inactivity timer */ + int nt_inact_init; /* initial node inact setting */ + + void (*nt_timeout)(struct ieee80211_node_table *); +}; +extern void ieee80211_node_table_reset(struct ieee80211_node_table *); +extern void ieee80211_node_table_free(struct ieee80211_node_table *); + +extern struct ieee80211_node *ieee80211_alloc_node( + struct ieee80211_node_table *, const u_int8_t *); +extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *, + const u_int8_t *); +#ifdef IEEE80211_DEBUG_REFCNT +extern void ieee80211_free_node_debug(struct ieee80211_node *, + const char *func, int line); +extern struct ieee80211_node *ieee80211_find_node_debug( + struct ieee80211_node_table *, const u_int8_t *, + const char *func, int line); +extern struct ieee80211_node * ieee80211_find_rxnode_debug( + struct ieee80211com *, const struct ieee80211_frame_min *, + const char *func, int line); +extern struct ieee80211_node *ieee80211_find_txnode_debug( + struct ieee80211com *, const u_int8_t *, + const char *func, int line); +extern struct ieee80211_node *ieee80211_find_node_with_channel_debug( + struct ieee80211_node_table *, const u_int8_t *macaddr, + struct ieee80211_channel *, const char *func, int line); +extern struct ieee80211_node *ieee80211_find_node_with_ssid_debug( + struct ieee80211_node_table *, const u_int8_t *macaddr, + u_int ssidlen, const u_int8_t *ssid, + const char *func, int line); +#define ieee80211_free_node(ni) \ + ieee80211_free_node_debug(ni, __func__, __LINE__) +#define ieee80211_find_node(nt, mac) \ + ieee80211_find_node_debug(nt, mac, __func__, __LINE__) +#define ieee80211_find_rxnode(nt, wh) \ + ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__) +#define ieee80211_find_txnode(nt, mac) \ + ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__) +#define ieee80211_find_node_with_channel(nt, mac, c) \ + ieee80211_find_node_with_channel_debug(nt, mac, c, __func__, __LINE__) +#define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \ + ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__) +#else +extern void ieee80211_free_node(struct ieee80211_node *); +extern struct ieee80211_node *ieee80211_find_node( + struct ieee80211_node_table *, const u_int8_t *); +extern struct ieee80211_node * ieee80211_find_rxnode( + struct ieee80211com *, const struct ieee80211_frame_min *); +extern struct ieee80211_node *ieee80211_find_txnode( + struct ieee80211com *, const u_int8_t *); +extern struct ieee80211_node *ieee80211_find_node_with_channel( + struct ieee80211_node_table *, const u_int8_t *macaddr, + struct ieee80211_channel *); +extern struct ieee80211_node *ieee80211_find_node_with_ssid( + struct ieee80211_node_table *, const u_int8_t *macaddr, + u_int ssidlen, const u_int8_t *ssid); +#endif + typedef void ieee80211_iter_func(void *, struct ieee80211_node *); -extern void ieee80211_iterate_nodes(struct ieee80211com *ic, +extern void ieee80211_iterate_nodes(struct ieee80211_node_table *, ieee80211_iter_func *, void *); -extern void ieee80211_timeout_nodes(struct ieee80211com *); -extern void ieee80211_create_ibss(struct ieee80211com* , - struct ieee80211_channel *); +extern void ieee80211_dump_node(struct ieee80211_node_table *, + struct ieee80211_node *); +extern void ieee80211_dump_nodes(struct ieee80211_node_table *); + +extern struct ieee80211_node *ieee80211_fakeup_adhoc_node( + struct ieee80211_node_table *nt, + const u_int8_t macaddr[]); +extern void ieee80211_node_join(struct ieee80211com *, + struct ieee80211_node *, int); +extern void ieee80211_node_leave(struct ieee80211com *, + struct ieee80211_node *); +extern u_int8_t ieee80211_getrssi(struct ieee80211com *ic); #endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index b08fff0..219bc25 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,32 +38,43 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include -#include -#include #include -#include -#include -#include -#include -#include +#include -#include -#include -#include -#include +#include #include +#include #include +#include +#include #include -#include - #ifdef INET #include #include +#include +#include +#endif + +#ifdef IEEE80211_DEBUG +/* + * Decide if an outbound management frame should be + * printed when debugging is enabled. This filters some + * of the less interesting frames that come frequently + * (e.g. beacons). + */ +static __inline int +doprint(struct ieee80211com *ic, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return (ic->ic_opmode == IEEE80211_M_IBSS); + } + return 1; +} #endif /* @@ -74,14 +85,13 @@ __FBSDID("$FreeBSD$"); * reference (and potentially free'ing up any associated storage). */ static int -ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, +ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, struct mbuf *m, int type) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; struct ieee80211_frame *wh; KASSERT(ni != NULL, ("null node")); - ni->ni_inact = 0; /* * Yech, hack alert! We want to pass the node down to the @@ -106,28 +116,46 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; *(u_int16_t *)wh->i_dur = 0; *(u_int16_t *)wh->i_seq = - htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); - ni->ni_txseq++; - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; + /* + * Hack. When sending PROBE_REQ frames while scanning we + * explicitly force a broadcast rather than (as before) clobber + * ni_macaddr and ni_bssid. This is stopgap, we need a way + * to communicate this directly rather than do something + * implicit based on surrounding state. + */ + if (type == IEEE80211_FC0_SUBTYPE_PROBE_REQ && + (ic->ic_flags & IEEE80211_F_SCAN)) { + IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); + } else { + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + } - if (ifp->if_flags & IFF_DEBUG) { - /* avoid to print too many frames */ - if (ic->ic_opmode == IEEE80211_M_IBSS || + 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__); + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } #ifdef IEEE80211_DEBUG - ieee80211_debug > 1 || -#endif - (type & IEEE80211_FC0_SUBTYPE_MASK) != - IEEE80211_FC0_SUBTYPE_PROBE_RESP) - if_printf(ifp, "sending %s to %s on channel %u\n", - ieee80211_mgt_subtype_name[ - (type & IEEE80211_FC0_SUBTYPE_MASK) - >> IEEE80211_FC0_SUBTYPE_SHIFT], - ether_sprintf(ni->ni_macaddr), - ieee80211_chan2ieee(ic, ni->ni_chan)); + /* avoid printing too many frames */ + if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || + ieee80211_msg_dumppkts(ic)) { + printf("[%s] send %s on channel %u\n", + ether_sprintf(wh->i_addr1), + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ieee80211_chan2ieee(ic, ni->ni_chan)); } - +#endif + IEEE80211_NODE_STAT(ni, tx_mgmt); IF_ENQUEUE(&ic->ic_mgtq, m); ifp->if_timer = 1; if_start(ifp); @@ -135,41 +163,315 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, } /* - * Encapsulate an outbound data frame. The mbuf chain is updated and - * a reference to the destination node is returned. If an error is - * encountered NULL is returned and the node reference will also be NULL. - * - * NB: The caller is responsible for free'ing a returned node reference. - * The convention is ic_bss is not reference counted; the caller must - * maintain that. + * Send a null data frame to the specified node. + */ +int +ieee80211_send_nulldata(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ifnet *ifp = ic->ic_ifp; + struct mbuf *m; + struct ieee80211_frame *wh; + + MGETHDR(m, M_NOWAIT, MT_HEADER); + if (m == NULL) { + /* XXX debug msg */ + ic->ic_stats.is_tx_nobuf++; + return ENOMEM; + } + m->m_pkthdr.rcvif = (void *) ieee80211_ref_node(ni); + + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA | + IEEE80211_FC0_SUBTYPE_NODATA; + *(u_int16_t *)wh->i_dur = 0; + *(u_int16_t *)wh->i_seq = + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; + + /* XXX WDS */ + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_myaddr); + m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); + + IEEE80211_NODE_STAT(ni, tx_data); + + IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ + if_start(ifp); + + return 0; +} + +/* + * Insure there is sufficient contiguous space to encapsulate the + * 802.11 data frame. If room isn't already there, arrange for it. + * Drivers and cipher modules assume we have done the necessary work + * and fail rudely if they don't find the space they need. + */ +static struct mbuf * +ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, + struct ieee80211_key *key, struct mbuf *m) +{ +#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) + int needed_space = hdrsize; + + if (key != NULL) { + /* XXX belongs in crypto code? */ + needed_space += key->wk_cipher->ic_header; + /* XXX frags */ + } + /* + * We know we are called just before stripping an Ethernet + * header and prepending an LLC header. This means we know + * there will be + * sizeof(struct ether_header) - sizeof(struct llc) + * bytes recovered to which we need additional space for the + * 802.11 header and any crypto header. + */ + if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { + struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); + if (n == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + "%s: cannot expand storage\n", __func__); + ic->ic_stats.is_tx_nobuf++; + m_freem(m); + return NULL; + } + KASSERT(needed_space <= MHLEN, + ("not enough room, need %u got %u\n", needed_space, MHLEN)); + /* + * Setup new mbuf to have leading space to prepend the + * 802.11 header and any crypto header bits that are + * required (the latter are added when the driver calls + * back to ieee80211_crypto_encap to do crypto encapsulation). + */ + /* NB: must be first 'cuz it clobbers m_data */ + m_move_pkthdr(n, m); + n->m_len = 0; /* NB: m_gethdr does not set */ + n->m_data += needed_space; + /* + * Pull up Ethernet header to create the expected layout. + * We could use m_pullup but that's overkill (i.e. we don't + * need the actual data) and it cannot fail so do it inline + * for speed. + */ + /* NB: struct ether_header is known to be contiguous */ + n->m_len += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_data += sizeof(struct ether_header); + /* + * Replace the head of the chain. + */ + n->m_next = m; + m = n; + } + return m; +#undef TO_BE_RECLAIMED +} + +/* + * Assign priority to a frame based on any vlan tag assigned + * to the station and/or any Diffserv setting in an IP header. + * Finally, if an ACM policy is setup (in station mode) it's + * applied. + */ +int +ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) +{ + int v_wme_ac, d_wme_ac, ac; +#ifdef INET + struct ether_header *eh; +#endif + + if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { + ac = WME_AC_BE; + goto done; + } + + /* + * If node has a vlan tag then all traffic + * to it must have a matching tag. + */ + v_wme_ac = 0; + if (ni->ni_vlan != 0) { + struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m); + if (mtag != NULL) { + IEEE80211_NODE_STAT(ni, tx_novlantag); + return 1; + } + if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) != + EVL_VLANOFTAG(ni->ni_vlan)) { + IEEE80211_NODE_STAT(ni, tx_vlanmismatch); + return 1; + } + /* map vlan priority to AC */ + switch (EVL_PRIOFTAG(ni->ni_vlan)) { + case 1: + case 2: + v_wme_ac = WME_AC_BK; + break; + case 0: + case 3: + v_wme_ac = WME_AC_BE; + break; + case 4: + case 5: + v_wme_ac = WME_AC_VI; + break; + case 6: + case 7: + v_wme_ac = WME_AC_VO; + break; + } + } + +#ifdef INET + eh = mtod(m, struct ether_header *); + if (eh->ether_type == htons(ETHERTYPE_IP)) { + const struct ip *ip = (struct ip *) + (mtod(m, u_int8_t *) + sizeof (*eh)); + /* + * IP frame, map the TOS field. + */ + switch (ip->ip_tos) { + case 0x08: + case 0x20: + d_wme_ac = WME_AC_BK; /* background */ + break; + case 0x28: + case 0xa0: + d_wme_ac = WME_AC_VI; /* video */ + break; + case 0x30: /* voice */ + case 0xe0: + case 0x88: /* XXX UPSD */ + case 0xb8: + d_wme_ac = WME_AC_VO; + break; + default: + d_wme_ac = WME_AC_BE; + break; + } + } else { +#endif /* INET */ + d_wme_ac = WME_AC_BE; +#ifdef INET + } +#endif + /* + * Use highest priority AC. + */ + if (v_wme_ac > d_wme_ac) + ac = v_wme_ac; + else + ac = d_wme_ac; + + /* + * Apply ACM policy. + */ + if (ic->ic_opmode == IEEE80211_M_STA) { + static const int acmap[4] = { + WME_AC_BK, /* WME_AC_BE */ + WME_AC_BK, /* WME_AC_BK */ + WME_AC_BE, /* WME_AC_VI */ + WME_AC_VI, /* WME_AC_VO */ + }; + while (ac != WME_AC_BK && + ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) + ac = acmap[ac]; + } +done: + M_WME_SETAC(m, ac); + return 0; +} + +/* + * Return the transmit key to use in sending a frame to the specified + * destination. Multicast traffic always uses the group key which is + * installed the default tx key. Otherwise if a unicast key is set + * we use that. When no unicast key is set we fall back to the default + * transmit key unless WPA is enabled in which case there should be + * a unicast frame so we don't want to use a default key (which in + * this case is the group/multicast key). + */ +static __inline struct ieee80211_key * +ieee80211_crypto_getkey(struct ieee80211com *ic, + const u_int8_t mac[IEEE80211_ADDR_LEN], struct ieee80211_node *ni) +{ +#define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none) + if (IEEE80211_IS_MULTICAST(mac)) { + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || + KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) + return NULL; + return &ic->ic_nw_keys[ic->ic_def_txkey]; + } else if (KEY_UNDEFINED(ni->ni_ucastkey)) { + if ((ic->ic_flags & IEEE80211_F_WPA) || + ic->ic_def_txkey == IEEE80211_KEYIX_NONE || + KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) + return NULL; + return &ic->ic_nw_keys[ic->ic_def_txkey]; + } else { + return &ni->ni_ucastkey; + } +#undef KEY_UNDEFINED +} + +/* + * Encapsulate an outbound data frame. The mbuf chain is updated. + * If an error is encountered NULL is returned. The caller is required + * to provide a node reference and pullup the ethernet header in the + * first mbuf. */ struct mbuf * -ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) +ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) { - struct ieee80211com *ic = (void *)ifp; struct ether_header eh; struct ieee80211_frame *wh; - struct ieee80211_node *ni = NULL; + struct ieee80211_key *key; struct llc *llc; + int hdrsize, datalen; - if (m->m_len < sizeof(struct ether_header)) { - m = m_pullup(m, sizeof(struct ether_header)); - if (m == NULL) { - ic->ic_stats.is_tx_nombuf++; - goto bad; - } - } + KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); - ni = ieee80211_find_txnode(ic, eh.ether_dhost); - if (ni == NULL) { - IEEE80211_DPRINTF(("%s: no node for dst %s, discard frame\n", - __func__, ether_sprintf(eh.ether_dhost))); - ic->ic_stats.is_tx_nonode++; + /* + * Insure space for additional headers. First identify + * transmit key to use in calculating any buffer adjustments + * required. This is also used below to do privacy + * encapsulation work. Then calculate the 802.11 header + * size and any padding required by the driver. + * + * Note key may be NULL if we fall back to the default + * transmit key and that is not set. In that case the + * buffer may not be expanded as needed by the cipher + * routines, but they will/should discard it. + */ + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + key = ieee80211_crypto_getkey(ic, eh.ether_dhost, ni); + if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%s] no default transmit key\n", + ether_sprintf(ni->ni_macaddr)); + /* XXX statistic */ + } + } else + key = NULL; + /* XXX 4-address format */ + if (ni->ni_flags & IEEE80211_NODE_QOS) + hdrsize = sizeof(struct ieee80211_qosframe); + else + hdrsize = sizeof(struct ieee80211_frame); + if (ic->ic_flags & IEEE80211_F_DATAPAD) + hdrsize = roundup(hdrsize, sizeof(u_int32_t)); + m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); + if (m == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; } - ni->ni_inact = 0; + /* NB: this could be optimized because of ieee80211_mbuf_adjust */ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; @@ -178,17 +480,16 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) llc->llc_snap.org_code[1] = 0; llc->llc_snap.org_code[2] = 0; llc->llc_snap.ether_type = eh.ether_type; - M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ + + M_PREPEND(m, hdrsize, M_DONTWAIT); if (m == NULL) { - ic->ic_stats.is_tx_nombuf++; + ic->ic_stats.is_tx_nobuf++; goto bad; } wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; *(u_int16_t *)wh->i_dur = 0; - *(u_int16_t *)wh->i_seq = - htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); - ni->ni_txseq++; switch (ic->ic_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; @@ -212,21 +513,62 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) case IEEE80211_M_MONITOR: goto bad; } - *pni = ni; + if (eh.ether_type != htons(ETHERTYPE_PAE) || + (key != NULL && (ic->ic_flags & IEEE80211_F_WPA))) { + /* + * IEEE 802.1X: send EAPOL frames always in the clear. + * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. + */ + if (key != NULL) { + wh->i_fc[1] |= IEEE80211_FC1_WEP; + /* XXX do fragmentation */ + if (!ieee80211_crypto_enmic(ic, key, m)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + "[%s] enmic failed, discard frame\n", + ether_sprintf(eh.ether_dhost)); + /* XXX statistic */ + goto bad; + } + } + } + + if (ni->ni_flags & IEEE80211_NODE_QOS) { + struct ieee80211_qosframe *qwh = + (struct ieee80211_qosframe *) wh; + int ac, tid; + + ac = M_WME_GETAC(m); + /* map from access class/queue to 11e header priorty value */ + tid = WME_AC_TO_TID(ac); + qwh->i_qos[0] = tid & IEEE80211_QOS_TID; + if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) + qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S; + qwh->i_qos[1] = 0; + qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + + *(u_int16_t *)wh->i_seq = + htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[tid]++; + } else { + *(u_int16_t *)wh->i_seq = + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; + } + + IEEE80211_NODE_STAT(ni, tx_data); + IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); + return m; bad: if (m != NULL) m_freem(m); - if (ni && ni != ic->ic_bss) - ieee80211_free_node(ic, ni); - *pni = NULL; return NULL; } /* * Add a supported rates element id to a frame. */ -u_int8_t * +static u_int8_t * ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) { int nrates; @@ -243,7 +585,7 @@ ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) /* * Add an extended supported rates element id to a frame. */ -u_int8_t * +static u_int8_t * ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) { /* @@ -271,20 +613,278 @@ ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) return frm + len; } -static struct mbuf * -ieee80211_getmbuf(int flags, int type, u_int pktlen) +/* + * Add an erp element to a frame. + */ +static u_int8_t * +ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic) { - struct mbuf *m; + u_int8_t erp; + + *frm++ = IEEE80211_ELEMID_ERP; + *frm++ = 1; + erp = 0; + if (ic->ic_nonerpsta != 0) + erp |= IEEE80211_ERP_NON_ERP_PRESENT; + if (ic->ic_flags & IEEE80211_F_USEPROT) + erp |= IEEE80211_ERP_USE_PROTECTION; + if (ic->ic_flags & IEEE80211_F_USEBARKER) + erp |= IEEE80211_ERP_LONG_PREAMBLE; + *frm++ = erp; + return frm; +} + +static u_int8_t * +ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) +{ +#define WPA_OUI_BYTES 0x00, 0x50, 0xf2 +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDSELECTOR(frm, sel) do { \ + memcpy(frm, sel, 4); \ + frm += 4; \ +} while (0) + static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; + static const u_int8_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 u_int8_t wep104_suite[4] = + { WPA_OUI_BYTES, WPA_CSE_WEP104 }; + static const u_int8_t key_mgt_unspec[4] = + { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; + static const u_int8_t key_mgt_psk[4] = + { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + u_int8_t *frm = ie; + u_int8_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); - KASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen)); - if (pktlen <= MHLEN) - MGETHDR(m, flags, type); + /* XXX filter out CKIP */ + + /* multicast cipher */ + if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && + rsn->rsn_mcastkeylen >= 13) + ADDSELECTOR(frm, wep104_suite); else - m = m_getcl(flags, type, M_PKTHDR); - return m; + ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); + + /* unicast cipher list */ + selcnt = frm; + ADDSHORT(frm, 0); /* selector count */ + if (rsn->rsn_ucastcipherset & (1<rsn_ucastcipherset & (1<rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_unspec); + } + if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_psk); + } + + /* optional capabilities */ + if (rsn->rsn_caps != 0) + 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 > %u", + ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + return frm; +#undef ADDSHORT +#undef ADDSELECTOR +#undef WPA_OUI_BYTES +} + +static u_int8_t * +ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) +{ +#define RSN_OUI_BYTES 0x00, 0x0f, 0xac +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDSELECTOR(frm, sel) do { \ + memcpy(frm, sel, 4); \ + frm += 4; \ +} while (0) + static const u_int8_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 u_int8_t wep104_suite[4] = + { RSN_OUI_BYTES, RSN_CSE_WEP104 }; + static const u_int8_t key_mgt_unspec[4] = + { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; + static const u_int8_t key_mgt_psk[4] = + { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + u_int8_t *frm = ie; + u_int8_t *selcnt; + + *frm++ = IEEE80211_ELEMID_RSN; + *frm++ = 0; /* length filled in below */ + ADDSHORT(frm, RSN_VERSION); + + /* XXX filter out CKIP */ + + /* multicast cipher */ + if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && + rsn->rsn_mcastkeylen >= 13) + ADDSELECTOR(frm, wep104_suite); + else + ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); + + /* unicast cipher list */ + selcnt = frm; + ADDSHORT(frm, 0); /* selector count */ + if (rsn->rsn_ucastcipherset & (1<rsn_ucastcipherset & (1<rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_unspec); + } + if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_psk); + } + + /* optional capabilities */ + if (rsn->rsn_caps != 0) + 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 > %u", + ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + return frm; +#undef ADDSELECTOR +#undef ADDSHORT +#undef RSN_OUI_BYTES +} + +/* + * Add a WPA/RSN element to a frame. + */ +static u_int8_t * +ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic) +{ + + KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); + if (ic->ic_flags & IEEE80211_F_WPA2) + frm = ieee80211_setup_rsn_ie(ic, frm); + if (ic->ic_flags & IEEE80211_F_WPA1) + frm = ieee80211_setup_wpa_ie(ic, frm); + return frm; +} + +#define WME_OUI_BYTES 0x00, 0x50, 0xf2 +/* + * Add a WME information element to a frame. + */ +static u_int8_t * +ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme) +{ + static const struct ieee80211_wme_info info = { + .wme_id = IEEE80211_ELEMID_VENDOR, + .wme_len = sizeof(struct ieee80211_wme_info) - 2, + .wme_oui = { WME_OUI_BYTES }, + .wme_type = WME_OUI_TYPE, + .wme_subtype = WME_INFO_OUI_SUBTYPE, + .wme_version = WME_VERSION, + .wme_info = 0, + }; + memcpy(frm, &info, sizeof(info)); + return frm + sizeof(info); } /* + * Add a WME parameters element to a frame. + */ +static u_int8_t * +ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme) +{ +#define SM(_v, _f) (((_v) << _f##_S) & _f) +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + /* NB: this works 'cuz a param has an info at the front */ + static const struct ieee80211_wme_info param = { + .wme_id = IEEE80211_ELEMID_VENDOR, + .wme_len = sizeof(struct ieee80211_wme_param) - 2, + .wme_oui = { WME_OUI_BYTES }, + .wme_type = WME_OUI_TYPE, + .wme_subtype = WME_PARAM_OUI_SUBTYPE, + .wme_version = WME_VERSION, + }; + int i; + + memcpy(frm, ¶m, sizeof(param)); + frm += __offsetof(struct ieee80211_wme_info, wme_info); + *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ + *frm++ = 0; /* reserved field */ + for (i = 0; i < WME_NUM_AC; i++) { + const struct wmeParams *ac = + &wme->wme_bssChanParams.cap_wmeParams[i]; + *frm++ = SM(i, WME_PARAM_ACI) + | SM(ac->wmep_acm, WME_PARAM_ACM) + | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) + ; + *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) + | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) + ; + ADDSHORT(frm, ac->wmep_txopLimit); + } + return frm; +#undef SM +#undef ADDSHORT +} +#undef WME_OUI_BYTES + +/* * Send a management frame. The node is for the destination (or ic_bss * when in station mode). Nodes other than ic_bss have their reference * count bumped to reflect our use for an indeterminant time. @@ -294,12 +894,11 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, int type, int arg) { #define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) - struct ifnet *ifp = &ic->ic_if; struct mbuf *m; u_int8_t *frm; enum ieee80211_phymode mode; u_int16_t capinfo; - int ret, timer; + int has_challenge, is_shared_key, ret, timer, status; KASSERT(ni != NULL, ("null node")); @@ -308,8 +907,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * the xmit is complete all the way in the driver. On error we * will remove our reference. */ - if (ni != ic->ic_bss) - ieee80211_ref_node(ni); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %s refcnt %d\n", + __func__, __LINE__, + ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + timer = 0; switch (type) { case IEEE80211_FC0_SUBTYPE_PROBE_REQ: @@ -318,22 +922,34 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME (optional) + * [tlv] user-specified ie's */ - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, - 2 + ic->ic_des_esslen + m = ieee80211_getmgtframe(&frm, + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_wme_param) + + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); + senderr(ENOMEM, is_tx_nobuf); + frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); mode = ieee80211_chan2mode(ic, ni->ni_chan); frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); + if (ic->ic_flags & IEEE80211_F_WME) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + if (ic->ic_opt_ie != NULL) { + memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); + frm += ic->ic_opt_ie_len; + } m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); - timer = IEEE80211_TRANS_WAIT; + IEEE80211_NODE_STAT(ni, tx_probereq); + if (ic->ic_opmode == IEEE80211_M_STA) + timer = IEEE80211_TRANS_WAIT; break; case IEEE80211_FC0_SUBTYPE_PROBE_RESP: @@ -346,19 +962,26 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] supported rates * [tlv] parameter set (FH/DS) * [tlv] parameter set (IBSS) + * [tlv] extended rate phy (ERP) * [tlv] extended supported rates + * [tlv] WPA */ - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, - 8 + 2 + 2 + 2 - + 2 + ni->ni_esslen + m = ieee80211_getmgtframe(&frm, + 8 + + sizeof(u_int16_t) + + sizeof(u_int16_t) + + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE - + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3) + + 7 /* max(7,3) */ + 6 - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 3 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + /* XXX !WPA1+WPA2 fits w/o a cluster */ + + (ic->ic_flags & IEEE80211_F_WPA ? + 2*sizeof(struct ieee80211_ie_wpa) : 0) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); + senderr(ENOMEM, is_tx_nobuf); memset(frm, 0, 8); /* timestamp should be filled later */ frm += 8; @@ -368,17 +991,19 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, capinfo = IEEE80211_CAPINFO_IBSS; else capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) + if (ic->ic_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; *(u_int16_t *)frm = htole16(capinfo); frm += 2; frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen); - frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); + frm = ieee80211_add_rates(frm, &ni->ni_rates); if (ic->ic_phytype == IEEE80211_T_FH) { *frm++ = IEEE80211_ELEMID_FHPARMS; @@ -400,44 +1025,97 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, *frm++ = IEEE80211_ELEMID_IBSSPARMS; *frm++ = 2; *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ - } else { /* IEEE80211_M_HOSTAP */ - /* TODO: TIM */ - *frm++ = IEEE80211_ELEMID_TIM; - *frm++ = 4; /* length */ - *frm++ = 0; /* DTIM count */ - *frm++ = 1; /* DTIM period */ - *frm++ = 0; /* bitmap control */ - *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ } - frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); + if (ic->ic_flags & IEEE80211_F_WPA) + frm = ieee80211_add_wpa(frm, ic); + if (ic->ic_curmode == IEEE80211_MODE_11G) + frm = ieee80211_add_erp(frm, ic); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); break; case IEEE80211_FC0_SUBTYPE_AUTH: - MGETHDR(m, M_DONTWAIT, MT_DATA); + status = arg >> 16; + arg &= 0xffff; + has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + ni->ni_challenge != NULL); + + /* + * Deduce whether we're doing open authentication or + * shared key authentication. We do the latter if + * we're in the middle of a shared key authentication + * handshake or if we're initiating an authentication + * request and configured to use shared key. + */ + is_shared_key = has_challenge || + arg >= IEEE80211_AUTH_SHARED_RESPONSE || + (arg == IEEE80211_AUTH_SHARED_REQUEST && + ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + + m = ieee80211_getmgtframe(&frm, + 3 * sizeof(u_int16_t) + + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? + sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - MH_ALIGN(m, 2 * 3); - m->m_pkthdr.len = m->m_len = 6; - frm = mtod(m, u_int8_t *); - /* TODO: shared key auth */ - ((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN); + senderr(ENOMEM, is_tx_nobuf); + + ((u_int16_t *)frm)[0] = + (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) + : htole16(IEEE80211_AUTH_ALG_OPEN); ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ - ((u_int16_t *)frm)[2] = 0; /* status */ + ((u_int16_t *)frm)[2] = htole16(status);/* status */ + + if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { + ((u_int16_t *)frm)[3] = + htole16((IEEE80211_CHALLENGE_LEN << 8) | + IEEE80211_ELEMID_CHALLENGE); + memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + m->m_pkthdr.len = m->m_len = + 4 * sizeof(u_int16_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__); + m->m_flags |= M_LINK0; /* WEP-encrypt, please */ + } + } else + m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t); + + /* XXX not right for shared key */ + if (status == IEEE80211_STATUS_SUCCESS) + IEEE80211_NODE_STAT(ni, tx_auth); + else + IEEE80211_NODE_STAT(ni, tx_auth_fail); + + /* + * When 802.1x is not in use mark the port + * authorized at this point so traffic can flow. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + status == IEEE80211_STATUS_SUCCESS && + ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ic, ni); if (ic->ic_opmode == IEEE80211_M_STA) timer = IEEE80211_TRANS_WAIT; break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %s deauthenticate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), arg); - MGETHDR(m, M_DONTWAIT, MT_DATA); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + "[%s] send station deauthenticate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg); + m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - MH_ALIGN(m, 2); - m->m_pkthdr.len = m->m_len = 2; - *mtod(m, u_int16_t *) = htole16(arg); /* reason */ + senderr(ENOMEM, is_tx_nobuf); + *(u_int16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(u_int16_t); + + IEEE80211_NODE_STAT(ni, tx_deauth); + IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); + + ieee80211_node_unauthorize(ic, ni); /* port closed */ break; case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: @@ -450,25 +1128,28 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME + * [tlv] user-specified ie's */ - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, - sizeof(capinfo) + m = ieee80211_getmgtframe(&frm, + sizeof(u_int16_t) + sizeof(u_int16_t) + IEEE80211_ADDR_LEN - + 2 + ni->ni_esslen + + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_wme_info) + + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); + senderr(ENOMEM, is_tx_nobuf); capinfo = 0; if (ic->ic_opmode == IEEE80211_M_IBSS) capinfo |= IEEE80211_CAPINFO_IBSS; else /* IEEE80211_M_STA */ capinfo |= IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) + if (ic->ic_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; /* * NB: Some 11a AP's reject the request when @@ -477,7 +1158,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; - if (ic->ic_flags & IEEE80211_F_SHSLOT) + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) && + (ic->ic_caps & IEEE80211_C_SHSLOT)) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; *(u_int16_t *)frm = htole16(capinfo); frm += 2; @@ -493,6 +1175,12 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + if (ic->ic_opt_ie != NULL) { + memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); + frm += ic->ic_opt_ie_len; + } m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); timer = IEEE80211_TRANS_WAIT; @@ -507,67 +1195,395 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [2] association ID * [tlv] supported rates * [tlv] extended supported rates + * [tlv] WME (if enabled and STA enabled) */ - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, - sizeof(capinfo) + m = ieee80211_getmgtframe(&frm, + sizeof(u_int16_t) + sizeof(u_int16_t) + sizeof(u_int16_t) + 2 + IEEE80211_RATE_SIZE - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_wme_param) + ); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); + senderr(ENOMEM, is_tx_nobuf); capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) + if (ic->ic_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; *(u_int16_t *)frm = htole16(capinfo); frm += 2; *(u_int16_t *)frm = htole16(arg); /* status */ frm += 2; - if (arg == IEEE80211_STATUS_SUCCESS) + if (arg == IEEE80211_STATUS_SUCCESS) { *(u_int16_t *)frm = htole16(ni->ni_associd); + IEEE80211_NODE_STAT(ni, tx_assoc); + } else + IEEE80211_NODE_STAT(ni, tx_assoc_fail); frm += 2; frm = ieee80211_add_rates(frm, &ni->ni_rates); frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: - if (ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "station %s disassociate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), arg); - MGETHDR(m, M_DONTWAIT, MT_DATA); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + "[%s] send station disassociate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg); + m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); if (m == NULL) - senderr(ENOMEM, is_tx_nombuf); - MH_ALIGN(m, 2); - m->m_pkthdr.len = m->m_len = 2; - *mtod(m, u_int16_t *) = htole16(arg); /* reason */ + senderr(ENOMEM, is_tx_nobuf); + *(u_int16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(u_int16_t); + + IEEE80211_NODE_STAT(ni, tx_disassoc); + IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); break; default: - IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n", - __func__, type)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "[%s] invalid mgmt frame type %u\n", + ether_sprintf(ni->ni_macaddr), type); senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } - ret = ieee80211_mgmt_output(ifp, ni, m, type); + ret = ieee80211_mgmt_output(ic, ni, m, type); if (ret == 0) { if (timer) ic->ic_mgt_timer = timer; } else { bad: - if (ni != ic->ic_bss) /* remove ref we added */ - ieee80211_free_node(ic, ni); + ieee80211_free_node(ni); } return ret; #undef senderr } + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +struct mbuf * +ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_frame *wh; + struct mbuf *m; + int pktlen; + u_int8_t *frm, *efrm; + u_int16_t capinfo; + struct ieee80211_rateset *rs; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [tlv] parameter set (IBSS/TIM) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] WME parameters + * [tlv] WPA/RSN parameters + * XXX Vendor-specific OIDs (e.g. Atheros) + * NB: we allocate the max space required for the TIM bitmap. + */ + rs = &ni->ni_rates; + pktlen = 8 /* time stamp */ + + sizeof(u_int16_t) /* beacon interval */ + + sizeof(u_int16_t) /* capabilities */ + + 2 + ni->ni_esslen /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* supported rates */ + + 2 + 1 /* DS parameters */ + + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ + + 2 + 1 /* ERP */ + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (ic->ic_caps & IEEE80211_C_WME ? /* WME */ + sizeof(struct ieee80211_wme_param) : 0) + + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ + 2*sizeof(struct ieee80211_ie_wpa) : 0) + ; + m = ieee80211_getmgtframe(&frm, pktlen); + if (m == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: cannot get buf; size %u\n", __func__, pktlen); + ic->ic_stats.is_tx_nobuf++; + return NULL; + } + + memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ + frm += 8; + *(u_int16_t *)frm = htole16(ni->ni_intval); + frm += 2; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + bo->bo_caps = (u_int16_t *)frm; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + *frm++ = IEEE80211_ELEMID_SSID; + if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { + *frm++ = ni->ni_esslen; + memcpy(frm, ni->ni_essid, ni->ni_esslen); + frm += ni->ni_esslen; + } else + *frm++ = 0; + frm = ieee80211_add_rates(frm, rs); + if (ic->ic_curmode != IEEE80211_MODE_FH) { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + bo->bo_tim = frm; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + bo->bo_tim_len = 0; + } else { + struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; + + tie->tim_ie = IEEE80211_ELEMID_TIM; + tie->tim_len = 4; /* length */ + tie->tim_count = 0; /* DTIM count */ + tie->tim_period = ic->ic_dtim_period; /* DTIM period */ + tie->tim_bitctl = 0; /* bitmap control */ + tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ + frm += sizeof(struct ieee80211_tim_ie); + bo->bo_tim_len = 1; + } + bo->bo_trailer = frm; + if (ic->ic_flags & IEEE80211_F_WME) { + bo->bo_wme = frm; + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + } + if (ic->ic_flags & IEEE80211_F_WPA) + frm = ieee80211_add_wpa(frm, ic); + if (ic->ic_curmode == IEEE80211_MODE_11G) + frm = ieee80211_add_erp(frm, ic); + efrm = ieee80211_add_xrates(frm, rs); + bo->bo_trailer_len = efrm - bo->bo_trailer; + m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *); + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + KASSERT(m != NULL, ("no space for 802.11 header?")); + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)wh->i_dur = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + *(u_int16_t *)wh->i_seq = 0; + + return m; +} + +/* + * Update the dynamic parts of a beacon frame based on the current state. + */ +int +ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) +{ + int len_changed = 0; + u_int16_t capinfo; + + IEEE80211_BEACON_LOCK(ic); + /* XXX faster to recalculate entirely or just changes? */ + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *bo->bo_caps = htole16(capinfo); + + if (ic->ic_flags & IEEE80211_F_WME) { + struct ieee80211_wme_state *wme = &ic->ic_wme; + + /* + * Check for agressive mode change. When there is + * significant high priority traffic in the BSS + * throttle back BE traffic by using conservative + * parameters. Otherwise BE uses agressive params + * to optimize performance of legacy/non-QoS traffic. + */ + if (wme->wme_flags & WME_F_AGGRMODE) { + if (wme->wme_hipri_traffic > + wme->wme_hipri_switch_thresh) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: traffic %u, disable aggressive mode\n", + __func__, wme->wme_hipri_traffic); + wme->wme_flags &= ~WME_F_AGGRMODE; + ieee80211_wme_updateparams_locked(ic); + wme->wme_hipri_traffic = + wme->wme_hipri_switch_hysteresis; + } else + wme->wme_hipri_traffic = 0; + } else { + if (wme->wme_hipri_traffic <= + wme->wme_hipri_switch_thresh) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: traffic %u, enable aggressive mode\n", + __func__, wme->wme_hipri_traffic); + wme->wme_flags |= WME_F_AGGRMODE; + ieee80211_wme_updateparams_locked(ic); + wme->wme_hipri_traffic = 0; + } else + wme->wme_hipri_traffic = + wme->wme_hipri_switch_hysteresis; + } + if (ic->ic_flags & IEEE80211_F_WMEUPDATE) { + (void) ieee80211_add_wme_param(bo->bo_wme, wme); + ic->ic_flags &= ~IEEE80211_F_WMEUPDATE; + } + } + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ + struct ieee80211_tim_ie *tie = + (struct ieee80211_tim_ie *) bo->bo_tim; + if (ic->ic_flags & IEEE80211_F_TIMUPDATE) { + u_int timlen, timoff, i; + /* + * ATIM/DTIM needs updating. If it fits in the + * current space allocated then just copy in the + * new bits. Otherwise we need to move any trailing + * data to make room. Note that we know there is + * contiguous space because ieee80211_beacon_allocate + * insures there is space in the mbuf to write a + * maximal-size virtual bitmap (based on ic_max_aid). + */ + /* + * Calculate the bitmap size and offset, copy any + * trailer out of the way, and then copy in the + * new bitmap and update the information element. + * Note that the tim bitmap must contain at least + * one byte and any offset must be even. + */ + if (ic->ic_ps_pending != 0) { + timoff = 128; /* impossibly large */ + for (i = 0; i < ic->ic_tim_len; i++) + if (ic->ic_tim_bitmap[i]) { + timoff = i &~ 1; + break; + } + KASSERT(timoff != 128, ("tim bitmap empty!")); + for (i = ic->ic_tim_len-1; i >= timoff; i--) + if (ic->ic_tim_bitmap[i]) + break; + timlen = 1 + (i - timoff); + } else { + timoff = 0; + timlen = 1; + } + if (timlen != bo->bo_tim_len) { + /* copy up/down trailer */ + ovbcopy(bo->bo_trailer, tie->tim_bitmap+timlen, + bo->bo_trailer_len); + bo->bo_trailer = tie->tim_bitmap+timlen; + bo->bo_wme = bo->bo_trailer; + bo->bo_tim_len = timlen; + + /* update information element */ + tie->tim_len = 3 + timlen; + tie->tim_bitctl = timoff; + len_changed = 1; + } + memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff, + bo->bo_tim_len); + + ic->ic_flags &= ~IEEE80211_F_TIMUPDATE; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "%s: TIM updated, pending %u, off %u, len %u\n", + __func__, ic->ic_ps_pending, timoff, timlen); + } + /* count down DTIM period */ + if (tie->tim_count == 0) + tie->tim_count = tie->tim_period - 1; + else + tie->tim_count--; + /* update state for buffered multicast frames on DTIM */ + if (mcast && (tie->tim_count == 1 || tie->tim_period == 1)) + tie->tim_bitctl |= 1; + else + tie->tim_bitctl &= ~1; + } + IEEE80211_BEACON_UNLOCK(ic); + + return len_changed; +} + +/* + * Save an outbound packet for a node in power-save sleep state. + * The new packet is placed on the node's saved queue, and the TIM + * is changed, if necessary. + */ +void +ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m) +{ + int qlen, age; + + IEEE80211_NODE_SAVEQ_LOCK(ni); + 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); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumppkts(ic)) + ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1); +#endif + m_freem(m); + return; + } + /* + * Tag the frame with it's expiry time and insert + * it in the queue. The aging interval is 4 times + * the listen interval specified by the station. + * Frames that sit around too long are reclaimed + * using this information. + */ + /* XXX handle overflow? */ + age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024; /* TU -> secs */ + _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); + IEEE80211_NODE_SAVEQ_UNLOCK(ni); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + "[%s] save frame, %u now queued\n", + ether_sprintf(ni->ni_macaddr), qlen); + + if (qlen == 1) + ic->ic_set_tim(ic, ni, 1); +} diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 1f4301a..1764b52 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,35 +40,20 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include +#include +#include + #include -#include #include -#include -#include -#include +#include /* XXX for ether_sprintf */ #include -#include - -#ifdef INET -#include -#include -#endif +/* XXX tunables */ +#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ +#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) @@ -78,6 +63,12 @@ const char *ieee80211_mgt_subtype_name[] = { "beacon", "atim", "disassoc", "auth", "deauth", "reserved#13", "reserved#14", "reserved#15" }; +const char *ieee80211_ctl_subtype_name[] = { + "reserved#0", "reserved#1", "reserved#2", "reserved#3", + "reserved#3", "reserved#5", "reserved#6", "reserved#7", + "reserved#8", "reserved#9", "ps_poll", "rts", + "cts", "ack", "cf_end", "cf_end_ack" +}; const char *ieee80211_state_name[IEEE80211_S_MAX] = { "INIT", /* IEEE80211_S_INIT */ "SCAN", /* IEEE80211_S_SCAN */ @@ -85,15 +76,23 @@ const char *ieee80211_state_name[IEEE80211_S_MAX] = { "ASSOC", /* IEEE80211_S_ASSOC */ "RUN" /* IEEE80211_S_RUN */ }; +const char *ieee80211_wme_acnames[] = { + "WME_AC_BE", + "WME_AC_BK", + "WME_AC_VI", + "WME_AC_VO", + "WME_UPSD", +}; static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); void -ieee80211_proto_attach(struct ifnet *ifp) +ieee80211_proto_attach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + struct ifnet *ifp = ic->ic_ifp; - ifp->if_hdrlen = sizeof(struct ieee80211_frame); + /* XXX room for crypto */ + ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); #ifdef notdef ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; @@ -103,6 +102,10 @@ ieee80211_proto_attach(struct ifnet *ifp) ic->ic_fragthreshold = 2346; /* XXX not used yet */ ic->ic_fixed_rate = -1; /* no fixed rate */ ic->ic_protmode = IEEE80211_PROT_CTSONLY; + ic->ic_roaming = IEEE80211_ROAMING_AUTO; + + 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); @@ -115,19 +118,125 @@ ieee80211_proto_attach(struct ifnet *ifp) } void -ieee80211_proto_detach(struct ifnet *ifp) +ieee80211_proto_detach(struct ieee80211com *ic) { - struct ieee80211com *ic = (void *)ifp; + + /* + * This should not be needed as we detach when reseting + * the state but be conservative here since the + * authenticator may do things like spawn kernel threads. + */ + if (ic->ic_auth->ia_detach) + ic->ic_auth->ia_detach(ic); IF_DRAIN(&ic->ic_mgtq); mtx_destroy(&ic->ic_mgtq.ifq_mtx); + + /* + * Detach any ACL'ator. + */ + if (ic->ic_acl != NULL) + ic->ic_acl->iac_detach(ic); } +/* + * Simple-minded authenticator module support. + */ + +#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) +/* XXX well-known names */ +static const char *auth_modnames[IEEE80211_AUTH_MAX] = { + "wlan_internal", /* IEEE80211_AUTH_NONE */ + "wlan_internal", /* IEEE80211_AUTH_OPEN */ + "wlan_internal", /* IEEE80211_AUTH_SHARED */ + "wlan_xauth", /* IEEE80211_AUTH_8021X */ + "wlan_internal", /* IEEE80211_AUTH_AUTO */ + "wlan_xauth", /* IEEE80211_AUTH_WPA */ +}; +static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; + +static const struct ieee80211_authenticator auth_internal = { + .ia_name = "wlan_internal", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +/* + * Setup internal authenticators once; they are never unregistered. + */ +static void +ieee80211_auth_setup(void) +{ + ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); +} +SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); + +const struct ieee80211_authenticator * +ieee80211_authenticator_get(int auth) +{ + if (auth >= IEEE80211_AUTH_MAX) + return NULL; + if (authenticators[auth] == NULL) + ieee80211_load_module(auth_modnames[auth]); + return authenticators[auth]; +} + +void +ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *auth) +{ + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = auth; +} + +void +ieee80211_authenticator_unregister(int type) +{ + + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = NULL; +} + +/* + * Very simple-minded ACL module support. + */ +/* XXX just one for now */ +static const struct ieee80211_aclator *acl = NULL; + void -ieee80211_print_essid(u_int8_t *essid, int len) +ieee80211_aclator_register(const struct ieee80211_aclator *iac) { + printf("wlan: %s acl policy registered\n", iac->iac_name); + acl = iac; +} + +void +ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) +{ + if (acl == iac) + acl = NULL; + printf("wlan: %s acl policy unregistered\n", iac->iac_name); +} + +const struct ieee80211_aclator * +ieee80211_aclator_get(const char *name) +{ + if (acl == NULL) + ieee80211_load_module("wlan_acl"); + return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; +} + +void +ieee80211_print_essid(const u_int8_t *essid, int len) +{ + const u_int8_t *p; int i; - u_int8_t *p; if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; @@ -149,12 +258,12 @@ ieee80211_print_essid(u_int8_t *essid, int len) } void -ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) +ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) { - struct ieee80211_frame *wh; + const struct ieee80211_frame *wh; int i; - wh = (struct ieee80211_frame *)buf; + wh = (const struct ieee80211_frame *)buf; switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: printf("NODS %s", ether_sprintf(wh->i_addr2)); @@ -172,7 +281,7 @@ ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) printf("(%s)", ether_sprintf(wh->i_addr2)); break; case IEEE80211_FC1_DIR_DSTODS: - printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); + printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s", ether_sprintf(wh->i_addr2)); printf("->%s)", ether_sprintf(wh->i_addr1)); @@ -191,8 +300,13 @@ ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } - if (wh->i_fc[1] & IEEE80211_FC1_WEP) - printf(" WEP"); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + int i; + printf(" WEP [IV"); + for (i = 0; i < IEEE80211_WEP_IVLEN; i++) + printf(" %.02x", buf[sizeof(*wh)+i]); + printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); + } if (rate >= 0) printf(" %dM", rate / 2); if (rssi >= 0) @@ -213,12 +327,18 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags { #define RV(v) ((v) & IEEE80211_RATE_VAL) int i, j, ignore, error; - int okrate, badrate; + int okrate, badrate, fixedrate; struct ieee80211_rateset *srs, *nrs; u_int8_t r; + /* + * If the fixed rate check was requested but no + * fixed has been defined then just remove it. + */ + if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate < 0) + flags &= ~IEEE80211_F_DOFRATE; error = 0; - okrate = badrate = 0; + okrate = badrate = fixedrate = 0; srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; nrs = &ni->ni_rates; for (i = 0; i < nrs->rs_nrates; ) { @@ -239,17 +359,10 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags badrate = r; if (flags & IEEE80211_F_DOFRATE) { /* - * Apply fixed rate constraint. Note that we do - * not apply the constraint to basic rates as - * otherwise we may not be able to associate if - * the rate set we submit to the AP is invalid - * (e.g. fix rate at 36Mb/s which is not a basic - * rate for 11a operation). + * Check any fixed rate is included. */ - if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 && - ic->ic_fixed_rate >= 0 && - r != RV(srs->rs_rates[ic->ic_fixed_rate])) - ignore++; + if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) + fixedrate = r; } if (flags & IEEE80211_F_DONEGO) { /* @@ -299,23 +412,423 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags okrate = nrs->rs_rates[i]; i++; } - if (okrate == 0 || error != 0) + if (okrate == 0 || error != 0 || + ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); #undef RV } +/* + * Reset 11g-related state. + */ +void +ieee80211_reset_erp(struct ieee80211com *ic) +{ + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ic->ic_nonerpsta = 0; + ic->ic_longslotsta = 0; + /* + * Short slot time is enabled only when operating in 11g + * and not in an IBSS. We must also honor whether or not + * the driver is capable of doing it. + */ + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ic->ic_curmode == IEEE80211_MODE_11G && + ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_caps & IEEE80211_C_SHSLOT))); + /* + * Set short preamble and ERP barker-preamble flags. + */ + if (ic->ic_curmode == IEEE80211_MODE_11A || + (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } +} + +/* + * Set the short slot time state and notify the driver. + */ +void +ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) +{ + if (onoff) + ic->ic_flags |= IEEE80211_F_SHSLOT; + else + ic->ic_flags &= ~IEEE80211_F_SHSLOT; + /* notify driver */ + if (ic->ic_updateslot != NULL) + ic->ic_updateslot(ic->ic_ifp); +} + +/* + * Check if the specified rate set supports ERP. + * NB: the rate set is assumed to be sorted. + */ +int +ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; + int i, j; + + if (rs->rs_nrates < N(rates)) + return 0; + for (i = 0; i < N(rates); i++) { + for (j = 0; j < rs->rs_nrates; j++) { + int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; + if (rates[i] == r) + goto next; + if (r > rates[i]) + return 0; + } + return 0; + next: + ; + } + return 1; +#undef N +} + +/* + * Mark the basic rates for the 11g rate table based on the + * operating mode. For real 11g we mark all the 11b rates + * and 6, 12, and 24 OFDM. For 11b compatibility we mark only + * 11b rates. There's also a pseudo 11a-mode used to mark only + * the basic OFDM rates. + */ +void +ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) +{ + static const struct ieee80211_rateset basic[] = { + { 0 }, /* IEEE80211_MODE_AUTO */ + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ + { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ + { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ + { 0 }, /* IEEE80211_MODE_FH */ + /* IEEE80211_MODE_PUREG (not yet) */ + { 7, { 2, 4, 11, 22, 12, 24, 48 } }, + }; + int i, j; + + for (i = 0; i < rs->rs_nrates; i++) { + rs->rs_rates[i] &= IEEE80211_RATE_VAL; + for (j = 0; j < basic[mode].rs_nrates; j++) + if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { + rs->rs_rates[i] |= IEEE80211_RATE_BASIC; + break; + } + } +} + +/* + * WME protocol support. The following parameters come from the spec. + */ +typedef struct phyParamType { + u_int8_t aifsn; + u_int8_t logcwmin; + u_int8_t logcwmax; + u_int16_t txopLimit; + u_int8_t acm; +} paramType; + +static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { + { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ + { 3, 4, 6 }, /* IEEE80211_MODE_11A */ + { 3, 5, 7 }, /* IEEE80211_MODE_11B */ + { 3, 4, 6 }, /* IEEE80211_MODE_11G */ + { 3, 5, 7 }, /* IEEE80211_MODE_FH */ + { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { + { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ + { 7, 4, 10 }, /* IEEE80211_MODE_11A */ + { 7, 5, 10 }, /* IEEE80211_MODE_11B */ + { 7, 4, 10 }, /* IEEE80211_MODE_11G */ + { 7, 5, 10 }, /* IEEE80211_MODE_FH */ + { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ + { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { + { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ + { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ + { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ + { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ + { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ + { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { + { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ + { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ + { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ + { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ + { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ +}; + +static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { + { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ + { 3, 4, 10 }, /* IEEE80211_MODE_11A */ + { 3, 5, 10 }, /* IEEE80211_MODE_11B */ + { 3, 4, 10 }, /* IEEE80211_MODE_11G */ + { 3, 5, 10 }, /* IEEE80211_MODE_FH */ + { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { + { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ + { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ + { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ + { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ + { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ + { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ + { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ +}; +static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { + { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ + { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ + { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ + { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ + { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ +}; + +void +ieee80211_wme_initparams(struct ieee80211com *ic) +{ + struct ieee80211_wme_state *wme = &ic->ic_wme; + const paramType *pPhyParam, *pBssPhyParam; + struct wmeParams *wmep; + int i; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return; + + for (i = 0; i < WME_NUM_AC; i++) { + switch (i) { + case WME_AC_BK: + pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; + pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; + break; + case WME_AC_VI: + pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; + pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; + break; + case WME_AC_VO: + pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; + pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; + break; + case WME_AC_BE: + default: + pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; + pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; + break; + } + + wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + wmep->wmep_acm = pPhyParam->acm; + wmep->wmep_aifsn = pPhyParam->aifsn; + wmep->wmep_logcwmin = pPhyParam->logcwmin; + wmep->wmep_logcwmax = pPhyParam->logcwmax; + wmep->wmep_txopLimit = pPhyParam->txopLimit; + } else { + wmep->wmep_acm = pBssPhyParam->acm; + wmep->wmep_aifsn = pBssPhyParam->aifsn; + wmep->wmep_logcwmin = pBssPhyParam->logcwmin; + wmep->wmep_logcwmax = pBssPhyParam->logcwmax; + wmep->wmep_txopLimit = pBssPhyParam->txopLimit; + + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " + "log2(cwmax) %u txpoLimit %u]\n", __func__ + , ieee80211_wme_acnames[i] + , wmep->wmep_acm + , wmep->wmep_aifsn + , wmep->wmep_logcwmin + , wmep->wmep_logcwmax + , wmep->wmep_txopLimit + ); + + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; + wmep->wmep_acm = pBssPhyParam->acm; + wmep->wmep_aifsn = pBssPhyParam->aifsn; + wmep->wmep_logcwmin = pBssPhyParam->logcwmin; + wmep->wmep_logcwmax = pBssPhyParam->logcwmax; + wmep->wmep_txopLimit = pBssPhyParam->txopLimit; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " + "log2(cwmax) %u txpoLimit %u]\n", __func__ + , ieee80211_wme_acnames[i] + , wmep->wmep_acm + , wmep->wmep_aifsn + , wmep->wmep_logcwmin + , wmep->wmep_logcwmax + , wmep->wmep_txopLimit + ); + } + /* NB: check ic_bss to avoid NULL deref on initial attach */ + if (ic->ic_bss != NULL) { + /* + * Calculate agressive mode switching threshold based + * on beacon interval. This doesn't need locking since + * we're only called before entering the RUN state at + * which point we start sending beacon frames. + */ + wme->wme_hipri_switch_thresh = + (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; + ieee80211_wme_updateparams(ic); + } +} + +/* + * Update WME parameters for ourself and the BSS. + */ +void +ieee80211_wme_updateparams_locked(struct ieee80211com *ic) +{ + static const paramType phyParam[IEEE80211_MODE_MAX] = { + { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ + { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ + { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ + { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ + { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ + { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ + { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ + }; + struct ieee80211_wme_state *wme = &ic->ic_wme; + const struct wmeParams *wmep; + struct wmeParams *chanp, *bssp; + int i; + + /* set up the channel access parameters for the physical device */ + for (i = 0; i < WME_NUM_AC; i++) { + chanp = &wme->wme_chanParams.cap_wmeParams[i]; + wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; + chanp->wmep_aifsn = wmep->wmep_aifsn; + chanp->wmep_logcwmin = wmep->wmep_logcwmin; + chanp->wmep_logcwmax = wmep->wmep_logcwmax; + chanp->wmep_txopLimit = wmep->wmep_txopLimit; + + chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; + chanp->wmep_aifsn = wmep->wmep_aifsn; + chanp->wmep_logcwmin = wmep->wmep_logcwmin; + chanp->wmep_logcwmax = wmep->wmep_logcwmax; + chanp->wmep_txopLimit = wmep->wmep_txopLimit; + } + + /* + * This implements agressive mode as found in certain + * vendors' AP's. When there is significant high + * priority (VI/VO) traffic in the BSS throttle back BE + * traffic by using conservative parameters. Otherwise + * BE uses agressive params to optimize performance of + * legacy/non-QoS traffic. + */ + if ((ic->ic_opmode == IEEE80211_M_HOSTAP && + (wme->wme_flags & WME_F_AGGRMODE) == 0) || + (ic->ic_opmode != IEEE80211_M_HOSTAP && + (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || + (ic->ic_flags & IEEE80211_F_WME) == 0) { + chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; + bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; + + chanp->wmep_aifsn = bssp->wmep_aifsn = + phyParam[ic->ic_curmode].aifsn; + chanp->wmep_logcwmin = bssp->wmep_logcwmin = + phyParam[ic->ic_curmode].logcwmin; + chanp->wmep_logcwmax = bssp->wmep_logcwmax = + phyParam[ic->ic_curmode].logcwmax; + chanp->wmep_txopLimit = bssp->wmep_txopLimit = + (ic->ic_caps & IEEE80211_C_BURST) ? + phyParam[ic->ic_curmode].txopLimit : 0; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s [acm %u aifsn %u log2(cwmin) %u " + "log2(cwmax) %u txpoLimit %u]\n", __func__ + , ieee80211_wme_acnames[WME_AC_BE] + , chanp->wmep_acm + , chanp->wmep_aifsn + , chanp->wmep_logcwmin + , chanp->wmep_logcwmax + , chanp->wmep_txopLimit + ); + } + + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) { + static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { + 3, /* IEEE80211_MODE_AUTO */ + 3, /* IEEE80211_MODE_11A */ + 4, /* IEEE80211_MODE_11B */ + 3, /* IEEE80211_MODE_11G */ + 4, /* IEEE80211_MODE_FH */ + 3, /* IEEE80211_MODE_TURBO_A */ + 3, /* IEEE80211_MODE_TURBO_G */ + }; + chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; + bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; + + chanp->wmep_logcwmin = bssp->wmep_logcwmin = + logCwMin[ic->ic_curmode]; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: %s log2(cwmin) %u\n", __func__ + , ieee80211_wme_acnames[WME_AC_BE] + , chanp->wmep_logcwmin + ); + } + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ + /* + * Arrange for a beacon update and bump the parameter + * set number so associated stations load the new values. + */ + wme->wme_bssChanParams.cap_info = + (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; + ic->ic_flags |= IEEE80211_F_WMEUPDATE; + } + + wme->wme_update(ic); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + "%s: WME params updated, cap_info 0x%x\n", __func__, + ic->ic_opmode == IEEE80211_M_STA ? + wme->wme_wmeChanParams.cap_info : + wme->wme_bssChanParams.cap_info); +} + +void +ieee80211_wme_updateparams(struct ieee80211com *ic) +{ + + if (ic->ic_caps & IEEE80211_C_WME) { + IEEE80211_BEACON_LOCK(ic); + ieee80211_wme_updateparams_locked(ic); + IEEE80211_BEACON_UNLOCK(ic); + } +} + static int -ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) +ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { - struct ifnet *ifp = &ic->ic_if; + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_node_table *nt; struct ieee80211_node *ni; enum ieee80211_state ostate; ostate = ic->ic_state; - IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__, - ieee80211_state_name[ostate], ieee80211_state_name[nstate])); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); ic->ic_state = nstate; /* state transition */ ni = ic->ic_bss; /* NB: no reference held */ switch (nstate) { @@ -329,22 +842,28 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_sta_leave(ic, ni); break; case IEEE80211_M_HOSTAP: - IEEE80211_NODE_LOCK(ic); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + nt = ic->ic_sta; + if (nt == NULL) { /* XXX cannot happen */ + if_printf(ifp, "no sta table (run)\n"); + break; + } + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { if (ni->ni_associd == 0) continue; IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); } - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_NODE_UNLOCK(nt); break; default: break; } - /* FALLTHRU */ + goto reset; case IEEE80211_S_ASSOC: switch (ic->ic_opmode) { case IEEE80211_M_STA: @@ -353,42 +872,41 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt IEEE80211_REASON_AUTH_LEAVE); break; case IEEE80211_M_HOSTAP: - IEEE80211_NODE_LOCK(ic); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + nt = ic->ic_sta; + if (nt == NULL) { /* XXX cannot happen */ + if_printf(ifp, "no sta table (assoc)\n"); + break; + } + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_LEAVE); } - IEEE80211_NODE_UNLOCK(ic); + IEEE80211_NODE_UNLOCK(nt); break; default: break; } - /* FALLTHRU */ + goto reset; case IEEE80211_S_AUTH: case IEEE80211_S_SCAN: + reset: ic->ic_mgt_timer = 0; IF_DRAIN(&ic->ic_mgtq); - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; - } - ieee80211_free_allnodes(ic); + ieee80211_reset_bss(ic); + ieee80211_crypto_delglobalkeys(ic); break; } + if (ic->ic_auth->ia_detach != NULL) + ic->ic_auth->ia_detach(ic); break; case IEEE80211_S_SCAN: - ic->ic_flags &= ~IEEE80211_F_SIBSS; - /* initialize bss for probe request */ - IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr); - IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr); - ni->ni_rates = ic->ic_sup_rates[ - ieee80211_chan2mode(ic, ni->ni_chan)]; - ni->ni_associd = 0; - ni->ni_rstamp = 0; switch (ostate) { case IEEE80211_S_INIT: - if (ic->ic_opmode == IEEE80211_M_HOSTAP && + if ((ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO) && ic->ic_des_chan != IEEE80211_CHAN_ANYC) { /* * AP operation and we already have a channel; @@ -396,51 +914,53 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt */ ieee80211_create_ibss(ic, ic->ic_des_chan); } else { - ieee80211_begin_scan(ifp); + ieee80211_begin_scan(ic, arg); } break; case IEEE80211_S_SCAN: - /* scan next */ - if (ic->ic_flags & IEEE80211_F_ASCAN) { + /* + * Scan next. If doing an active scan and the + * channel is not marked passive-only then send + * a probe request. Otherwise just listen for + * beacons on the channel. + */ + if ((ic->ic_flags & IEEE80211_F_ASCAN) && + (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); } break; case IEEE80211_S_RUN: /* beacon miss */ - if (ifp->if_flags & IFF_DEBUG) { - /* XXX bssid clobbered above */ - if_printf(ifp, "no recent beacons from %s;" - " rescanning\n", - ether_sprintf(ic->ic_bss->ni_bssid)); - } - ieee80211_free_allnodes(ic); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, + "no recent beacons from %s; rescanning\n", + ether_sprintf(ic->ic_bss->ni_bssid)); + ieee80211_sta_leave(ic, ni); + ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: /* timeout restart scan */ - ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + ni = ieee80211_find_node(&ic->ic_scan, + ic->ic_bss->ni_macaddr); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } - ieee80211_begin_scan(ifp); + ieee80211_begin_scan(ic, arg); break; } break; case IEEE80211_S_AUTH: switch (ostate) { case IEEE80211_S_INIT: - IEEE80211_DPRINTF(("%s: invalid transition\n", - __func__)); - break; case IEEE80211_S_SCAN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: - switch (mgt) { + switch (arg) { case IEEE80211_FC0_SUBTYPE_AUTH: /* ??? */ IEEE80211_SEND_MGMT(ic, ni, @@ -452,7 +972,7 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt } break; case IEEE80211_S_RUN: - switch (mgt) { + switch (arg) { case IEEE80211_FC0_SUBTYPE_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); @@ -462,6 +982,7 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt /* try to reauth */ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); + ieee80211_sta_leave(ic, ni); break; } break; @@ -472,8 +993,8 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt case IEEE80211_S_INIT: case IEEE80211_S_SCAN: case IEEE80211_S_ASSOC: - IEEE80211_DPRINTF(("%s: invalid transition\n", - __func__)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: invalid transition\n", __func__); break; case IEEE80211_S_AUTH: IEEE80211_SEND_MGMT(ic, ni, @@ -482,28 +1003,35 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt case IEEE80211_S_RUN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + ieee80211_sta_leave(ic, ni); break; } break; case IEEE80211_S_RUN: + if (ic->ic_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } switch (ostate) { case IEEE80211_S_INIT: + if (ic->ic_opmode == IEEE80211_M_MONITOR) + break; + /* fall thru... */ case IEEE80211_S_AUTH: case IEEE80211_S_RUN: - IEEE80211_DPRINTF(("%s: invalid transition\n", - __func__)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + "%s: invalid transition\n", __func__); break; case IEEE80211_S_SCAN: /* adhoc/hostap mode */ case IEEE80211_S_ASSOC: /* infra mode */ KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, ("%s: bogus xmit rate %u setup\n", __func__, ni->ni_txrate)); - if (ifp->if_flags & IFF_DEBUG) { - if_printf(ifp, " "); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(ic)) { if (ic->ic_opmode == IEEE80211_M_STA) - printf("associated "); + if_printf(ifp, "associated "); else - printf("synchronized "); + if_printf(ifp, "synchronized "); printf("with %s ssid ", ether_sprintf(ni->ni_bssid)); ieee80211_print_essid(ic->ic_bss->ni_essid, @@ -512,10 +1040,39 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt ieee80211_chan2ieee(ic, ni->ni_chan), IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); } +#endif ic->ic_mgt_timer = 0; - if_start(ifp); + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_notify_node_join(ic, ni, + arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + if_start(ifp); /* XXX not authorized yet */ break; } + /* + * Start/stop the authenticator when operating as an + * AP. We delay until here to allow configuration to + * happen out of order. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ + ic->ic_auth->ia_attach != NULL) { + /* XXX check failure */ + ic->ic_auth->ia_attach(ic); + } else if (ic->ic_auth->ia_detach != NULL) { + ic->ic_auth->ia_detach(ic); + } + /* + * When 802.1x is not in use mark the port authorized + * at this point so traffic can flow. + */ + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ic, ni); + /* + * Enable inactivity processing. + * XXX + */ + ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; + if (ic->ic_sta != NULL) + ic->ic_sta->nt_inact_timer = IEEE80211_INACT_WAIT; break; } return 0; diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 8c85003..f793f8e 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -52,27 +52,193 @@ enum ieee80211_state { extern const char *ieee80211_mgt_subtype_name[]; -extern void ieee80211_proto_attach(struct ifnet *); -extern void ieee80211_proto_detach(struct ifnet *); +extern void ieee80211_proto_attach(struct ieee80211com *); +extern void ieee80211_proto_detach(struct ieee80211com *); struct ieee80211_node; -extern void ieee80211_input(struct ifnet *, struct mbuf *, +extern void ieee80211_input(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, u_int32_t); extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, u_int32_t); +extern int ieee80211_send_nulldata(struct ieee80211com *, + struct ieee80211_node *); extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, int, int); -extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *, - struct ieee80211_node **); -extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *); -extern u_int8_t *ieee80211_add_rates(u_int8_t *frm, - const struct ieee80211_rateset *); +extern int ieee80211_classify(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +extern struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); +extern void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, + struct mbuf *); + +extern void ieee80211_reset_erp(struct ieee80211com *); +extern void ieee80211_set_shortslottime(struct ieee80211com *, int onoff); +extern int ieee80211_iserp_rateset(struct ieee80211com *, + struct ieee80211_rateset *); +extern void ieee80211_set11gbasicrates(struct ieee80211_rateset *, + enum ieee80211_phymode); + +/* + * Return the size of the 802.11 header for a management or data frame. + */ +static inline int +ieee80211_hdrsize(const void *data) +{ + const struct ieee80211_frame *wh = data; + int size = sizeof(struct ieee80211_frame); + + /* NB: we don't handle control frames */ + KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL, + ("%s: control frame", __func__)); + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + size += IEEE80211_ADDR_LEN; + if (IEEE80211_QOS_HAS_SEQ(wh)) + size += sizeof(u_int16_t); + return size; +} + +/* + * Return the size of the 802.11 header; handles any type of frame. + */ +static inline int +ieee80211_anyhdrsize(const void *data) +{ + const struct ieee80211_frame *wh = data; + + if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { + switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_CTS: + case IEEE80211_FC0_SUBTYPE_ACK: + return sizeof(struct ieee80211_frame_ack); + } + return sizeof(struct ieee80211_frame_min); + } else + return ieee80211_hdrsize(data); +} + +/* + * Template for an in-kernel authenticator. Authenticators + * register with the protocol code and are typically loaded + * as separate modules as needed. + */ +struct ieee80211_authenticator { + const char *ia_name; /* printable name */ + int (*ia_attach)(struct ieee80211com *); + void (*ia_detach)(struct ieee80211com *); + void (*ia_node_join)(struct ieee80211com *, + struct ieee80211_node *); + void (*ia_node_leave)(struct ieee80211com *, + struct ieee80211_node *); +}; +extern void ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *); +extern void ieee80211_authenticator_unregister(int type); +extern const struct ieee80211_authenticator * + ieee80211_authenticator_get(int auth); + +/* + * Template for an MAC ACL policy module. Such modules + * register with the protocol code and are passed the sender's + * address of each received frame for validation. + */ +struct ieee80211_aclator { + const char *iac_name; /* printable name */ + int (*iac_attach)(struct ieee80211com *); + void (*iac_detach)(struct ieee80211com *); + int (*iac_check)(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_add)(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_remove)(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_flush)(struct ieee80211com *); + int (*iac_setpolicy)(struct ieee80211com *, int); + int (*iac_getpolicy)(struct ieee80211com *); +}; +extern void ieee80211_aclator_register(const struct ieee80211_aclator *); +extern void ieee80211_aclator_unregister(const struct ieee80211_aclator *); +extern const struct ieee80211_aclator *ieee80211_aclator_get(const char *name); + +/* flags for ieee80211_fix_rate() */ +#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ +#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ +#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ +#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ +extern int ieee80211_fix_rate(struct ieee80211com *, + struct ieee80211_node *, int); + +/* + * WME/WMM support. + */ +struct wmeParams { + u_int8_t wmep_acm; + u_int8_t wmep_aifsn; + u_int8_t wmep_logcwmin; /* log2(cwmin) */ + u_int8_t wmep_logcwmax; /* log2(cwmax) */ + u_int8_t wmep_txopLimit; + u_int8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */ +}; + +struct chanAccParams { + u_int8_t cap_info; /* version of the current set */ + struct wmeParams cap_wmeParams[WME_NUM_AC]; +}; + +struct ieee80211_wme_state { + u_int wme_flags; +#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME agressive mode */ + u_int wme_hipri_traffic; /* VI/VO frames in beacon interval */ + u_int wme_hipri_switch_thresh;/* agressive mode switch thresh */ + u_int wme_hipri_switch_hysteresis;/* agressive mode switch hysteresis */ + + struct wmeParams wme_params[4]; /* from assoc resp for each AC*/ + struct chanAccParams wme_wmeChanParams; /* WME params applied to self */ + struct chanAccParams wme_wmeBssChanParams;/* WME params bcast to stations */ + struct chanAccParams wme_chanParams; /* params applied to self */ + struct chanAccParams wme_bssChanParams; /* params bcast to stations */ + + int (*wme_update)(struct ieee80211com *); +}; + +extern void ieee80211_wme_initparams(struct ieee80211com *); +extern void ieee80211_wme_updateparams(struct ieee80211com *); +extern void ieee80211_wme_updateparams_locked(struct ieee80211com *); + #define ieee80211_new_state(_ic, _nstate, _arg) \ (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) -extern u_int8_t *ieee80211_add_xrates(u_int8_t *frm, - const struct ieee80211_rateset *); -extern void ieee80211_print_essid(u_int8_t *, int); -extern void ieee80211_dump_pkt(u_int8_t *, int, int, int); +extern void ieee80211_print_essid(const u_int8_t *, int); +extern void ieee80211_dump_pkt(const u_int8_t *, int, int, int); extern const char *ieee80211_state_name[IEEE80211_S_MAX]; +extern const char *ieee80211_wme_acnames[]; + +/* + * Beacon frames constructed by ieee80211_beacon_alloc + * have the following structure filled in so drivers + * can update the frame later w/ minimal overhead. + */ +struct ieee80211_beacon_offsets { + u_int16_t *bo_caps; /* capabilities */ + u_int8_t *bo_tim; /* start of atim/dtim */ + u_int8_t *bo_wme; /* start of WME parameters */ + u_int8_t *bo_trailer; /* start of fixed-size trailer */ + u_int16_t bo_tim_len; /* atim/dtim length in bytes */ + u_int16_t bo_trailer_len; /* trailer length in bytes */ +}; +extern struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_beacon_offsets *); +extern int ieee80211_beacon_update(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_beacon_offsets *, + struct mbuf *, int broadcast); + +/* + * Notification methods called from the 802.11 state machine. + * Note that while these are defined here, their implementation + * is OS-specific. + */ +extern void ieee80211_notify_node_join(struct ieee80211com *, + struct ieee80211_node *, int newassoc); +extern void ieee80211_notify_node_leave(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_notify_scan_done(struct ieee80211com *); #endif /* _NET80211_IEEE80211_PROTO_H_ */ diff --git a/sys/net80211/ieee80211_radiotap.h b/sys/net80211/ieee80211_radiotap.h index 68e9367..e36a911 100644 --- a/sys/net80211/ieee80211_radiotap.h +++ b/sys/net80211/ieee80211_radiotap.h @@ -46,11 +46,11 @@ * function of...") that I cannot set false expectations for lawyerly * readers. */ -#ifdef _KERNEL +#if defined(__KERNEL__) || defined(_KERNEL) #ifndef DLT_IEEE802_11_RADIO #define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ #endif -#endif /* _KERNEL */ +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ /* The radio capture header precedes the 802.11 header. */ struct ieee80211_radiotap_header { diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index c5ef197..ad98613 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, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,124 +37,54 @@ /* * Definitions for IEEE 802.11 drivers. */ +#define IEEE80211_DEBUG +#undef IEEE80211_DEBUG_REFCNT /* node refcnt stuff */ +/* NB: portability glue must go first */ +#ifdef __NetBSD__ +#include +#elif __FreeBSD__ +#include +#elif __linux__ +#include +#else +#error "No support for your operating system!" +#endif + +#include #include #include #include /* for ieee80211_stats */ #include #include -#define IEEE80211_CHAN_MAX 255 -#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ -#define IEEE80211_CHAN_ANYC \ - ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) - -#define IEEE80211_TXPOWER_MAX 100 /* max power */ -#define IEEE80211_TXPOWER_MIN 0 /* kill radio (if possible) */ - -enum ieee80211_phytype { - IEEE80211_T_DS, /* direct sequence spread spectrum */ - IEEE80211_T_FH, /* frequency hopping */ - IEEE80211_T_OFDM, /* frequency division multiplexing */ - IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ -}; -#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ - -/* XXX not really a mode; there are really multiple PHY's */ -enum ieee80211_phymode { - IEEE80211_MODE_AUTO = 0, /* autoselect */ - IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ - IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ - IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ - IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ - IEEE80211_MODE_TURBO = 5, /* 5GHz, OFDM, 2x clock */ -}; -#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1) +#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */ +#define IEEE80211_TXPOWER_MIN 0 /* kill radio */ -enum ieee80211_opmode { - IEEE80211_M_STA = 1, /* infrastructure station */ - IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ - IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ - IEEE80211_M_HOSTAP = 6, /* Software Access Point */ - IEEE80211_M_MONITOR = 8 /* Monitor mode */ -}; +#define IEEE80211_DTIM_MAX 15 /* max DTIM period */ +#define IEEE80211_DTIM_MIN 1 /* min DTIM period */ +#define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */ -/* - * 802.11g protection mode. - */ -enum ieee80211_protmode { - IEEE80211_PROT_NONE = 0, /* no protection */ - IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ - IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ -}; +#define IEEE80211_BINTVAL_MAX 500 /* max beacon interval (ms) */ +#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval */ +#define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval */ -/* - * Channels are specified by frequency and attributes. - */ -struct ieee80211_channel { - u_int16_t ic_freq; /* setting in Mhz */ - u_int16_t ic_flags; /* see below */ -}; +#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ +#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ -/* bits 0-3 are for private use by drivers */ -/* channel attributes */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ - -/* - * Useful combinations of channel characteristics. - */ -#define IEEE80211_CHAN_FHSS \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) -#define IEEE80211_CHAN_A \ - (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) -#define IEEE80211_CHAN_B \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) -#define IEEE80211_CHAN_PUREG \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) -#define IEEE80211_CHAN_G \ - (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) -#define IEEE80211_CHAN_T \ - (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) - -#define IEEE80211_IS_CHAN_FHSS(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) -#define IEEE80211_IS_CHAN_A(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) -#define IEEE80211_IS_CHAN_B(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) -#define IEEE80211_IS_CHAN_PUREG(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) -#define IEEE80211_IS_CHAN_G(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) -#define IEEE80211_IS_CHAN_T(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) - -#define IEEE80211_IS_CHAN_2GHZ(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) -#define IEEE80211_IS_CHAN_5GHZ(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) -#define IEEE80211_IS_CHAN_OFDM(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) -#define IEEE80211_IS_CHAN_CCK(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) -#define IEEE80211_IS_CHAN_GFSK(_c) \ - (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) - -/* ni_chan encoding for FH phy */ -#define IEEE80211_FH_CHANMOD 80 -#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) -#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) -#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) +struct ieee80211_aclator; +struct sysctl_ctx_list; struct ieee80211com { - struct arpcom ic_ac; + SLIST_ENTRY(ieee80211com) ic_next; + struct ifnet *ic_ifp; /* associated device */ + struct ieee80211_stats ic_stats; /* statistics */ + struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */ + u_int32_t ic_debug; /* debug msg flags */ + int ic_vap; /* virtual AP index */ + ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */ + + int (*ic_reset)(struct ifnet *); void (*ic_recv_mgmt)(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, u_int32_t); @@ -164,12 +94,16 @@ struct ieee80211com { enum ieee80211_state, int); void (*ic_newassoc)(struct ieee80211com *, struct ieee80211_node *, int); + void (*ic_updateslot)(struct ifnet *); + void (*ic_set_tim)(struct ieee80211com *, + struct ieee80211_node *, int); u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; - u_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)]; - u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)]; - u_char ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)]; + u_int8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; + u_int8_t ic_chan_active[IEEE80211_CHAN_BYTES]; + u_int8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; + struct ieee80211_node_table ic_scan; /* scan candidates */ struct ifqueue ic_mgtq; u_int32_t ic_flags; /* state flags */ u_int32_t ic_caps; /* capabilities */ @@ -179,6 +113,16 @@ struct ieee80211com { enum ieee80211_opmode ic_opmode; /* operation mode */ enum ieee80211_state ic_state; /* 802.11 state */ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ + enum ieee80211_roamingmode ic_roaming; /* roaming mode */ + struct ieee80211_node_table *ic_sta; /* stations/neighbors */ + u_int32_t *ic_aid_bitmap; /* association id map */ + u_int16_t ic_max_aid; + u_int16_t ic_sta_assoc; /* stations associated */ + u_int16_t ic_ps_sta; /* stations in power save */ + u_int16_t ic_ps_pending; /* ps sta's w/ pending frames */ + u_int8_t *ic_tim_bitmap; /* power-save stations w/ data*/ + u_int16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */ + u_int16_t ic_dtim_period; /* DTIM period */ struct ifmedia ic_media; /* interface media config */ struct bpf_if *ic_rawbpf; /* packet filter structure */ struct ieee80211_node *ic_bss; /* information for this node */ @@ -186,90 +130,134 @@ struct ieee80211com { int ic_fixed_rate; /* index to ic_sup_rates[] */ u_int16_t ic_rtsthreshold; u_int16_t ic_fragthreshold; - struct mtx ic_nodelock; /* on node table */ - u_int ic_scangen; /* gen# for timeout scan */ - struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *); - void (*ic_node_free)(struct ieee80211com *, - struct ieee80211_node *); - void (*ic_node_copy)(struct ieee80211com *, - struct ieee80211_node *, - const struct ieee80211_node *); - u_int8_t (*ic_node_getrssi)(struct ieee80211com *, - struct ieee80211_node *); - TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */ - LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE]; + struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*); + void (*ic_node_free)(struct ieee80211_node *); + void (*ic_node_cleanup)(struct ieee80211_node *); + u_int8_t (*ic_node_getrssi)(const struct ieee80211_node*); u_int16_t ic_lintval; /* listen interval */ u_int16_t ic_holdover; /* PM hold over duration */ u_int16_t ic_txmin; /* min tx retry count */ u_int16_t ic_txmax; /* max tx retry count */ u_int16_t ic_txlifetime; /* tx lifetime */ - u_int16_t ic_txpower; /* tx power setting (dbM) */ + u_int16_t ic_txpowlimit; /* global tx power limit */ u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ + u_int16_t ic_nonerpsta; /* # non-ERP stations */ + u_int16_t ic_longslotsta; /* # long slot time stations */ int ic_mgt_timer; /* mgmt timeout */ int ic_inact_timer; /* inactivity timer wait */ int ic_des_esslen; u_int8_t ic_des_essid[IEEE80211_NWID_LEN]; struct ieee80211_channel *ic_des_chan; /* desired channel */ u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN]; - struct ieee80211_wepkey ic_nw_keys[IEEE80211_WEP_NKID]; - int ic_wep_txkey; /* default tx key index */ - void *ic_wep_ctx; /* wep crypt context */ - u_int32_t ic_iv; /* initial vector for wep */ - struct ieee80211_stats ic_stats; /* statistics */ + void *ic_opt_ie; /* user-specified IE's */ + u_int16_t ic_opt_ie_len; /* length of ni_opt_ie */ + /* + * Inactivity timer settings for nodes. + */ + int ic_inact_init; /* initial setting */ + int ic_inact_auth; /* auth but not assoc setting */ + int ic_inact_run; /* authorized setting */ + int ic_inact_probe; /* inactive probe time */ + + /* + * WME/WMM state. + */ + struct ieee80211_wme_state ic_wme; + + /* + * Cipher state/configuration. + */ + struct ieee80211_crypto_state ic_crypto; +#define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */ +#define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */ + + /* + * 802.1x glue. When an authenticator attaches it + * fills in this section. We assume that when ic_ec + * is setup that the methods are safe to call. + */ + const struct ieee80211_authenticator *ic_auth; + struct eapolcom *ic_ec; + + /* + * Access control glue. When a control agent attaches + * it fills in this section. We assume that when ic_ac + * is setup that the methods are safe to call. + */ + const struct ieee80211_aclator *ic_acl; + void *ic_as; }; -#define ic_if ic_ac.ac_if -#define ic_softc ic_if.if_softc #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) #define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) /* ic_flags */ -#define IEEE80211_F_ASCAN 0x00000001 /* STATUS: active scan */ -#define IEEE80211_F_SIBSS 0x00000002 /* STATUS: start IBSS */ -#define IEEE80211_F_WEPON 0x00000100 /* CONF: WEP enabled */ -#define IEEE80211_F_IBSSON 0x00000200 /* CONF: IBSS creation enable */ -#define IEEE80211_F_PMGTON 0x00000400 /* CONF: Power mgmt enable */ -#define IEEE80211_F_DESBSSID 0x00000800 /* CONF: des_bssid is set */ -#define IEEE80211_F_SCANAP 0x00001000 /* CONF: Scanning AP */ -#define IEEE80211_F_ROAMING 0x00002000 /* CONF: roaming enabled */ -#define IEEE80211_F_SWRETRY 0x00004000 /* CONF: sw tx retry enabled */ -#define IEEE80211_F_TXPMGT 0x00018000 /* STATUS: tx power */ -#define IEEE80211_F_TXPOW_OFF 0x00000000 /* TX Power: radio disabled */ -#define IEEE80211_F_TXPOW_FIXED 0x00008000 /* TX Power: fixed rate */ -#define IEEE80211_F_TXPOW_AUTO 0x00010000 /* TX Power: undefined */ -#define IEEE80211_F_SHSLOT 0x00020000 /* CONF: short slot time */ -#define IEEE80211_F_SHPREAMBLE 0x00040000 /* CONF: short preamble */ +/* NB: bits 0x6f available */ +/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ +#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ +#define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */ +#define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */ +#define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */ +/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */ +#define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/ +#define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */ +#define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */ +#define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */ +#define IEEE80211_F_ROAMING 0x00004000 /* CONF: roaming enabled (???)*/ +#define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */ +#define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */ +#define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */ +#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ +#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */ #define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ #define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ +#define IEEE80211_F_TIMUPDATE 0x00400000 /* STATUS: update beacon tim */ +#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */ +#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */ +#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */ +#define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */ +#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */ +#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ +#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ +#define IEEE80211_F_WMEUPDATE 0x20000000 /* STATUS: update beacon wme */ /* ic_caps */ #define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ -#define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */ -#define IEEE80211_C_PMGT 0x00000004 /* CAPABILITY: Power mgmt */ -#define IEEE80211_C_HOSTAP 0x00000008 /* CAPABILITY: HOSTAP avail */ -#define IEEE80211_C_AHDEMO 0x00000010 /* CAPABILITY: Old Adhoc Demo */ -#define IEEE80211_C_SWRETRY 0x00000020 /* CAPABILITY: sw tx retry */ -#define IEEE80211_C_TXPMGT 0x00000040 /* CAPABILITY: tx power mgmt */ -#define IEEE80211_C_SHSLOT 0x00000080 /* CAPABILITY: short slottime */ -#define IEEE80211_C_SHPREAMBLE 0x00000100 /* CAPABILITY: short preamble */ -#define IEEE80211_C_MONITOR 0x00000200 /* CAPABILITY: monitor mode */ +#define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */ +#define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */ +#define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */ +#define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */ +#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ +#define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ +#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ +#define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ +#define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ +#define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ +#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ +#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ +#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ +#define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */ +#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ +#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ +#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ +#define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ +#define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ +/* XXX protection/barker? */ -/* flags for ieee80211_fix_rate() */ -#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ -#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ -#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ -#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ +#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */ -void ieee80211_ifattach(struct ifnet *); -void ieee80211_ifdetach(struct ifnet *); -void ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t); +void ieee80211_ifattach(struct ieee80211com *); +void ieee80211_ifdetach(struct ieee80211com *); +void ieee80211_announce(struct ieee80211com *); +void ieee80211_media_init(struct ieee80211com *, + ifm_change_cb_t, ifm_stat_cb_t); +struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); -int ieee80211_ioctl(struct ifnet *, u_long, caddr_t); -int ieee80211_cfgget(struct ifnet *, u_long, caddr_t); -int ieee80211_cfgset(struct ifnet *, u_long, caddr_t); -void ieee80211_watchdog(struct ifnet *); -int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int); +int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t); +int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t); +int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t); +void ieee80211_watchdog(struct ieee80211com *); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); @@ -280,14 +268,65 @@ int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, struct ieee80211_channel *); -#define IEEE80211_DEBUG +/* + * Key update synchronization methods. XXX should not be visible. + */ +static __inline void +ieee80211_key_update_begin(struct ieee80211com *ic) +{ + ic->ic_crypto.cs_key_update_begin(ic); +} +static __inline void +ieee80211_key_update_end(struct ieee80211com *ic) +{ + ic->ic_crypto.cs_key_update_end(ic); +} + +#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ +#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ +#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ +#define IEEE80211_MSG_INPUT 0x08000000 /* input handling */ +#define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */ +#define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */ +#define IEEE80211_MSG_NODE 0x01000000 /* node handling */ +#define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */ +#define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */ +#define IEEE80211_MSG_SCAN 0x00200000 /* scanning */ +#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */ +#define IEEE80211_MSG_STATE 0x00080000 /* state machine */ +#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */ +#define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */ +#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */ +#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */ +#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */ +#define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */ +#define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */ +#define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */ +#define IEEE80211_MSG_WME 0x00000400 /* WME protocol */ + +#define IEEE80211_MSG_ANY 0xffffffff /* anything */ + #ifdef IEEE80211_DEBUG -extern int ieee80211_debug; -#define IEEE80211_DPRINTF(X) if (ieee80211_debug) printf X -#define IEEE80211_DPRINTF2(X) if (ieee80211_debug>1) printf X +#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \ + if (_ic->ic_debug & (_m)) \ + printf(_fmt, __VA_ARGS__); \ +} while (0) +#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) #else -#define IEEE80211_DPRINTF(X) -#define IEEE80211_DPRINTF2(X) +#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) #endif #endif /* _NET80211_IEEE80211_VAR_H_ */ diff --git a/sys/net80211/ieee80211_xauth.c b/sys/net80211/ieee80211_xauth.c new file mode 100644 index 0000000..d97b38d --- /dev/null +++ b/sys/net80211/ieee80211_xauth.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2004 Video54 Technologies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * External authenticator placeholder module. + * + * This support is optional; it is only used when the 802.11 layer's + * authentication mode is set to use 802.1x or WPA is enabled separately + * (for WPA-PSK). If compiled as a module this code does not need + * to be present unless 802.1x/WPA is in use. + * + * The authenticator hooks into the 802.11 layer. At present we use none + * of the available callbacks--the user mode authenticator process works + * entirely from messages about stations joining and leaving. + */ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +/* + * One module handles everything for now. May want + * to split things up for embedded applications. + */ +static const struct ieee80211_authenticator xauth = { + .ia_name = "external", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +/* + * Module glue. + */ +static int +wlan_xauth_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth); + ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth); + return 0; + case MOD_UNLOAD: + ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X); + ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA); + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_xauth_mod = { + "wlan_xauth", + wlan_xauth_modevent, + 0 +}; +DECLARE_MODULE(wlan_xauth, wlan_xauth_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_xauth, 1); +MODULE_DEPEND(wlan_xauth, wlan, 1, 1, 1); -- cgit v1.1