summaryrefslogtreecommitdiff
path: root/sys/net80211/ieee80211_proto.c
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2004-06-22 22:53:53 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2004-06-22 22:53:53 +0000
commitcd019f3d8285ec874db667574939d9d7eb232e9d (patch)
treeabd3323d87ab9f4bf995094367a68a36e18db9bd /sys/net80211/ieee80211_proto.c
parent30265c03b173b2dc6666564bd1e7f0449f1d1e8e (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.c566
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;
+}