diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2004-06-22 22:53:53 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2004-06-22 22:53:53 +0000 |
commit | cd019f3d8285ec874db667574939d9d7eb232e9d (patch) | |
tree | abd3323d87ab9f4bf995094367a68a36e18db9bd /sys/net80211/ieee80211_proto.c | |
parent | 30265c03b173b2dc6666564bd1e7f0449f1d1e8e (diff) |
Import current NetBSD/FreeBSD 802.11 framework.
Based in part on a diff from Matthew Gream.
Diffstat (limited to 'sys/net80211/ieee80211_proto.c')
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c new file mode 100644 index 00000000000..faaee044985 --- /dev/null +++ b/sys/net80211/ieee80211_proto.c @@ -0,0 +1,566 @@ +/* $OpenBSD: ieee80211_proto.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 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 <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.8 2004/04/02 20:22:25 + sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $"); +#endif + +/* + * IEEE 802.11 protocol support. + */ + +#if defined(__NetBSD__) +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +const char *ieee80211_mgt_subtype_name[] = { + "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", + "probe_req", "probe_resp", "reserved#6", "reserved#7", + "beacon", "atim", "disassoc", "auth", + "deauth", "reserved#13", "reserved#14", "reserved#15" +}; +const char *ieee80211_state_name[IEEE80211_S_MAX] = { + "INIT", /* IEEE80211_S_INIT */ + "SCAN", /* IEEE80211_S_SCAN */ + "AUTH", /* IEEE80211_S_AUTH */ + "ASSOC", /* IEEE80211_S_ASSOC */ + "RUN" /* IEEE80211_S_RUN */ +}; + +static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); + +void +ieee80211_proto_attach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + ifp->if_hdrlen = sizeof(struct ieee80211_frame); + +#ifdef notdef + ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; +#else + ic->ic_rtsthreshold = IEEE80211_RTS_MAX; +#endif + ic->ic_fragthreshold = 2346; /* XXX not used yet */ + ic->ic_fixed_rate = -1; /* no fixed rate */ + ic->ic_protmode = IEEE80211_PROT_CTSONLY; + +#ifdef __FreeBSD__ + mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); +#endif + + /* protocol state change handler */ + ic->ic_newstate = ieee80211_newstate; + + /* initialize management frame handlers */ + ic->ic_recv_mgmt = ieee80211_recv_mgmt; + ic->ic_send_mgmt = ieee80211_send_mgmt; +} + +void +ieee80211_proto_detach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + +#ifdef __FreeBSD__ + IF_DRAIN(&ic->ic_mgtq); + mtx_destroy(&ic->ic_mgtq.ifq_mtx); +#else + IF_PURGE(&ic->ic_mgtq); + IF_PURGE(&ic->ic_pwrsaveq); +#endif +} + +void +ieee80211_print_essid(u_int8_t *essid, int len) +{ + int i; + u_int8_t *p; + + if (len > IEEE80211_NWID_LEN) + len = IEEE80211_NWID_LEN; + /* determine printable or not */ + for (i = 0, p = essid; i < len; i++, p++) { + if (*p < ' ' || *p > 0x7e) + break; + } + if (i == len) { + printf("\""); + for (i = 0, p = essid; i < len; i++, p++) + printf("%c", *p); + printf("\""); + } else { + printf("0x"); + for (i = 0, p = essid; i < len; i++, p++) + printf("%02x", *p); + } +} + +void +ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) +{ + struct ieee80211_frame *wh; + int i; + + wh = (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)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_TODS: + printf("TODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s)", ether_sprintf(wh->i_addr1)); + break; + case IEEE80211_FC1_DIR_FROMDS: + printf("FRDS %s", ether_sprintf(wh->i_addr3)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr2)); + break; + case IEEE80211_FC1_DIR_DSTODS: + printf("DSDS %s", ether_sprintf((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)); + break; + } + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_DATA: + printf(" data"); + break; + case IEEE80211_FC0_TYPE_MGT: + printf(" %s", ieee80211_mgt_subtype_name[ + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT]); + break; + default: + printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); + break; + } + if (wh->i_fc[1] & IEEE80211_FC1_WEP) + printf(" WEP"); + if (rate >= 0) + printf(" %dM", rate / 2); + if (rssi >= 0) + printf(" +%d", rssi); + printf("\n"); + if (len > 0) { + for (i = 0; i < len; i++) { + if ((i & 1) == 0) + printf(" "); + printf("%02x", buf[i]); + } + printf("\n"); + } +} + +int +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; + struct ieee80211_rateset *srs, *nrs; + u_int8_t r; + + error = 0; + okrate = badrate = 0; + srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + nrs = &ni->ni_rates; + for (i = 0; i < nrs->rs_nrates; ) { + ignore = 0; + if (flags & IEEE80211_F_DOSORT) { + /* + * Sort rates. + */ + for (j = i + 1; j < nrs->rs_nrates; j++) { + if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { + r = nrs->rs_rates[i]; + nrs->rs_rates[i] = nrs->rs_rates[j]; + nrs->rs_rates[j] = r; + } + } + } + r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; + 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). + */ + 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 (flags & IEEE80211_F_DONEGO) { + /* + * Check against supported rates. + */ + for (j = 0; j < srs->rs_nrates; j++) { + if (r == RV(srs->rs_rates[j])) { + /* + * Overwrite with the supported rate + * value so any basic rate bit is set. + * This insures that response we send + * to stations have the necessary basic + * rate bit set. + */ + nrs->rs_rates[i] = srs->rs_rates[j]; + break; + } + } + if (j == srs->rs_nrates) { + /* + * A rate in the node's rate set is not + * supported. If this is a basic rate and we + * are operating as an AP then this is an error. + * Otherwise we just discard/ignore the rate. + * Note that this is important for 11b stations + * when they want to associate with an 11g AP. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) + error++; + ignore++; + } + } + if (flags & IEEE80211_F_DODEL) { + /* + * Delete unacceptable rates. + */ + if (ignore) { + nrs->rs_nrates--; + for (j = i; j < nrs->rs_nrates; j++) + nrs->rs_rates[j] = nrs->rs_rates[j + 1]; + nrs->rs_rates[j] = 0; + continue; + } + } + if (!ignore) + okrate = nrs->rs_rates[i]; + i++; + } + if (okrate == 0 || error != 0) + return badrate | IEEE80211_RATE_BASIC; + else + return RV(okrate); +#undef RV +} + +static int +ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + ieee80211_node_critsec_decl(s); + + ostate = ic->ic_state; + IEEE80211_DPRINTF(("%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) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_INIT: + break; + case IEEE80211_S_RUN: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + break; + case IEEE80211_M_HOSTAP: + ieee80211_node_critsec_begin(ic, s); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (ni->ni_associd == 0) + continue; + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + } + ieee80211_node_critsec_end(ic, s); + break; + default: + break; + } + /* FALLTHRU */ + case IEEE80211_S_ASSOC: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + break; + case IEEE80211_M_HOSTAP: + ieee80211_node_critsec_begin(ic, s); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + } + ieee80211_node_critsec_end(ic, s); + break; + default: + break; + } + /* FALLTHRU */ + case IEEE80211_S_AUTH: + case IEEE80211_S_SCAN: + ic->ic_mgt_timer = 0; +#ifdef __FreeBSD__ + IF_DRAIN(&ic->ic_mgtq); +#else + IF_PURGE(&ic->ic_mgtq); + IF_PURGE(&ic->ic_pwrsaveq); +#endif + if (ic->ic_wep_ctx != NULL) { + free(ic->ic_wep_ctx, M_DEVBUF); + ic->ic_wep_ctx = NULL; + } + ieee80211_free_allnodes(ic); + break; + } + break; + case IEEE80211_S_SCAN: + ic->ic_flags &= ~IEEE80211_F_SIBSS; + /* initialize bss for probe request */ +#if defined(__OpenBSD__) + IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr); +#else + IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr); +#endif + 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 && + ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * AP operation and we already have a channel; + * bypass the scan and startup immediately. + */ + ieee80211_create_ibss(ic, ic->ic_des_chan); + } else { + ieee80211_begin_scan(ifp); + } + break; + case IEEE80211_S_SCAN: + /* scan next */ + if (ic->ic_flags & IEEE80211_F_ASCAN) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); + } + 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); + /* FALLTHRU */ + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + /* timeout restart scan */ + ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + if (ni != NULL) { + ni->ni_fails++; + ieee80211_unref_node(&ni); + } + ieee80211_begin_scan(ifp); + 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) { + case IEEE80211_FC0_SUBTYPE_AUTH: + /* ??? */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + /* ignore and retry scan on timeout */ + break; + } + break; + case IEEE80211_S_RUN: + switch (mgt) { + case IEEE80211_FC0_SUBTYPE_AUTH: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + ic->ic_state = ostate; /* stay RUN */ + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + /* try to reauth */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + } + break; + } + break; + case IEEE80211_S_ASSOC: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + case IEEE80211_S_ASSOC: + IEEE80211_DPRINTF(("%s: invalid transition\n", + __func__)); + break; + case IEEE80211_S_AUTH: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + break; + case IEEE80211_S_RUN: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + break; + } + break; + case IEEE80211_S_RUN: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_AUTH: + case IEEE80211_S_RUN: + IEEE80211_DPRINTF(("%s: invalid transition\n", + __func__)); + break; + case IEEE80211_S_SCAN: /* adhoc/hostap mode */ + case IEEE80211_S_ASSOC: /* infra mode */ + IASSERT(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, " "); + if (ic->ic_opmode == IEEE80211_M_STA) + printf("associated "); + else + printf("synchronized "); + printf("with %s ssid ", + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(ic->ic_bss->ni_essid, + ni->ni_esslen); + printf(" channel %d start %uMb\n", + ieee80211_chan2ieee(ic, ni->ni_chan), + IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); + } + ic->ic_mgt_timer = 0; + (*ifp->if_start)(ifp); + break; + } + break; + } + return 0; +} |