summaryrefslogtreecommitdiffstats
path: root/sys/net80211/ieee80211_proto.c
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2003-06-23 16:55:01 +0000
committersam <sam@FreeBSD.org>2003-06-23 16:55:01 +0000
commit505adc686a0029ab4aa4877118f251a1894a2603 (patch)
tree66ac01a331d111073b96a9870f6a5b21af78a8e1 /sys/net80211/ieee80211_proto.c
parent93890f9f0a6aff4f62adc5d1fe890af26121c947 (diff)
downloadFreeBSD-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.c508
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;
+}
OpenPOWER on IntegriCloud