diff options
author | sam <sam@FreeBSD.org> | 2003-06-23 16:55:01 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2003-06-23 16:55:01 +0000 |
commit | 505adc686a0029ab4aa4877118f251a1894a2603 (patch) | |
tree | 66ac01a331d111073b96a9870f6a5b21af78a8e1 /sys/net80211/ieee80211_proto.c | |
parent | 93890f9f0a6aff4f62adc5d1fe890af26121c947 (diff) | |
download | FreeBSD-src-505adc686a0029ab4aa4877118f251a1894a2603.zip FreeBSD-src-505adc686a0029ab4aa4877118f251a1894a2603.tar.gz |
new 802.11 layer:
o code reorg (relative to old netbsd-derived code) for future growth
o drivers now specify available channels and rates and 802.11 layer handles
almost all ifmedia actions
o multi-mode support for 11a/b/g devices
o 11g protocol additions (incomplete)
o new element id additions (for other than 11g)
o node/station table redone for proper locking and to eliminate driver
incestuousness
o split device flags and capabilities to reduce confusion and provide room
for expansion
o incomplete power management infrastructure (need to revisit)
o incomplete hooks for software retry
o more...
Diffstat (limited to 'sys/net80211/ieee80211_proto.c')
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c new file mode 100644 index 0000000..a3dd481 --- /dev/null +++ b/sys/net80211/ieee80211_proto.c @@ -0,0 +1,508 @@ +/*- + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may 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. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 protocol support. + */ + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/bus.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <machine/atomic.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_llc.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#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" +}; + +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 */ + + mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_name, "mgmt send q", MTX_DEF); + + /* 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; + + IF_DRAIN(&ic->ic_mgtq); + mtx_destroy(&ic->ic_mgtq.ifq_mtx); +} + +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 < ni->ni_rates.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])) + break; + } + if (j == srs->rs_nrates) { + if (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 +} + +int +ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_node *ni; + int error, ostate; +#ifdef IEEE80211_DEBUG + static const char *stname[] = + { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" }; +#endif + + ostate = ic->ic_state; + IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__, + stname[ostate], stname[nstate])); + if (ic->ic_newstate) { + error = (*ic->ic_newstate)(ic->ic_softc, nstate); + if (error == EINPROGRESS) + return 0; + if (error != 0) + return error; + } + + /* state transition */ + ic->ic_state = nstate; + 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: + mtx_lock(&ic->ic_nodelock); + 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); + } + mtx_unlock(&ic->ic_nodelock); + 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: + mtx_lock(&ic->ic_nodelock); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + } + mtx_unlock(&ic->ic_nodelock); + break; + default: + break; + } + /* FALLTHRU */ + case IEEE80211_S_AUTH: + case IEEE80211_S_SCAN: + 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); + break; + } + 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 && + 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, ni); + } + 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, ic->ic_bss); + 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 */ + 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, " "); + 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; +} |