[Midnightbsd-cvs] src: sys/net80211: sync with freebsd
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Wed Oct 1 12:38:09 EDT 2008
Log Message:
-----------
sync with freebsd
Modified Files:
--------------
src/sys/net80211:
_ieee80211.h (r1.1.1.1 -> r1.2)
ieee80211.c (r1.2 -> r1.3)
ieee80211.h (r1.1.1.1 -> r1.2)
ieee80211_acl.c (r1.1.1.1 -> r1.2)
ieee80211_crypto.c (r1.1.1.1 -> r1.2)
ieee80211_crypto.h (r1.1.1.1 -> r1.2)
ieee80211_crypto_ccmp.c (r1.1.1.2 -> r1.2)
ieee80211_crypto_none.c (r1.1.1.1 -> r1.2)
ieee80211_crypto_tkip.c (r1.1.1.2 -> r1.2)
ieee80211_crypto_wep.c (r1.1.1.2 -> r1.2)
ieee80211_freebsd.c (r1.1.1.2 -> r1.2)
ieee80211_freebsd.h (r1.1.1.1 -> r1.2)
ieee80211_input.c (r1.2 -> r1.3)
ieee80211_ioctl.c (r1.2 -> r1.3)
ieee80211_ioctl.h (r1.1.1.2 -> r1.2)
ieee80211_node.c (r1.2 -> r1.3)
ieee80211_node.h (r1.2 -> r1.3)
ieee80211_output.c (r1.2 -> r1.3)
ieee80211_proto.c (r1.2 -> r1.3)
ieee80211_proto.h (r1.1.1.2 -> r1.2)
ieee80211_radiotap.h (r1.1.1.2 -> r1.2)
ieee80211_var.h (r1.2 -> r1.3)
ieee80211_xauth.c (r1.1.1.1 -> r1.2)
src/sys/opencrypto:
cast.c (r1.1.1.1 -> r1.2)
criov.c (r1.1.1.1 -> r1.2)
crypto.c (r1.2 -> r1.3)
cryptodev.c (r1.2 -> r1.3)
cryptodev.h (r1.1.1.1 -> r1.2)
cryptosoft.c (r1.1.1.1 -> r1.2)
cryptosoft.h (r1.1.1.1 -> r1.2)
xform.c (r1.1.1.1 -> r1.2)
xform.h (r1.1.1.1 -> r1.2)
Added Files:
-----------
src/sys/net80211:
ieee80211_amrr.c (r1.1)
ieee80211_amrr.h (r1.1)
ieee80211_ht.c (r1.1)
ieee80211_ht.h (r1.1)
ieee80211_power.c (r1.1)
ieee80211_power.h (r1.1)
ieee80211_regdomain.c (r1.1)
ieee80211_regdomain.h (r1.1)
ieee80211_scan.c (r1.1)
ieee80211_scan.h (r1.1)
ieee80211_scan_ap.c (r1.1)
ieee80211_scan_sta.c (r1.1)
src/sys/opencrypto:
cryptodev_if.m (r1.1)
Removed Files:
-------------
src/sys/opencrypto:
crypto_if.m
-------------- next part --------------
Index: ieee80211_acl.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_acl.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_acl.c -L sys/net80211/ieee80211_acl.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_acl.c
+++ sys/net80211/ieee80211_acl.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
* 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
@@ -30,7 +24,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_acl.c,v 1.3.2.1 2005/09/03 22:40:02 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_acl.c,v 1.6 2007/06/11 03:36:54 sam Exp $");
/*
* IEEE 802.11 MAC ACL support.
@@ -70,7 +64,7 @@
struct acl {
TAILQ_ENTRY(acl) acl_list;
LIST_ENTRY(acl) acl_hash;
- u_int8_t acl_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t acl_macaddr[IEEE80211_ADDR_LEN];
};
struct aclstate {
acl_lock_t as_lock;
@@ -83,7 +77,7 @@
/* simple hash is enough for variation of macaddr */
#define ACL_HASH(addr) \
- (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
+ (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
@@ -118,7 +112,7 @@
}
static __inline struct acl *
-_find_acl(struct aclstate *as, const u_int8_t *macaddr)
+_find_acl(struct aclstate *as, const uint8_t *macaddr)
{
struct acl *acl;
int hash;
@@ -143,7 +137,7 @@
}
static int
-acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct aclstate *as = ic->ic_as;
@@ -159,7 +153,7 @@
}
static int
-acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct aclstate *as = ic->ic_as;
struct acl *acl, *new;
@@ -197,7 +191,7 @@
}
static int
-acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct aclstate *as = ic->ic_as;
struct acl *acl;
--- /dev/null
+++ sys/net80211/ieee80211_scan.h
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 2005-2007 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.
+ *
+ * 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: src/sys/net80211/ieee80211_scan.h,v 1.3 2007/06/30 21:39:21 thompsa Exp $
+ */
+#ifndef _NET80211_IEEE80211_SCAN_H_
+#define _NET80211_IEEE80211_SCAN_H_
+
+#define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX
+
+struct ieee80211_scanner;
+
+struct ieee80211_scan_ssid {
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+};
+#define IEEE80211_SCAN_MAX_SSID 1
+
+struct ieee80211_scan_state {
+ struct ieee80211com *ss_ic;
+ const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
+ void *ss_priv; /* scanner private state */
+ uint16_t ss_flags;
+#define IEEE80211_SCAN_NOPICK 0x0001 /* scan only, no selection */
+#define IEEE80211_SCAN_ACTIVE 0x0002 /* active scan (probe req) */
+#define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */
+#define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */
+#define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */
+#define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */
+ uint8_t ss_nssid; /* # ssid's to probe/match */
+ struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID];
+ /* ssid's to probe/match */
+ /* ordered channel set */
+ struct ieee80211_channel *ss_chans[IEEE80211_SCAN_MAX];
+ uint16_t ss_next; /* ix of next chan to scan */
+ uint16_t ss_last; /* ix+1 of last chan to scan */
+ unsigned long ss_mindwell; /* min dwell on channel */
+ unsigned long ss_maxdwell; /* max dwell on channel */
+};
+
+/*
+ * The upper 16 bits of the flags word is used to communicate
+ * information to the scanning code that is NOT recorded in
+ * ss_flags. It might be better to split this stuff out into
+ * a separate variable to avoid confusion.
+ */
+#define IEEE80211_SCAN_FLUSH 0x10000 /* flush candidate table */
+#define IEEE80211_SCAN_NOSSID 0x20000 /* don't update ssid list */
+
+struct ieee80211com;
+void ieee80211_scan_attach(struct ieee80211com *);
+void ieee80211_scan_detach(struct ieee80211com *);
+
+void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *);
+
+int ieee80211_scan_update(struct ieee80211com *);
+#define IEEE80211_SCAN_FOREVER 0x7fffffff
+int ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[]);
+int ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[]);
+int ieee80211_bg_scan(struct ieee80211com *);
+void ieee80211_cancel_scan(struct ieee80211com *);
+void ieee80211_scan_next(struct ieee80211com *);
+void ieee80211_scan_done(struct ieee80211com *);
+
+struct ieee80211_scanparams;
+void ieee80211_add_scan(struct ieee80211com *,
+ const struct ieee80211_scanparams *,
+ const struct ieee80211_frame *,
+ int subtype, int rssi, int noise, int rstamp);
+void ieee80211_scan_timeout(struct ieee80211com *);
+
+void ieee80211_scan_assoc_success(struct ieee80211com *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+enum {
+ IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */
+ IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */
+};
+void ieee80211_scan_assoc_fail(struct ieee80211com *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int reason);
+void ieee80211_scan_flush(struct ieee80211com *);
+
+struct ieee80211_scan_entry;
+typedef void ieee80211_scan_iter_func(void *,
+ const struct ieee80211_scan_entry *);
+void ieee80211_scan_iterate(struct ieee80211com *,
+ ieee80211_scan_iter_func, void *);
+
+/*
+ * Parameters supplied when adding/updating an entry in a
+ * scan cache. Pointer variables should be set to NULL
+ * if no data is available. Pointer references can be to
+ * local data; any information that is saved will be copied.
+ * All multi-byte values must be in host byte order.
+ */
+struct ieee80211_scanparams {
+ uint16_t capinfo; /* 802.11 capabilities */
+ uint16_t fhdwell; /* FHSS dwell interval */
+ struct ieee80211_channel *curchan;
+ uint8_t bchan; /* chan# advertised inside beacon */
+ uint8_t fhindex;
+ uint8_t erp;
+ uint16_t bintval;
+ uint8_t timoff;
+ uint8_t *tim;
+ uint8_t *tstamp;
+ uint8_t *country;
+ uint8_t *ssid;
+ uint8_t *rates;
+ uint8_t *xrates;
+ uint8_t *doth;
+ uint8_t *wpa;
+ uint8_t *rsn;
+ uint8_t *wme;
+ uint8_t *htcap;
+ uint8_t *htinfo;
+ uint8_t *ath;
+};
+
+/*
+ * Scan cache entry format used when exporting data from a policy
+ * module; this data may be represented some other way internally.
+ */
+struct ieee80211_scan_entry {
+ uint8_t se_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t se_bssid[IEEE80211_ADDR_LEN];
+ uint8_t se_ssid[2+IEEE80211_NWID_LEN];
+ uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE];
+ uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE];
+ uint32_t se_rstamp; /* recv timestamp */
+ union {
+ uint8_t data[8];
+ uint64_t tsf;
+ } se_tstamp; /* from last rcv'd beacon */
+ uint16_t se_intval; /* beacon interval (host byte order) */
+ uint16_t se_capinfo; /* capabilities (host byte order) */
+ struct ieee80211_channel *se_chan;/* channel where sta found */
+ uint16_t se_timoff; /* byte offset to TIM ie */
+ uint16_t se_fhdwell; /* FH only (host byte order) */
+ uint8_t se_fhindex; /* FH only */
+ uint8_t se_erp; /* ERP from beacon/probe resp */
+ int8_t se_rssi; /* avg'd recv ssi */
+ int8_t se_noise; /* noise floor */
+ uint8_t se_dtimperiod; /* DTIM period */
+ uint8_t *se_wpa_ie; /* captured WPA ie */
+ uint8_t *se_rsn_ie; /* captured RSN ie */
+ uint8_t *se_wme_ie; /* captured WME ie */
+ uint8_t *se_htcap_ie; /* captured HTP cap ie */
+ uint8_t *se_htinfo_ie; /* captured HTP info ie */
+ uint8_t *se_ath_ie; /* captured Atheros ie */
+ u_int se_age; /* age of entry (0 on create) */
+};
+MALLOC_DECLARE(M_80211_SCAN);
+
+/*
+ * Template for an in-kernel scan policy module.
+ * Modules register with the scanning code and are
+ * typically loaded as needed.
+ */
+struct ieee80211_scanner {
+ const char *scan_name; /* printable name */
+ int (*scan_attach)(struct ieee80211_scan_state *);
+ int (*scan_detach)(struct ieee80211_scan_state *);
+ int (*scan_start)(struct ieee80211_scan_state *,
+ struct ieee80211com *);
+ int (*scan_restart)(struct ieee80211_scan_state *,
+ struct ieee80211com *);
+ int (*scan_cancel)(struct ieee80211_scan_state *,
+ struct ieee80211com *);
+ int (*scan_end)(struct ieee80211_scan_state *,
+ struct ieee80211com *);
+ int (*scan_flush)(struct ieee80211_scan_state *);
+ /* add an entry to the cache */
+ int (*scan_add)(struct ieee80211_scan_state *,
+ const struct ieee80211_scanparams *,
+ const struct ieee80211_frame *,
+ int subtype, int rssi, int noise, int rstamp);
+ /* age and/or purge entries in the cache */
+ void (*scan_age)(struct ieee80211_scan_state *);
+ /* note that association failed for an entry */
+ void (*scan_assoc_fail)(struct ieee80211_scan_state *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ int reason);
+ /* note that association succeed for an entry */
+ void (*scan_assoc_success)(struct ieee80211_scan_state *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+ /* iterate over entries in the scan cache */
+ void (*scan_iterate)(struct ieee80211_scan_state *,
+ ieee80211_scan_iter_func *, void *);
+};
+void ieee80211_scanner_register(enum ieee80211_opmode,
+ const struct ieee80211_scanner *);
+void ieee80211_scanner_unregister(enum ieee80211_opmode,
+ const struct ieee80211_scanner *);
+void ieee80211_scanner_unregister_all(const struct ieee80211_scanner *);
+const struct ieee80211_scanner *ieee80211_scanner_get(enum ieee80211_opmode);
+#endif /* _NET80211_IEEE80211_SCAN_H_ */
Index: ieee80211_output.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_output.c -L sys/net80211/ieee80211_output.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_output.c
+++ sys/net80211/ieee80211_output.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.26.2.7 2006/03/23 23:28:43 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.54.2.3 2007/12/07 05:46:08 kmacy Exp $");
#include "opt_inet.h"
@@ -51,6 +45,7 @@
#include <net/if_vlan_var.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
#ifdef INET
#include <netinet/in.h>
@@ -59,6 +54,16 @@
#include <netinet/ip.h>
#endif
+#define ETHER_HEADER_COPY(dst, src) \
+ memcpy(dst, src, sizeof(struct ether_header))
+
+static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic,
+ struct mbuf *m1, const struct ether_header *eh1,
+ struct mbuf *m2, const struct ether_header *eh2);
+static int ieee80211_fragment(struct ieee80211com *, struct mbuf *,
+ u_int hdrsize, u_int ciphdrsize, u_int mtu);
+static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
+
#ifdef IEEE80211_DEBUG
/*
* Decide if an outbound management frame should be
@@ -88,9 +93,9 @@
struct ieee80211_node *ni,
struct ieee80211_frame *wh,
int type,
- const u_int8_t sa[IEEE80211_ADDR_LEN],
- const u_int8_t da[IEEE80211_ADDR_LEN],
- const u_int8_t bssid[IEEE80211_ADDR_LEN])
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ const uint8_t bssid[IEEE80211_ADDR_LEN])
{
#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
@@ -116,6 +121,14 @@
IEEE80211_ADDR_COPY(wh->i_addr2, bssid);
IEEE80211_ADDR_COPY(wh->i_addr3, sa);
break;
+ case IEEE80211_M_WDS:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ /* XXX cheat, bssid holds RA */
+ IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, da);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
+ break;
case IEEE80211_M_MONITOR: /* NB: to quiet compiler */
break;
}
@@ -125,11 +138,11 @@
IEEE80211_ADDR_COPY(wh->i_addr2, sa);
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
- *(u_int16_t *)&wh->i_dur[0] = 0;
+ *(uint16_t *)&wh->i_dur[0] = 0;
/* NB: use non-QoS tid */
- *(u_int16_t *)&wh->i_seq[0] =
- htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
- ni->ni_txseqs[0]++;
+ *(uint16_t *)&wh->i_seq[0] =
+ htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
#undef WH4
}
@@ -140,9 +153,9 @@
* dispatched to the driver, then it is responsible for freeing the
* reference (and potentially free'ing up any associated storage).
*/
-static int
+int
ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int type, int timer)
+ struct mbuf *m, int type)
{
struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_frame *wh;
@@ -178,6 +191,11 @@
ether_sprintf(wh->i_addr1), __func__);
wh->i_fc[1] |= IEEE80211_FC1_WEP;
}
+ if (ni->ni_flags & IEEE80211_NODE_QOS) {
+ /* NB: force all management frames to the highest queue */
+ M_WME_SETAC(m, WME_AC_VO);
+ } else
+ M_WME_SETAC(m, WME_AC_BE);
#ifdef IEEE80211_DEBUG
/* avoid printing too many frames */
if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
@@ -192,15 +210,134 @@
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
IF_ENQUEUE(&ic->ic_mgtq, m);
- if (timer) {
+ if_start(ifp);
+ ifp->if_opackets++;
+
+ return 0;
+}
+
+/*
+ * Raw packet transmit stub for legacy drivers.
+ * Send the packet through the mgt q so we bypass
+ * the normal encapsulation work.
+ */
+int
+ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+
+ m->m_pkthdr.rcvif = (void *) ni;
+ IF_ENQUEUE(&ic->ic_mgtq, m);
+ if_start(ifp);
+ ifp->if_opackets++;
+
+ return 0;
+}
+
+/*
+ * 802.11 output routine. This is (currently) used only to
+ * connect bpf write calls to the 802.11 layer for injecting
+ * raw 802.11 frames. Note we locate the ieee80211com from
+ * the ifnet using a spare field setup at attach time. This
+ * will go away when the virtual ap support comes in.
+ */
+int
+ieee80211_output(struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt0)
+{
+#define senderr(e) do { error = (e); goto bad;} while (0)
+ struct ieee80211com *ic = ifp->if_llsoftc; /* XXX */
+ struct ieee80211_node *ni = NULL;
+ struct ieee80211_frame *wh;
+ int error;
+
+ /*
+ * Hand to the 802.3 code if not tagged as
+ * a raw 802.11 frame.
+ */
+ if (dst->sa_family != AF_IEEE80211)
+ return ether_output(ifp, m, dst, rt0);
+#ifdef MAC
+ error = mac_check_ifnet_transmit(ifp, m);
+ if (error)
+ senderr(error);
+#endif
+ if (ifp->if_flags & IFF_MONITOR)
+ senderr(ENETDOWN);
+ if ((ifp->if_flags & IFF_UP) == 0)
+ senderr(ENETDOWN);
+
+ /* XXX bypass bridge, pfil, carp, etc. */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
+ senderr(EIO); /* XXX */
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0)
+ senderr(EIO); /* XXX */
+
+ /* locate destination node */
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ case IEEE80211_FC1_DIR_FROMDS:
+ ni = ieee80211_find_txnode(ic, wh->i_addr1);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ case IEEE80211_FC1_DIR_DSTODS:
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
+ senderr(EIO); /* XXX */
+ ni = ieee80211_find_txnode(ic, wh->i_addr3);
+ break;
+ default:
+ senderr(EIO); /* XXX */
+ }
+ if (ni == NULL) {
/*
- * Set the mgt frame timeout.
+ * Permit packets w/ bpf params through regardless
+ * (see below about sa_len).
*/
- ic->ic_mgt_timer = timer;
- ifp->if_timer = 1;
+ if (dst->sa_len == 0)
+ senderr(EHOSTUNREACH);
+ ni = ieee80211_ref_node(ic->ic_bss);
}
- if_start(ifp);
- return 0;
+
+ /* XXX ctrl frames should go through */
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ (m->m_flags & M_PWR_SAV) == 0) {
+ /*
+ * Station in power save mode; pass the frame
+ * to the 802.11 layer and continue. We'll get
+ * the frame back when the time is right.
+ */
+ ieee80211_pwrsave(ni, m);
+ error = 0;
+ goto reclaim;
+ }
+
+ /* calculate priority so drivers can find the tx queue */
+ /* XXX assumes an 802.3 frame */
+ if (ieee80211_classify(ic, m, ni))
+ senderr(EIO); /* XXX */
+
+ BPF_MTAP(ifp, m);
+ /*
+ * NB: DLT_IEEE802_11_RADIO identifies the parameters are
+ * present by setting the sa_len field of the sockaddr (yes,
+ * this is a hack).
+ * NB: we assume sa_data is suitably aligned to cast.
+ */
+ return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)
+ (dst->sa_len ? dst->sa_data : NULL));
+bad:
+ if (m != NULL)
+ m_freem(m);
+reclaim:
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return error;
+#undef senderr
}
/*
@@ -218,13 +355,14 @@
struct mbuf *m;
struct ieee80211_frame *wh;
- MGETHDR(m, M_NOWAIT, MT_HEADER);
+ MGETHDR(m, M_NOWAIT, MT_DATA);
if (m == NULL) {
/* XXX debug msg */
- ic->ic_stats.is_tx_nobuf++;
ieee80211_unref_node(&ni);
+ ic->ic_stats.is_tx_nobuf++;
return ENOMEM;
}
+ MH_ALIGN(m, sizeof(struct ieee80211_frame));
m->m_pkthdr.rcvif = (void *) ni;
wh = mtod(m, struct ieee80211_frame *);
@@ -233,9 +371,11 @@
ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
/* NB: power management bit is never sent by an AP */
if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- ic->ic_opmode != IEEE80211_M_HOSTAP)
+ ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_WDS)
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ M_WME_SETAC(m, WME_AC_BE);
IEEE80211_NODE_STAT(ni, tx_data);
@@ -276,64 +416,32 @@
*/
v_wme_ac = 0;
if (ni->ni_vlan != 0) {
- struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m);
- if (mtag == NULL) {
+ if ((m->m_flags & M_VLANTAG) == 0) {
IEEE80211_NODE_STAT(ni, tx_novlantag);
return 1;
}
- if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) !=
+ if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) !=
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;
- }
+ v_wme_ac = TID_TO_WME_AC(EVL_PRIOFTAG(ni->ni_vlan));
}
#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));
+ uint8_t tos;
/*
- * IP frame, map the TOS field.
+ * IP frame, map the DSCP bits from 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;
- }
+ /* XXX m_copydata may be too slow for fast path */
+ /* NB: ip header may not be in first mbuf */
+ m_copydata(m, sizeof(struct ether_header) +
+ offsetof(struct ip, ip_tos), sizeof(tos), &tos);
+ tos >>= 5; /* NB: ECN + low 3 bits of DSCP */
+ d_wme_ac = TID_TO_WME_AC(tos);
} else {
#endif /* INET */
d_wme_ac = WME_AC_BE;
@@ -378,7 +486,7 @@
struct ieee80211_key *key, struct mbuf *m)
{
#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc))
- int needed_space = hdrsize;
+ int needed_space = ic->ic_headroom + hdrsize;
if (key != NULL) {
/* XXX belongs in crypto code? */
@@ -450,7 +558,6 @@
#undef TO_BE_RECLAIMED
}
-#define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none)
/*
* Return the transmit key to use in sending a unicast frame.
* If a unicast key is set we use that. When no unicast key is set
@@ -459,9 +566,9 @@
static __inline struct ieee80211_key *
ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
{
- if (KEY_UNDEFINED(ni->ni_ucastkey)) {
+ if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+ IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
return NULL;
return &ic->ic_nw_keys[ic->ic_def_txkey];
} else {
@@ -478,7 +585,7 @@
ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
{
if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+ IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
return NULL;
return &ic->ic_nw_keys[ic->ic_def_txkey];
}
@@ -497,8 +604,13 @@
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct llc *llc;
- int hdrsize, datalen, addqos;
+ int hdrsize, datalen, addqos, txfrag, isff;
+ /*
+ * Copy existing Ethernet header to a safe place. The
+ * rest of the code assumes it's ok to strip it when
+ * reorganizing state for the final encapsulation.
+ */
KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
@@ -526,6 +638,7 @@
ether_sprintf(eh.ether_dhost), __func__,
ic->ic_def_txkey);
ic->ic_stats.is_tx_nodefkey++;
+ goto bad;
}
} else
key = NULL;
@@ -537,29 +650,83 @@
* once negotiated in which case we'll need to make this
* configurable.
*/
- addqos = (ni->ni_flags & IEEE80211_NODE_QOS) &&
+ addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
eh.ether_type != htons(ETHERTYPE_PAE);
if (addqos)
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;
- }
+ hdrsize = roundup(hdrsize, sizeof(uint32_t));
- /* 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;
- llc->llc_control = LLC_UI;
- llc->llc_snap.org_code[0] = 0;
- llc->llc_snap.org_code[1] = 0;
- llc->llc_snap.org_code[2] = 0;
- llc->llc_snap.ether_type = eh.ether_type;
+ if ((isff = m->m_flags & M_FF) != 0) {
+ struct mbuf *m2;
+ struct ether_header eh2;
+
+ /*
+ * Fast frame encapsulation. There must be two packets
+ * chained with m_nextpkt. We do header adjustment for
+ * each, add the tunnel encapsulation, and then concatenate
+ * the mbuf chains to form a single frame for transmission.
+ */
+ m2 = m->m_nextpkt;
+ if (m2 == NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "%s: only one frame\n", __func__);
+ goto bad;
+ }
+ m->m_nextpkt = NULL;
+ /*
+ * Include fast frame headers in adjusting header
+ * layout; this allocates space according to what
+ * ieee80211_encap_fastframe will do.
+ */
+ m = ieee80211_mbuf_adjust(ic,
+ hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 +
+ sizeof(struct ether_header),
+ key, m);
+ if (m == NULL) {
+ /* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+ m_freem(m2);
+ goto bad;
+ }
+ /*
+ * Copy second frame's Ethernet header out of line
+ * and adjust for encapsulation headers. Note that
+ * we make room for padding in case there isn't room
+ * at the end of first frame.
+ */
+ KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
+ memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header));
+ m2 = ieee80211_mbuf_adjust(ic,
+ ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
+ NULL, m2);
+ if (m2 == NULL) {
+ /* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+ goto bad;
+ }
+ m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2);
+ if (m == NULL)
+ goto bad;
+ } else {
+ /*
+ * Normal frame.
+ */
+ m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+ if (m == NULL) {
+ /* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+ goto bad;
+ }
+ /* NB: this could be optimized 'cuz 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;
+ llc->llc_control = LLC_UI;
+ llc->llc_snap.org_code[0] = 0;
+ llc->llc_snap.org_code[1] = 0;
+ llc->llc_snap.org_code[2] = 0;
+ llc->llc_snap.ether_type = eh.ether_type;
+ }
datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */
M_PREPEND(m, hdrsize, M_DONTWAIT);
@@ -569,7 +736,7 @@
}
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;
+ *(uint16_t *)wh->i_dur = 0;
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
@@ -595,6 +762,7 @@
IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
break;
case IEEE80211_M_MONITOR:
+ case IEEE80211_M_WDS:
goto bad;
}
if (m->m_flags & M_MORE_DATA)
@@ -608,19 +776,53 @@
/* map from access class/queue to 11e header priorty value */
tid = WME_AC_TO_TID(ac);
qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+ /*
+ * Check if A-MPDU tx aggregation is setup or if we
+ * should try to enable it. The sta must be associated
+ * with HT and A-MPDU enabled for use. On the first
+ * frame that goes out We issue an ADDBA request and
+ * wait for a reply. The frame being encapsulated
+ * will go out w/o using A-MPDU, or possibly it might
+ * be collected by the driver and held/retransmit.
+ * ieee80211_ampdu_request handles staggering requests
+ * in case the receiver NAK's us or we are otherwise
+ * unable to establish a BA stream.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+ struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+
+ if (IEEE80211_AMPDU_RUNNING(tap)) {
+ /*
+ * Operational, mark frame for aggregation.
+ */
+ qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+ } else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
+ /*
+ * Not negotiated yet, request service.
+ */
+ ieee80211_ampdu_request(ni, tap);
+ }
+ }
+ /* XXX works even when BA marked above */
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
- qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
+ qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
qwh->i_qos[1] = 0;
qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
- *(u_int16_t *)wh->i_seq =
+ *(uint16_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]++;
- }
+ *(uint16_t *)wh->i_seq =
+ htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
+ }
+ /* check if xmit fragmentation is required */
+ txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold &&
+ !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ (ic->ic_caps & IEEE80211_C_TXFRAG) &&
+ !isff); /* NB: don't fragment ff's */
if (key != NULL) {
/*
* IEEE 802.1X: send EAPOL frames always in the clear.
@@ -629,10 +831,10 @@
if (eh.ether_type != htons(ETHERTYPE_PAE) ||
((ic->ic_flags & IEEE80211_F_WPA) &&
(ic->ic_opmode == IEEE80211_M_STA ?
- !KEY_UNDEFINED(*key) : !KEY_UNDEFINED(ni->ni_ucastkey)))) {
+ !IEEE80211_KEY_UNDEFINED(key) :
+ !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
wh->i_fc[1] |= IEEE80211_FC1_WEP;
- /* XXX do fragmentation */
- if (!ieee80211_crypto_enmic(ic, key, m, 0)) {
+ if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
"[%s] enmic failed, discard frame\n",
ether_sprintf(eh.ether_dhost));
@@ -641,8 +843,21 @@
}
}
}
+ /*
+ * NB: frag flags may leak from above; they should only
+ * be set on return to the caller if we fragment at
+ * the 802.11 layer.
+ */
+ m->m_flags &= ~(M_FRAG | M_FIRSTFRAG);
+ if (txfrag && !ieee80211_fragment(ic, m, hdrsize,
+ key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold))
+ goto bad;
IEEE80211_NODE_STAT(ni, tx_data);
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ IEEE80211_NODE_STAT(ni, tx_mcast);
+ else
+ IEEE80211_NODE_STAT(ni, tx_ucast);
IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
return m;
@@ -653,10 +868,231 @@
}
/*
+ * Do Ethernet-LLC encapsulation for each payload in a fast frame
+ * tunnel encapsulation. The frame is assumed to have an Ethernet
+ * header at the front that must be stripped before prepending the
+ * LLC followed by the Ethernet header passed in (with an Ethernet
+ * type that specifies the payload size).
+ */
+static struct mbuf *
+ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
+ const struct ether_header *eh)
+{
+ struct llc *llc;
+ uint16_t payload;
+
+ /* XXX optimize by combining m_adj+M_PREPEND */
+ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
+ llc = mtod(m, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ llc->llc_snap.org_code[0] = 0;
+ llc->llc_snap.org_code[1] = 0;
+ llc->llc_snap.org_code[2] = 0;
+ llc->llc_snap.ether_type = eh->ether_type;
+ payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */
+
+ M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
+ if (m == NULL) { /* XXX cannot happen */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "%s: no space for ether_header\n", __func__);
+ ic->ic_stats.is_tx_nobuf++;
+ return NULL;
+ }
+ ETHER_HEADER_COPY(mtod(m, void *), eh);
+ mtod(m, struct ether_header *)->ether_type = htons(payload);
+ return m;
+}
+
+/*
+ * Do fast frame tunnel encapsulation. The two frames and
+ * Ethernet headers are supplied. The caller is assumed to
+ * have arrange for space in the mbuf chains for encapsulating
+ * headers (to avoid major mbuf fragmentation).
+ *
+ * The encapsulated frame is returned or NULL if there is a
+ * problem (should not happen).
+ */
+static struct mbuf *
+ieee80211_encap_fastframe(struct ieee80211com *ic,
+ struct mbuf *m1, const struct ether_header *eh1,
+ struct mbuf *m2, const struct ether_header *eh2)
+{
+ struct llc *llc;
+ struct mbuf *m;
+ int pad;
+
+ /*
+ * First, each frame gets a standard encapsulation.
+ */
+ m1 = ieee80211_encap1(ic, m1, eh1);
+ if (m1 == NULL) {
+ m_freem(m2);
+ return NULL;
+ }
+ m2 = ieee80211_encap1(ic, m2, eh2);
+ if (m2 == NULL) {
+ m_freem(m1);
+ return NULL;
+ }
+
+ /*
+ * Pad leading frame to a 4-byte boundary. If there
+ * is space at the end of the first frame, put it
+ * there; otherwise prepend to the front of the second
+ * frame. We know doing the second will always work
+ * because we reserve space above. We prefer appending
+ * as this typically has better DMA alignment properties.
+ */
+ for (m = m1; m->m_next != NULL; m = m->m_next)
+ ;
+ pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len;
+ if (pad) {
+ if (M_TRAILINGSPACE(m) < pad) { /* prepend to second */
+ m2->m_data -= pad;
+ m2->m_len += pad;
+ m2->m_pkthdr.len += pad;
+ } else { /* append to first */
+ m->m_len += pad;
+ m1->m_pkthdr.len += pad;
+ }
+ }
+
+ /*
+ * Now, stick 'em together and prepend the tunnel headers;
+ * first the Atheros tunnel header (all zero for now) and
+ * then a special fast frame LLC.
+ *
+ * XXX optimize by prepending together
+ */
+ m->m_next = m2; /* NB: last mbuf from above */
+ m1->m_pkthdr.len += m2->m_pkthdr.len;
+ M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
+ if (m1 == NULL) { /* XXX cannot happen */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "%s: no space for tunnel header\n", __func__);
+ ic->ic_stats.is_tx_nobuf++;
+ return NULL;
+ }
+ memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
+
+ M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
+ if (m1 == NULL) { /* XXX cannot happen */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "%s: no space for llc header\n", __func__);
+ ic->ic_stats.is_tx_nobuf++;
+ return NULL;
+ }
+ llc = mtod(m1, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0;
+ llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1;
+ llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2;
+ llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE);
+
+ ic->ic_stats.is_ff_encap++;
+
+ return m1;
+}
+
+/*
+ * Fragment the frame according to the specified mtu.
+ * The size of the 802.11 header (w/o padding) is provided
+ * so we don't need to recalculate it. We create a new
+ * mbuf for each fragment and chain it through m_nextpkt;
+ * we might be able to optimize this by reusing the original
+ * packet's mbufs but that is significantly more complicated.
+ */
+static int
+ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
+ u_int hdrsize, u_int ciphdrsize, u_int mtu)
+{
+ struct ieee80211_frame *wh, *whf;
+ struct mbuf *m, *prev, *next;
+ u_int totalhdrsize, fragno, fragsize, off, remainder, payload;
+
+ KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?"));
+ KASSERT(m0->m_pkthdr.len > mtu,
+ ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu));
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ /* NB: mark the first frag; it will be propagated below */
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG;
+ totalhdrsize = hdrsize + ciphdrsize;
+ fragno = 1;
+ off = mtu - ciphdrsize;
+ remainder = m0->m_pkthdr.len - off;
+ prev = m0;
+ do {
+ fragsize = totalhdrsize + remainder;
+ if (fragsize > mtu)
+ fragsize = mtu;
+ KASSERT(fragsize < MCLBYTES,
+ ("fragment size %u too big!", fragsize));
+ if (fragsize > MHLEN)
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ goto bad;
+ /* leave room to prepend any cipher header */
+ m_align(m, fragsize - ciphdrsize);
+
+ /*
+ * Form the header in the fragment. Note that since
+ * we mark the first fragment with the MORE_FRAG bit
+ * it automatically is propagated to each fragment; we
+ * need only clear it on the last fragment (done below).
+ */
+ whf = mtod(m, struct ieee80211_frame *);
+ memcpy(whf, wh, hdrsize);
+ *(uint16_t *)&whf->i_seq[0] |= htole16(
+ (fragno & IEEE80211_SEQ_FRAG_MASK) <<
+ IEEE80211_SEQ_FRAG_SHIFT);
+ fragno++;
+
+ payload = fragsize - totalhdrsize;
+ /* NB: destination is known to be contiguous */
+ m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize);
+ m->m_len = hdrsize + payload;
+ m->m_pkthdr.len = hdrsize + payload;
+ m->m_flags |= M_FRAG;
+
+ /* chain up the fragment */
+ prev->m_nextpkt = m;
+ prev = m;
+
+ /* deduct fragment just formed */
+ remainder -= payload;
+ off += payload;
+ } while (remainder != 0);
+ whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG;
+
+ /* strip first mbuf now that everything has been copied */
+ m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize)));
+ m0->m_flags |= M_FIRSTFRAG | M_FRAG;
+
+ ic->ic_stats.is_tx_fragframes++;
+ ic->ic_stats.is_tx_frags += fragno-1;
+
+ return 1;
+bad:
+ /* reclaim fragments but leave original frame for caller to free */
+ for (m = m0->m_nextpkt; m != NULL; m = next) {
+ next = m->m_nextpkt;
+ m->m_nextpkt = NULL; /* XXX paranoid */
+ m_freem(m);
+ }
+ m0->m_nextpkt = NULL;
+ return 0;
+}
+
+/*
* Add a supported rates element id to a frame.
*/
-static u_int8_t *
-ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+static uint8_t *
+ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
{
int nrates;
@@ -672,8 +1108,8 @@
/*
* Add an extended supported rates element id to a frame.
*/
-static u_int8_t *
-ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+static uint8_t *
+ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
{
/*
* Add an extended supported rates element if operating in 11g mode.
@@ -691,8 +1127,8 @@
/*
* Add an ssid elemet to a frame.
*/
-static u_int8_t *
-ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
+static uint8_t *
+ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
{
*frm++ = IEEE80211_ELEMID_SSID;
*frm++ = len;
@@ -703,10 +1139,10 @@
/*
* Add an erp element to a frame.
*/
-static u_int8_t *
-ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
+static uint8_t *
+ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
{
- u_int8_t erp;
+ uint8_t erp;
*frm++ = IEEE80211_ELEMID_ERP;
*frm++ = 1;
@@ -721,8 +1157,8 @@
return frm;
}
-static u_int8_t *
-ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
+static uint8_t *
+ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie)
{
#define WPA_OUI_BYTES 0x00, 0x50, 0xf2
#define ADDSHORT(frm, v) do { \
@@ -734,8 +1170,8 @@
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] = {
+ static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
+ static const uint8_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 */
@@ -743,15 +1179,15 @@
{ 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
{ WPA_OUI_BYTES, WPA_CSE_NULL },
};
- static const u_int8_t wep104_suite[4] =
+ static const uint8_t wep104_suite[4] =
{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
- static const u_int8_t key_mgt_unspec[4] =
+ static const uint8_t key_mgt_unspec[4] =
{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
- static const u_int8_t key_mgt_psk[4] =
+ static const uint8_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;
+ uint8_t *frm = ie;
+ uint8_t *selcnt;
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 0; /* length filled in below */
@@ -807,8 +1243,8 @@
#undef WPA_OUI_BYTES
}
-static u_int8_t *
-ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie)
+static uint8_t *
+ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie)
{
#define RSN_OUI_BYTES 0x00, 0x0f, 0xac
#define ADDSHORT(frm, v) do { \
@@ -820,7 +1256,7 @@
memcpy(frm, sel, 4); \
frm += 4; \
} while (0)
- static const u_int8_t cipher_suite[][4] = {
+ static const uint8_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 },
@@ -828,15 +1264,15 @@
{ 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
{ RSN_OUI_BYTES, RSN_CSE_NULL },
};
- static const u_int8_t wep104_suite[4] =
+ static const uint8_t wep104_suite[4] =
{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
- static const u_int8_t key_mgt_unspec[4] =
+ static const uint8_t key_mgt_unspec[4] =
{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
- static const u_int8_t key_mgt_psk[4] =
+ static const uint8_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;
+ uint8_t *frm = ie;
+ uint8_t *selcnt;
*frm++ = IEEE80211_ELEMID_RSN;
*frm++ = 0; /* length filled in below */
@@ -893,8 +1329,8 @@
/*
* Add a WPA/RSN element to a frame.
*/
-static u_int8_t *
-ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
+static uint8_t *
+ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic)
{
KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
@@ -909,8 +1345,8 @@
/*
* 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 uint8_t *
+ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme)
{
static const struct ieee80211_wme_info info = {
.wme_id = IEEE80211_ELEMID_VENDOR,
@@ -928,8 +1364,8 @@
/*
* 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)
+static uint8_t *
+ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme)
{
#define SM(_v, _f) (((_v) << _f##_S) & _f)
#define ADDSHORT(frm, v) do { \
@@ -970,23 +1406,48 @@
}
#undef WME_OUI_BYTES
+#define ATH_OUI_BYTES 0x00, 0x03, 0x7f
+/*
+ * Add a WME information element to a frame.
+ */
+static uint8_t *
+ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix)
+{
+ static const struct ieee80211_ath_ie info = {
+ .ath_id = IEEE80211_ELEMID_VENDOR,
+ .ath_len = sizeof(struct ieee80211_ath_ie) - 2,
+ .ath_oui = { ATH_OUI_BYTES },
+ .ath_oui_type = ATH_OUI_TYPE,
+ .ath_oui_subtype= ATH_OUI_SUBTYPE,
+ .ath_version = ATH_OUI_VERSION,
+ };
+ struct ieee80211_ath_ie *ath = (struct ieee80211_ath_ie *) frm;
+
+ memcpy(frm, &info, sizeof(info));
+ ath->ath_capability = caps;
+ ath->ath_defkeyix[0] = (defkeyix & 0xff);
+ ath->ath_defkeyix[1] = ((defkeyix >> 8) & 0xff);
+ return frm + sizeof(info);
+}
+#undef ATH_OUI_BYTES
+
/*
* Send a probe request frame with the specified ssid
* and any optional information element data.
*/
int
ieee80211_send_probereq(struct ieee80211_node *ni,
- const u_int8_t sa[IEEE80211_ADDR_LEN],
- const u_int8_t da[IEEE80211_ADDR_LEN],
- const u_int8_t bssid[IEEE80211_ADDR_LEN],
- const u_int8_t *ssid, size_t ssidlen,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t *ssid, size_t ssidlen,
const void *optie, size_t optielen)
{
struct ieee80211com *ic = ni->ni_ic;
- enum ieee80211_phymode mode;
struct ieee80211_frame *wh;
+ const struct ieee80211_rateset *rs;
struct mbuf *m;
- u_int8_t *frm;
+ uint8_t *frm;
/*
* Hold a reference on the node so it doesn't go away until after
@@ -1008,6 +1469,7 @@
* [tlv] user-specified ie's
*/
m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
@@ -1020,15 +1482,15 @@
}
frm = ieee80211_add_ssid(frm, ssid, ssidlen);
- mode = ieee80211_chan2mode(ic, ic->ic_curchan);
- frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
- frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+ rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+ frm = ieee80211_add_rates(frm, rs);
+ frm = ieee80211_add_xrates(frm, rs);
if (optie != NULL) {
memcpy(frm, optie, optielen);
frm += optielen;
}
- m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
if (m == NULL)
@@ -1058,10 +1520,10 @@
/*
* Calculate capability information for mgt frames.
*/
-static u_int16_t
+static uint16_t
getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
{
- u_int16_t capinfo;
+ uint16_t capinfo;
KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode"));
@@ -1090,11 +1552,13 @@
ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
int type, int arg)
{
+#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT)
#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+ const struct ieee80211_rateset *rs;
struct mbuf *m;
- u_int8_t *frm;
- u_int16_t capinfo;
- int has_challenge, is_shared_key, ret, timer, status;
+ uint8_t *frm;
+ uint16_t capinfo;
+ int has_challenge, is_shared_key, ret, status;
KASSERT(ni != NULL, ("null node"));
@@ -1110,7 +1574,6 @@
ieee80211_node_refcnt(ni)+1);
ieee80211_ref_node(ni);
- timer = 0;
switch (type) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
/*
@@ -1126,11 +1589,17 @@
* [tlv] extended supported rates
* [tlv] WPA
* [tlv] WME (optional)
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * [tlv] Atheros capabilities
*/
m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
8
- + sizeof(u_int16_t)
- + sizeof(u_int16_t)
+ + sizeof(uint16_t)
+ + sizeof(uint16_t)
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 7 /* max(7,3) */
@@ -1141,23 +1610,28 @@
+ (ic->ic_flags & IEEE80211_F_WPA ?
2*sizeof(struct ieee80211_ie_wpa) : 0)
+ sizeof(struct ieee80211_wme_param)
+ /* XXX check for cluster requirement */
+ + 2*sizeof(struct ieee80211_ie_htcap) + 4
+ + 2*sizeof(struct ieee80211_ie_htinfo) + 4
+ + sizeof(struct ieee80211_ath_ie)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
memset(frm, 0, 8); /* timestamp should be filled later */
frm += 8;
- *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
+ *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
frm += 2;
capinfo = getcapinfo(ic, ic->ic_curchan);
- *(u_int16_t *)frm = htole16(capinfo);
+ *(uint16_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, &ni->ni_rates);
+ rs = ieee80211_get_suprates(ic, ic->ic_curchan);
+ frm = ieee80211_add_rates(frm, rs);
- if (ic->ic_phytype == IEEE80211_T_FH) {
+ if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
*frm++ = IEEE80211_ELEMID_FHPARMS;
*frm++ = 5;
*frm++ = ni->ni_fhdwell & 0x00ff;
@@ -1180,12 +1654,32 @@
}
if (ic->ic_flags & IEEE80211_F_WPA)
frm = ieee80211_add_wpa(frm, ic);
- if (ic->ic_curmode == IEEE80211_MODE_11G)
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
frm = ieee80211_add_erp(frm, ic);
- frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ frm = ieee80211_add_xrates(frm, rs);
+ /*
+ * NB: legacy 11b clients do not get certain ie's.
+ * The caller identifies such clients by passing
+ * a token in arg to us. Could expand this to be
+ * any legacy client for stuff like HT ie's.
+ */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+ arg != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap(frm, ni);
+ frm = ieee80211_add_htinfo(frm, ni);
+ }
if (ic->ic_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
+ arg != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap_vendor(frm, ni);
+ frm = ieee80211_add_htinfo_vendor(frm, ni);
+ }
+ if (ni->ni_ath_ie != NULL)
+ frm = ieee80211_add_ath(frm, ni->ni_ath_flags,
+ ni->ni_ath_defkeyix);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
break;
case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -1208,27 +1702,28 @@
ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
m = ieee80211_getmgtframe(&frm,
- 3 * sizeof(u_int16_t)
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ 3 * sizeof(uint16_t)
+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
- sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)
+ sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- ((u_int16_t *)frm)[0] =
+ ((uint16_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] = htole16(status);/* status */
+ ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */
+ ((uint16_t *)frm)[2] = htole16(status);/* status */
if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
- ((u_int16_t *)frm)[3] =
+ ((uint16_t *)frm)[3] =
htole16((IEEE80211_CHALLENGE_LEN << 8) |
IEEE80211_ELEMID_CHALLENGE);
- memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+ memcpy(&((uint16_t *)frm)[4], ni->ni_challenge,
IEEE80211_CHALLENGE_LEN);
m->m_pkthdr.len = m->m_len =
- 4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN;
+ 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN;
if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
"[%s] request encrypt frame (%s)\n",
@@ -1236,7 +1731,7 @@
m->m_flags |= M_LINK0; /* WEP-encrypt, please */
}
} else
- m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t);
+ m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t);
/* XXX not right for shared key */
if (status == IEEE80211_STATUS_SUCCESS)
@@ -1245,18 +1740,21 @@
IEEE80211_NODE_STAT(ni, tx_auth_fail);
if (ic->ic_opmode == IEEE80211_M_STA)
- timer = IEEE80211_TRANS_WAIT;
+ ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
+ (void *) ic->ic_state);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
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));
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t));
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- *(u_int16_t *)frm = htole16(arg); /* reason */
- m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+ *(uint16_t *)frm = htole16(arg); /* reason */
+ m->m_pkthdr.len = m->m_len = sizeof(uint16_t);
IEEE80211_NODE_STAT(ni, tx_deauth);
IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
@@ -1275,16 +1773,22 @@
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Atheros capabilities (if negotiated)
* [tlv] user-specified ie's
*/
m = ieee80211_getmgtframe(&frm,
- sizeof(u_int16_t)
- + sizeof(u_int16_t)
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t)
+ + sizeof(uint16_t)
+ IEEE80211_ADDR_LEN
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ sizeof(struct ieee80211_wme_info)
+ + 2*sizeof(struct ieee80211_ie_htcap) + 4
+ + sizeof(struct ieee80211_ath_ie)
+ (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
);
if (m == NULL)
@@ -1302,13 +1806,19 @@
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
- if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
(ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
- *(u_int16_t *)frm = htole16(capinfo);
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) &&
+ (ic->ic_flags & IEEE80211_F_DOTH))
+ capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
+ *(uint16_t *)frm = htole16(capinfo);
frm += 2;
- *(u_int16_t *)frm = htole16(ic->ic_lintval);
+ KASSERT(ic->ic_bss->ni_intval != 0,
+ ("beacon interval is zero!"));
+ *(uint16_t *)frm = htole16(howmany(ic->ic_lintval,
+ ic->ic_bss->ni_intval));
frm += 2;
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
@@ -1319,48 +1829,71 @@
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_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_htcap_ie != NULL &&
+ ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+ frm = ieee80211_add_htcap(frm, ni);
if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_htcap_ie != NULL &&
+ ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+ frm = ieee80211_add_htcap_vendor(frm, ni);
+ if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ frm = ieee80211_add_ath(frm,
+ IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+ (ic->ic_flags & IEEE80211_F_WPA) == 0 &&
+ ni->ni_authmode != IEEE80211_AUTH_8021X &&
+ ic->ic_def_txkey != IEEE80211_KEYIX_NONE ?
+ ic->ic_def_txkey : 0x7fff);
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 *);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- timer = IEEE80211_TRANS_WAIT;
+ ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
+ (void *) ic->ic_state);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
/*
- * asreq frame format
+ * asresp frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME (if enabled and STA enabled)
+ * [tlv] HT capabilities (standard or vendor OUI)
+ * [tlv] HT information (standard or vendor OUI)
+ * [tlv] Atheros capabilities (if enabled and STA enabled)
*/
m = ieee80211_getmgtframe(&frm,
- sizeof(u_int16_t)
- + sizeof(u_int16_t)
- + sizeof(u_int16_t)
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t)
+ + sizeof(uint16_t)
+ + sizeof(uint16_t)
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ sizeof(struct ieee80211_wme_param)
+ + sizeof(struct ieee80211_ie_htcap) + 4
+ + sizeof(struct ieee80211_ie_htinfo) + 4
+ + sizeof(struct ieee80211_ath_ie)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
capinfo = getcapinfo(ic, ic->ic_curchan);
- *(u_int16_t *)frm = htole16(capinfo);
+ *(uint16_t *)frm = htole16(capinfo);
frm += 2;
- *(u_int16_t *)frm = htole16(arg); /* status */
+ *(uint16_t *)frm = htole16(arg); /* status */
frm += 2;
if (arg == IEEE80211_STATUS_SUCCESS) {
- *(u_int16_t *)frm = htole16(ni->ni_associd);
+ *(uint16_t *)frm = htole16(ni->ni_associd);
IEEE80211_NODE_STAT(ni, tx_assoc);
} else
IEEE80211_NODE_STAT(ni, tx_assoc_fail);
@@ -1368,20 +1901,35 @@
frm = ieee80211_add_rates(frm, &ni->ni_rates);
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ /* NB: respond according to what we received */
+ if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) {
+ frm = ieee80211_add_htcap(frm, ni);
+ frm = ieee80211_add_htinfo(frm, ni);
+ }
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 *);
+ if ((ni->ni_flags & HTFLAGS) == HTFLAGS) {
+ frm = ieee80211_add_htcap_vendor(frm, ni);
+ frm = ieee80211_add_htinfo_vendor(frm, ni);
+ }
+ if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ frm = ieee80211_add_ath(frm,
+ IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+ ni->ni_ath_defkeyix);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
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));
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t));
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- *(u_int16_t *)frm = htole16(arg); /* reason */
- m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+ *(uint16_t *)frm = htole16(arg); /* reason */
+ m->m_pkthdr.len = m->m_len = sizeof(uint16_t);
IEEE80211_NODE_STAT(ni, tx_disassoc);
IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
@@ -1394,28 +1942,72 @@
senderr(EINVAL, is_tx_unknownmgt);
/* NOTREACHED */
}
- ret = ieee80211_mgmt_output(ic, ni, m, type, timer);
- if (ret != 0) {
+
+ ret = ieee80211_mgmt_output(ic, ni, m, type);
+ if (ret != 0)
+ goto bad;
+ return 0;
bad:
- ieee80211_free_node(ni);
- }
+ ieee80211_free_node(ni);
return ret;
#undef senderr
+#undef HTFLAGS
+}
+
+static void
+ieee80211_tx_mgt_timeout(void *arg)
+{
+ struct ieee80211_node *ni = arg;
+ struct ieee80211com *ic = ni->ni_ic;
+
+ if (ic->ic_state != IEEE80211_S_INIT &&
+ (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ /*
+ * NB: it's safe to specify a timeout as the reason here;
+ * it'll only be used in the right state.
+ */
+ ieee80211_new_state(ic, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_TIMEOUT);
+ }
+}
+
+static void
+ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ enum ieee80211_state ostate = (enum ieee80211_state) arg;
+
+ /*
+ * Frame transmit completed; arrange timer callback. If
+ * transmit was successfuly we wait for response. Otherwise
+ * we arrange an immediate callback instead of doing the
+ * callback directly since we don't know what state the driver
+ * is in (e.g. what locks it is holding). This work should
+ * not be too time-critical and not happen too often so the
+ * added overhead is acceptable.
+ *
+ * XXX what happens if !acked but response shows up before callback?
+ */
+ if (ic->ic_state == ostate)
+ callout_reset(&ic->ic_mgtsend,
+ status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
+ ieee80211_tx_mgt_timeout, ni);
}
/*
* Allocate a beacon frame and fillin the appropriate bits.
*/
struct mbuf *
-ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
+ieee80211_beacon_alloc(struct ieee80211_node *ni,
struct ieee80211_beacon_offsets *bo)
{
+ struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_frame *wh;
struct mbuf *m;
int pktlen;
- u_int8_t *frm, *efrm;
- u_int16_t capinfo;
+ uint8_t *frm;
+ uint16_t capinfo;
struct ieee80211_rateset *rs;
/*
@@ -1427,29 +2019,39 @@
* [tlv] supported rates
* [3] parameter set (DS)
* [tlv] parameter set (IBSS/TIM)
+ * [tlv] country code
* [tlv] extended rate phy (ERP)
* [tlv] extended supported rates
* [tlv] WME parameters
* [tlv] WPA/RSN parameters
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
* 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 */
+ + sizeof(uint16_t) /* beacon interval */
+ + sizeof(uint16_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 */
+ + sizeof(struct ieee80211_country_ie) /* country code */
+ 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)
+ /* XXX conditional? */
+ + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
+ + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
;
- m = ieee80211_getmgtframe(&frm, pktlen);
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
if (m == NULL) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
"%s: cannot get buf; size %u\n", __func__, pktlen);
@@ -1457,13 +2059,15 @@
return NULL;
}
+ memset(bo, 0, sizeof(*bo));
+
memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */
frm += 8;
- *(u_int16_t *)frm = htole16(ni->ni_intval);
+ *(uint16_t *)frm = htole16(ni->ni_intval);
frm += 2;
capinfo = getcapinfo(ic, ni->ni_chan);
- bo->bo_caps = (u_int16_t *)frm;
- *(u_int16_t *)frm = htole16(capinfo);
+ bo->bo_caps = (uint16_t *)frm;
+ *(uint16_t *)frm = htole16(capinfo);
frm += 2;
*frm++ = IEEE80211_ELEMID_SSID;
if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
@@ -1473,10 +2077,10 @@
} else
*frm++ = 0;
frm = ieee80211_add_rates(frm, rs);
- if (ic->ic_curmode != IEEE80211_MODE_FH) {
+ if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) {
*frm++ = IEEE80211_ELEMID_DSPARMS;
*frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+ *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
}
bo->bo_tim = frm;
if (ic->ic_opmode == IEEE80211_M_IBSS) {
@@ -1496,21 +2100,33 @@
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);
- ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
- }
+ bo->bo_tim_trailer = frm;
+ if (ic->ic_flags & IEEE80211_F_DOTH)
+ frm = ieee80211_add_countryie(frm, ic,
+ ic->ic_countrycode, ic->ic_location);
if (ic->ic_flags & IEEE80211_F_WPA)
frm = ieee80211_add_wpa(frm, ic);
- if (ic->ic_curmode == IEEE80211_MODE_11G) {
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
bo->bo_erp = frm;
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 *);
+ frm = ieee80211_add_xrates(frm, rs);
+ if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) {
+ frm = ieee80211_add_htcap(frm, ni);
+ bo->bo_htinfo = frm;
+ frm = ieee80211_add_htinfo(frm, ni);
+ }
+ if (ic->ic_flags & IEEE80211_F_WME) {
+ bo->bo_wme = frm;
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ }
+ if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+ frm = ieee80211_add_htcap_vendor(frm, ni);
+ frm = ieee80211_add_htinfo_vendor(frm, ni);
+ }
+ bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
KASSERT(m != NULL, ("no space for 802.11 header?"));
@@ -1518,11 +2134,11 @@
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;
+ *(uint16_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;
+ *(uint16_t *)wh->i_seq = 0;
return m;
}
@@ -1531,11 +2147,12 @@
* Update the dynamic parts of a beacon frame based on the current state.
*/
int
-ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
+ieee80211_beacon_update(struct ieee80211_node *ni,
struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
{
+ struct ieee80211com *ic = ni->ni_ic;
int len_changed = 0;
- u_int16_t capinfo;
+ uint16_t capinfo;
IEEE80211_BEACON_LOCK(ic);
/* XXX faster to recalculate entirely or just changes? */
@@ -1577,16 +2194,21 @@
wme->wme_hipri_traffic =
wme->wme_hipri_switch_hysteresis;
}
- if (ic->ic_flags & IEEE80211_F_WMEUPDATE) {
+ if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) {
(void) ieee80211_add_wme_param(bo->bo_wme, wme);
- ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+ clrbit(bo->bo_flags, IEEE80211_BEACON_WME);
}
}
+ if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
+ ieee80211_ht_update_beacon(ic, bo);
+ clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO);
+ }
+
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) {
+ if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) {
u_int timlen, timoff, i;
/*
* ATIM/DTIM needs updating. If it fits in the
@@ -1623,12 +2245,14 @@
if (timlen != bo->bo_tim_len) {
/* copy up/down trailer */
int adjust = tie->tim_bitmap+timlen
- - bo->bo_trailer;
- ovbcopy(bo->bo_trailer, bo->bo_trailer+adjust,
- bo->bo_trailer_len);
- bo->bo_trailer += adjust;
+ - bo->bo_tim_trailer;
+ ovbcopy(bo->bo_tim_trailer,
+ bo->bo_tim_trailer+adjust,
+ bo->bo_tim_trailer_len);
+ bo->bo_tim_trailer += adjust;
bo->bo_wme += adjust;
bo->bo_erp += adjust;
+ bo->bo_htinfo += adjust;
bo->bo_tim_len = timlen;
/* update information element */
@@ -1639,7 +2263,7 @@
memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
bo->bo_tim_len);
- ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
+ clrbit(bo->bo_flags, IEEE80211_BEACON_TIM);
IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
"%s: TIM updated, pending %u, off %u, len %u\n",
@@ -1655,61 +2279,15 @@
tie->tim_bitctl |= 1;
else
tie->tim_bitctl &= ~1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) {
+ if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
/*
* ERP element needs updating.
*/
(void) ieee80211_add_erp(bo->bo_erp, ic);
- ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE;
+ clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
}
}
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_bintval) << 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 with age %d, %u now queued\n",
- ether_sprintf(ni->ni_macaddr), age, qlen);
-
- if (qlen == 1)
- ic->ic_set_tim(ni, 1);
-}
--- /dev/null
+++ sys/net80211/ieee80211_scan_ap.c
@@ -0,0 +1,408 @@
+/*-
+ * Copyright (c) 2002-2007 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.
+ *
+ * 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>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_scan_ap.c,v 1.1.2.1 2007/11/11 17:44:36 sam Exp $");
+
+/*
+ * IEEE 802.11 ap scanning support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+struct ap_state {
+ int as_maxrssi[IEEE80211_CHAN_MAX];
+};
+
+static int ap_flush(struct ieee80211_scan_state *);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+/*
+ * Attach prior to any scanning work.
+ */
+static int
+ap_attach(struct ieee80211_scan_state *ss)
+{
+ struct ap_state *as;
+
+ MALLOC(as, struct ap_state *, sizeof(struct ap_state),
+ M_80211_SCAN, M_NOWAIT);
+ ss->ss_priv = as;
+ ap_flush(ss);
+ nrefs++; /* NB: we assume caller locking */
+ return 1;
+}
+
+/*
+ * Cleanup any private state.
+ */
+static int
+ap_detach(struct ieee80211_scan_state *ss)
+{
+ struct ap_state *as = ss->ss_priv;
+
+ if (as != NULL) {
+ KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+ nrefs--; /* NB: we assume caller locking */
+ FREE(as, M_80211_SCAN);
+ }
+ return 1;
+}
+
+/*
+ * Flush all per-scan state.
+ */
+static int
+ap_flush(struct ieee80211_scan_state *ss)
+{
+ struct ap_state *as = ss->ss_priv;
+
+ memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi));
+ ss->ss_last = 0; /* insure no channel will be picked */
+ return 0;
+}
+
+static int
+find11gchannel(struct ieee80211com *ic, int i, int freq)
+{
+ const struct ieee80211_channel *c;
+ int j;
+
+ /*
+ * The normal ordering in the channel list is b channel
+ * immediately followed by g so optimize the search for
+ * this. We'll still do a full search just in case.
+ */
+ for (j = i+1; j < ic->ic_nchans; j++) {
+ c = &ic->ic_channels[j];
+ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+ return 1;
+ }
+ for (j = 0; j < i; j++) {
+ c = &ic->ic_channels[j];
+ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Start an ap scan by populating the channel list.
+ */
+static int
+ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ struct ieee80211_channel *c;
+ int i;
+
+ ss->ss_last = 0;
+ if (ic->ic_des_mode == IEEE80211_MODE_AUTO) {
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (IEEE80211_IS_CHAN_TURBO(c)) {
+#ifdef IEEE80211_F_XR
+ /* XR is not supported on turbo channels */
+ if (ic->ic_flags & IEEE80211_F_XR)
+ continue;
+#endif
+ /* dynamic channels are scanned in base mode */
+ if (!IEEE80211_IS_CHAN_ST(c))
+ continue;
+ } else if (IEEE80211_IS_CHAN_HT(c)) {
+ /* HT channels are scanned in legacy */
+ continue;
+ } else {
+ /*
+ * Use any 11g channel instead of 11b one.
+ */
+ if (IEEE80211_IS_CHAN_B(c) &&
+ find11gchannel(ic, i, c->ic_freq))
+ continue;
+ }
+ if (ss->ss_last >= IEEE80211_SCAN_MAX)
+ break;
+ ss->ss_chans[ss->ss_last++] = c;
+ }
+ } else {
+ static const u_int chanflags[IEEE80211_MODE_MAX] = {
+ 0, /* IEEE80211_MODE_AUTO */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
+ IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */
+ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
+ IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */
+ IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
+ IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */
+ };
+ u_int modeflags;
+
+ modeflags = chanflags[ic->ic_des_mode];
+ if ((ic->ic_flags & IEEE80211_F_TURBOP) &&
+ modeflags != IEEE80211_CHAN_ST) {
+ if (ic->ic_des_mode == IEEE80211_MODE_11G)
+ modeflags = IEEE80211_CHAN_108G;
+ else
+ modeflags = IEEE80211_CHAN_108A;
+ }
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if ((c->ic_flags & modeflags) != modeflags)
+ continue;
+#ifdef IEEE80211_F_XR
+ /* XR is not supported on turbo channels */
+ if (IEEE80211_IS_CHAN_TURBO(c) &&
+ (ic->ic_flags & IEEE80211_F_XR))
+ continue;
+#endif
+ if (ss->ss_last >= IEEE80211_SCAN_MAX)
+ break;
+ /*
+ * Do not select static turbo channels if
+ * the mode is not static turbo.
+ */
+ if (IEEE80211_IS_CHAN_STURBO(c) &&
+ ic->ic_des_mode != IEEE80211_MODE_STURBO_A)
+ continue;
+ ss->ss_chans[ss->ss_last++] = c;
+ }
+ }
+ ss->ss_next = 0;
+ /* XXX tunables */
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ ss->ss_maxdwell = msecs_to_ticks(300); /* 300ms */
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic)) {
+ if_printf(ic->ic_ifp, "scan set ");
+ ieee80211_scan_dump_channels(ss);
+ printf(" dwell min %ld max %ld\n",
+ ss->ss_mindwell, ss->ss_maxdwell);
+ }
+#endif /* IEEE80211_DEBUG */
+
+ return 0;
+}
+
+/*
+ * Restart a bg scan.
+ */
+static int
+ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ return 0;
+}
+
+/*
+ * Cancel an ongoing scan.
+ */
+static int
+ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ return 0;
+}
+
+/*
+ * Record max rssi on channel.
+ */
+static int
+ap_add(struct ieee80211_scan_state *ss,
+ const struct ieee80211_scanparams *sp,
+ const struct ieee80211_frame *wh,
+ int subtype, int rssi, int noise, int rstamp)
+{
+ struct ap_state *as = ss->ss_priv;
+ struct ieee80211com *ic = ss->ss_ic;
+ int chan;
+
+ chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+ /* XXX better quantification of channel use? */
+ /* XXX count bss's? */
+ if (rssi > as->as_maxrssi[chan])
+ as->as_maxrssi[chan] = rssi;
+ /* XXX interference, turbo requirements */
+ return 1;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static int
+ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ struct ap_state *as = ss->ss_priv;
+ int i, chan, bestchan, bestchanix;
+
+ KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
+ ("wrong opmode %u", ic->ic_opmode));
+ /* XXX select channel more intelligently, e.g. channel spread, power */
+ bestchan = -1;
+ bestchanix = 0; /* NB: silence compiler */
+ /* NB: use scan list order to preserve channel preference */
+ for (i = 0; i < ss->ss_last; 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.
+ */
+ /* XXX channel have interference? */
+ chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]);
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n",
+ __func__, chan, as->as_maxrssi[chan],
+ bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0);
+
+ if (as->as_maxrssi[chan] == 0) {
+ bestchan = chan;
+ bestchanix = i;
+ /* XXX use other considerations */
+ break;
+ }
+ if (bestchan == -1 ||
+ as->as_maxrssi[chan] < as->as_maxrssi[bestchan])
+ bestchan = chan;
+ }
+ if (bestchan == -1) {
+ /* no suitable channel, should not happen */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no suitable channel! (should not happen)\n", __func__);
+ /* XXX print something? */
+ return 0; /* restart scan */
+ } else {
+ struct ieee80211_channel *c;
+
+ /* XXX notify all vap's? */
+ /*
+ * If this is a dynamic turbo frequency,
+ * start with normal mode first.
+ */
+ c = ss->ss_chans[bestchanix];
+ if (IEEE80211_IS_CHAN_TURBO(c) &&
+ !IEEE80211_IS_CHAN_STURBO(c)) {
+ c = ieee80211_find_channel(ic, c->ic_freq,
+ c->ic_flags & ~IEEE80211_CHAN_TURBO);
+ if (c == NULL) {
+ /* should never happen ?? */
+ return 0;
+ }
+ }
+ ieee80211_create_ibss(ic,
+ ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext));
+ return 1;
+ }
+}
+
+static void
+ap_age(struct ieee80211_scan_state *ss)
+{
+ /* XXX is there anything meaningful to do? */
+}
+
+static void
+ap_iterate(struct ieee80211_scan_state *ss,
+ ieee80211_scan_iter_func *f, void *arg)
+{
+ /* NB: nothing meaningful we can do */
+}
+
+static void
+ap_assoc_success(struct ieee80211_scan_state *ss,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+ /* should not be called */
+}
+
+static void
+ap_assoc_fail(struct ieee80211_scan_state *ss,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
+{
+ /* should not be called */
+}
+
+static const struct ieee80211_scanner ap_default = {
+ .scan_name = "default",
+ .scan_attach = ap_attach,
+ .scan_detach = ap_detach,
+ .scan_start = ap_start,
+ .scan_restart = ap_restart,
+ .scan_cancel = ap_cancel,
+ .scan_end = ap_end,
+ .scan_flush = ap_flush,
+ .scan_add = ap_add,
+ .scan_age = ap_age,
+ .scan_iterate = ap_iterate,
+ .scan_assoc_success = ap_assoc_success,
+ .scan_assoc_fail = ap_assoc_fail,
+};
+
+/*
+ * Module glue.
+ */
+static int
+wlan_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default);
+ return 0;
+ case MOD_UNLOAD:
+ case MOD_QUIESCE:
+ if (nrefs) {
+ printf("wlan_scan_ap: still in use (%u dynamic refs)\n",
+ nrefs);
+ return EBUSY;
+ }
+ if (type == MOD_UNLOAD)
+ ieee80211_scanner_unregister_all(&ap_default);
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t wlan_mod = {
+ "wlan_scan_ap",
+ wlan_modevent,
+ 0
+};
+DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_scan_ap, 1);
+MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1);
Index: ieee80211_ioctl.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_ioctl.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_ioctl.c -L sys/net80211/ieee80211_ioctl.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_ioctl.c
+++ sys/net80211/ieee80211_ioctl.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,9 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.25.2.12 2006/04/03 17:21:05 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.57.2.1 2007/11/11 17:44:36 sam Exp $");
+
+#include "opt_compat.h"
/*
* IEEE 802.11 ioctl support (FreeBSD-specific)
@@ -43,12 +39,13 @@
#include <sys/endian.h>
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/priv.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <net/if.h>
-#include <net/if_arp.h>
+#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -65,752 +62,15 @@
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ioctl.h>
-#include <dev/wi/if_wavelan_ieee.h>
-
#define IS_UP(_ic) \
(((_ic)->ic_ifp->if_flags & IFF_UP) && \
((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
#define IS_UP_AUTO(_ic) \
(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+#define RESCAN 1
-/*
- * 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 ieee80211com *ic, u_long cmd, caddr_t data)
-{
- 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;
-
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- return error;
- wreq.wi_len = 0;
- switch (wreq.wi_type) {
- case WI_RID_SERIALNO:
- /* nothing appropriate */
- break;
- case WI_RID_NODENAME:
- strcpy((char *)&wreq.wi_val[1], hostname);
- wreq.wi_val[0] = htole16(strlen(hostname));
- wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
- break;
- case WI_RID_CURRENT_SSID:
- if (ic->ic_state != IEEE80211_S_RUN) {
- wreq.wi_val[0] = 0;
- wreq.wi_len = 1;
- break;
- }
- wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen);
- memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid,
- ic->ic_bss->ni_esslen);
- wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2;
- break;
- case WI_RID_OWN_SSID:
- case WI_RID_DESIRED_SSID:
- wreq.wi_val[0] = htole16(ic->ic_des_esslen);
- memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
- wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
- break;
- case WI_RID_CURRENT_BSSID:
- if (ic->ic_state == IEEE80211_S_RUN)
- IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid);
- else
- memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
- wreq.wi_len = IEEE80211_ADDR_LEN / 2;
- break;
- case WI_RID_CHANNEL_LIST:
- memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
- /*
- * 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++)
- if (isset(ic->ic_chan_active, i)) {
- setbit((u_int8_t *)wreq.wi_val, j);
- wreq.wi_len = j / 16 + 1;
- }
- break;
- case WI_RID_OWN_CHNL:
- wreq.wi_val[0] = htole16(
- ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
- wreq.wi_len = 1;
- break;
- case WI_RID_CURRENT_CHAN:
- wreq.wi_val[0] = htole16(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- wreq.wi_len = 1;
- break;
- case WI_RID_COMMS_QUALITY:
- wreq.wi_val[0] = 0; /* quality */
- wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss));
- wreq.wi_val[2] = 0; /* noise */
- wreq.wi_len = 3;
- break;
- case WI_RID_PROMISC:
- wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0);
- wreq.wi_len = 1;
- break;
- case WI_RID_PORTTYPE:
- wreq.wi_val[0] = htole16(ic->ic_opmode);
- wreq.wi_len = 1;
- break;
- case WI_RID_MAC_NODE:
- IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr);
- wreq.wi_len = IEEE80211_ADDR_LEN / 2;
- break;
- case WI_RID_TX_RATE:
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- wreq.wi_val[0] = 0; /* auto */
- else
- wreq.wi_val[0] = htole16(
- (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] &
- IEEE80211_RATE_VAL) / 2);
- wreq.wi_len = 1;
- break;
- case WI_RID_CUR_TX_RATE:
- wreq.wi_val[0] = htole16(
- (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] &
- IEEE80211_RATE_VAL) / 2);
- wreq.wi_len = 1;
- break;
- case WI_RID_RTS_THRESH:
- wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
- wreq.wi_len = 1;
- break;
- case WI_RID_CREATE_IBSS:
- wreq.wi_val[0] =
- htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0);
- wreq.wi_len = 1;
- break;
- case WI_RID_MICROWAVE_OVEN:
- wreq.wi_val[0] = 0; /* no ... not supported */
- wreq.wi_len = 1;
- break;
- case WI_RID_ROAMING_MODE:
- wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */
- wreq.wi_len = 1;
- break;
- case WI_RID_SYSTEM_SCALE:
- wreq.wi_val[0] = htole16(1); /* low density ... not supp */
- wreq.wi_len = 1;
- break;
- case WI_RID_PM_ENABLED:
- wreq.wi_val[0] =
- htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
- wreq.wi_len = 1;
- break;
- case WI_RID_MAX_SLEEP:
- wreq.wi_val[0] = htole16(ic->ic_lintval);
- wreq.wi_len = 1;
- break;
- case WI_RID_CUR_BEACON_INT:
- wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval);
- wreq.wi_len = 1;
- break;
- case WI_RID_WEP_AVAIL:
- wreq.wi_val[0] = htole16(1); /* always available */
- wreq.wi_len = 1;
- break;
- case WI_RID_CNFAUTHMODE:
- wreq.wi_val[0] = htole16(1); /* TODO: open system only */
- wreq.wi_len = 1;
- break;
- case WI_RID_ENCRYPTION:
- wreq.wi_val[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_def_txkey);
- wreq.wi_len = 1;
- break;
- case WI_RID_DEFLT_CRYPT_KEYS:
- keys = (struct wi_ltv_keys *)&wreq;
- /* do not show keys to non-root user */
- error = suser(curthread);
- if (error) {
- memset(keys, 0, sizeof(*keys));
- error = 0;
- break;
- }
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- keys->wi_keys[i].wi_keylen =
- 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_keylen);
- }
- wreq.wi_len = sizeof(*keys) / 2;
- break;
- case WI_RID_MAX_DATALEN:
- 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:
- /*
- * 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:
- /* 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_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;
- 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;
- }
- if (error == 0) {
- wreq.wi_len++;
- error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
- }
- return error;
-}
-
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
-{
-#define IEEERATE(_ic,_m,_i) \
- ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
- int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
- for (i = 0; i < nrates; i++)
- if (IEEERATE(ic, mode, i) == rate)
- return i;
- return -1;
-#undef IEEERATE
-}
-
-/*
- * Prepare to do a user-initiated scan for AP's. If no
- * current/default channel is setup or the current channel
- * is invalid then pick the first available channel from
- * the active list as the place to start the scan.
- */
-static int
-ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[])
-{
-
- /*
- * 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;
- memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
- /*
- * 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.
- */
- /* XXX handle proberequest case */
- ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */
- return 0;
-}
-
-int
-ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data)
-{
- struct ifnet *ifp = ic->ic_ifp;
- int i, j, len, error, rate;
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_ltv_keys *keys;
- struct wi_req wreq;
- u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
-
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- return error;
- len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
- switch (wreq.wi_type) {
- case WI_RID_SERIALNO:
- case WI_RID_NODENAME:
- return EPERM;
- case WI_RID_CURRENT_SSID:
- return EPERM;
- case WI_RID_OWN_SSID:
- case WI_RID_DESIRED_SSID:
- if (le16toh(wreq.wi_val[0]) * 2 > len ||
- le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
- error = ENOSPC;
- break;
- }
- memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
- ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2;
- memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen);
- error = ENETRESET;
- break;
- case WI_RID_CURRENT_BSSID:
- return EPERM;
- case WI_RID_OWN_CHNL:
- if (len != 2)
- return EINVAL;
- i = le16toh(wreq.wi_val[0]);
- if (i < 0 ||
- i > IEEE80211_CHAN_MAX ||
- isclr(ic->ic_chan_active, i))
- return EINVAL;
- ic->ic_ibss_chan = &ic->ic_channels[i];
- 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:
- return EPERM;
- case WI_RID_COMMS_QUALITY:
- return EPERM;
- case WI_RID_PROMISC:
- if (len != 2)
- return EINVAL;
- if (ifp->if_flags & IFF_PROMISC) {
- if (wreq.wi_val[0] == 0) {
- ifp->if_flags &= ~IFF_PROMISC;
- error = ENETRESET;
- }
- } else {
- if (wreq.wi_val[0] != 0) {
- ifp->if_flags |= IFF_PROMISC;
- error = ENETRESET;
- }
- }
- break;
- case WI_RID_PORTTYPE:
- if (len != 2)
- return EINVAL;
- switch (le16toh(wreq.wi_val[0])) {
- case IEEE80211_M_STA:
- break;
- case IEEE80211_M_IBSS:
- if (!(ic->ic_caps & IEEE80211_C_IBSS))
- return EINVAL;
- break;
- case IEEE80211_M_AHDEMO:
- if (ic->ic_phytype != IEEE80211_T_DS ||
- !(ic->ic_caps & IEEE80211_C_AHDEMO))
- return EINVAL;
- break;
- case IEEE80211_M_HOSTAP:
- if (!(ic->ic_caps & IEEE80211_C_HOSTAP))
- return EINVAL;
- break;
- default:
- return EINVAL;
- }
- if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
- ic->ic_opmode = le16toh(wreq.wi_val[0]);
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- }
- break;
-#if 0
- case WI_RID_MAC_NODE:
- if (len != IEEE80211_ADDR_LEN)
- return EINVAL;
- IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val);
- /* if_init will copy lladdr into ic_myaddr */
- error = ENETRESET;
- break;
-#endif
- case WI_RID_TX_RATE:
- if (len != 2)
- return EINVAL;
- if (wreq.wi_val[0] == 0) {
- /* auto */
- ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- break;
- }
- rate = 2 * le16toh(wreq.wi_val[0]);
- if (ic->ic_curmode == IEEE80211_MODE_AUTO) {
- /*
- * In autoselect mode search for the rate. We take
- * the first instance which may not be right, but we
- * are limited by the interface. Note that we also
- * lock the mode to insure the rate is meaningful
- * when it is used.
- */
- for (j = IEEE80211_MODE_11A;
- j < IEEE80211_MODE_MAX; j++) {
- if ((ic->ic_modecaps & (1<<j)) == 0)
- continue;
- i = findrate(ic, j, rate);
- if (i != -1) {
- /* lock mode too */
- ic->ic_curmode = j;
- goto setrate;
- }
- }
- } else {
- i = findrate(ic, ic->ic_curmode, rate);
- if (i != -1)
- goto setrate;
- }
- return EINVAL;
- setrate:
- ic->ic_fixed_rate = i;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- break;
- case WI_RID_CUR_TX_RATE:
- return EPERM;
- case WI_RID_RTS_THRESH:
- if (len != 2)
- return EINVAL;
- if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
- return EINVAL; /* TODO: RTS */
- break;
- case WI_RID_CREATE_IBSS:
- if (len != 2)
- return EINVAL;
- if (wreq.wi_val[0] != 0) {
- if ((ic->ic_caps & IEEE80211_C_IBSS) == 0)
- return EINVAL;
- if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
- ic->ic_flags |= IEEE80211_F_IBSSON;
- if (ic->ic_opmode == IEEE80211_M_IBSS &&
- ic->ic_state == IEEE80211_S_SCAN)
- 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 = IS_UP_AUTO(ic) ? ENETRESET : 0;
- }
- }
- }
- break;
- case WI_RID_MICROWAVE_OVEN:
- if (len != 2)
- return EINVAL;
- if (wreq.wi_val[0] != 0)
- return EINVAL; /* not supported */
- break;
- case WI_RID_ROAMING_MODE:
- if (len != 2)
- return EINVAL;
- 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)
- return EINVAL;
- if (le16toh(wreq.wi_val[0]) != 1)
- return EINVAL; /* not supported */
- break;
- case WI_RID_PM_ENABLED:
- if (len != 2)
- return EINVAL;
- if (wreq.wi_val[0] != 0) {
- if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
- return EINVAL;
- if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- 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 = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- }
- }
- break;
- case WI_RID_MAX_SLEEP:
- if (len != 2)
- return EINVAL;
- ic->ic_lintval = le16toh(wreq.wi_val[0]);
- if (ic->ic_flags & IEEE80211_F_PMGTON)
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- break;
- case WI_RID_CUR_BEACON_INT:
- return EPERM;
- case WI_RID_WEP_AVAIL:
- return EPERM;
- case WI_RID_CNFAUTHMODE:
- if (len != 2)
- return EINVAL;
- 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)
- return EINVAL;
- if (wreq.wi_val[0] != 0) {
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
- return EINVAL;
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- error = ENETRESET;
- }
- } else {
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
- error = ENETRESET;
- }
- }
- break;
- case WI_RID_TX_CRYPT_KEY:
- if (len != 2)
- return EINVAL;
- i = le16toh(wreq.wi_val[0]);
- if (i >= IEEE80211_WEP_NKID)
- return EINVAL;
- 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))
- return EINVAL;
- keys = (struct wi_ltv_keys *)&wreq;
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- len = le16toh(keys->wi_keys[i].wi_keylen);
- if (len != 0 && len < IEEE80211_WEP_KEYLEN)
- return EINVAL;
- if (len > IEEE80211_KEYBUF_SIZE)
- return EINVAL;
- }
- 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);
- 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;
- case WI_RID_MAX_DATALEN:
- if (len != 2)
- return EINVAL;
- len = le16toh(wreq.wi_val[0]);
- if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
- return EINVAL;
- ic->ic_fragthreshold = len;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- break;
- case WI_RID_IFACE_STATS:
- error = EPERM;
- break;
- case WI_RID_SCAN_REQ: /* XXX wicontrol */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- break;
- error = ieee80211_setupscan(ic, ic->ic_chan_avail);
- if (error == 0)
- error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- break;
- case WI_RID_SCAN_APS:
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- break;
- len--; /* XXX: tx rate? */
- /* FALLTHRU */
- case WI_RID_CHANNEL_LIST:
- 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++) {
- if ((j / 8) >= len)
- break;
- if (isclr((u_int8_t *)wreq.wi_val, j))
- continue;
- if (isclr(ic->ic_chan_active, i)) {
- if (wreq.wi_type != WI_RID_CHANNEL_LIST)
- continue;
- if (isclr(ic->ic_chan_avail, i))
- return EPERM;
- }
- setbit(chanlist, i);
- }
- error = ieee80211_setupscan(ic, chanlist);
- if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
- /* NB: ignore error from ieee80211_setupscan */
- error = ENETRESET;
- } else if (error == 0)
- error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- break;
- default:
- error = EINVAL;
- break;
- }
- if (error == ENETRESET && !IS_UP_AUTO(ic))
- error = 0;
- return error;
-}
+static struct ieee80211_channel *findchannel(struct ieee80211com *,
+ int ieee, int mode);
static int
cap2cipher(int flag)
@@ -859,7 +119,7 @@
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) {
+ if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
/* NB: only root can read key data */
ik.ik_keyrsc = wk->wk_keyrsc;
ik.ik_keytsc = wk->wk_keytsc;
@@ -892,37 +152,21 @@
static int
ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
{
- struct ieee80211req_chaninfo chans; /* XXX off stack? */
- int i, space;
+ int 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]);
+ ic_chans[ic->ic_nchans]);
if (space > ireq->i_len)
space = ireq->i_len;
- return copyout(&chans, ireq->i_data, space);
+ /* XXX assumes compatible layout */
+ return copyout(&ic->ic_nchans, ireq->i_data, space);
}
static int
-ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq)
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
{
struct ieee80211_node *ni;
- struct ieee80211req_wpaie wpaie;
+ struct ieee80211req_wpaie2 wpaie;
int error;
if (ireq->i_len < IEEE80211_ADDR_LEN)
@@ -932,7 +176,7 @@
return error;
ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT; /* XXX */
memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
if (ni->ni_wpa_ie != NULL) {
int ielen = ni->ni_wpa_ie[1] + 2;
@@ -940,9 +184,29 @@
ielen = sizeof(wpaie.wpa_ie);
memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
}
+ if (req == IEEE80211_IOC_WPAIE2) {
+ memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
+ if (ni->ni_rsn_ie != NULL) {
+ int ielen = ni->ni_rsn_ie[1] + 2;
+ if (ielen > sizeof(wpaie.rsn_ie))
+ ielen = sizeof(wpaie.rsn_ie);
+ memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
+ }
+ if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
+ ireq->i_len = sizeof(struct ieee80211req_wpaie2);
+ } else {
+ /* compatibility op, may overwrite wpa ie */
+ /* XXX check ic_flags? */
+ if (ni->ni_rsn_ie != NULL) {
+ int ielen = ni->ni_rsn_ie[1] + 2;
+ if (ielen > sizeof(wpaie.wpa_ie))
+ ielen = sizeof(wpaie.wpa_ie);
+ memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
+ }
+ if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
+ ireq->i_len = sizeof(struct ieee80211req_wpaie);
+ }
ieee80211_free_node(ni);
- if (ireq->i_len > sizeof(wpaie))
- ireq->i_len = sizeof(wpaie);
return copyout(&wpaie, ireq->i_data, ireq->i_len);
}
@@ -950,7 +214,7 @@
ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
- u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
int error;
@@ -961,102 +225,276 @@
return error;
ni = ieee80211_find_node(&ic->ic_sta, macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return EINVAL;
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,
+ error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off,
ireq->i_len - off);
ieee80211_free_node(ni);
return error;
}
+static __inline uint8_t *
+copyie(uint8_t *cp, const uint8_t *ie)
+{
+ if (ie != NULL) {
+ memcpy(cp, ie, 2+ie[1]);
+ cp += 2+ie[1];
+ }
+ return cp;
+}
+
+#ifdef COMPAT_FREEBSD6
+#define IEEE80211_IOC_SCAN_RESULTS_OLD 24
+
+struct scan_result_old {
+ uint16_t isr_len; /* length (mult of 4) */
+ uint16_t isr_freq; /* MHz */
+ uint16_t isr_flags; /* channel flags */
+ uint8_t isr_noise;
+ uint8_t isr_rssi;
+ uint8_t isr_intval; /* beacon interval */
+ uint8_t isr_capinfo; /* capabilities */
+ uint8_t isr_erp; /* ERP element */
+ uint8_t isr_bssid[IEEE80211_ADDR_LEN];
+ uint8_t isr_nrates;
+ uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
+ uint8_t isr_ssid_len; /* SSID length */
+ uint8_t isr_ie_len; /* IE length */
+ uint8_t isr_pad[5];
+ /* variable length SSID followed by IE data */
+};
+
+struct oscanreq {
+ struct scan_result_old *sr;
+ size_t space;
+};
+
+static size_t
+old_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
+{
+ size_t len;
+
+ *ielen = 0;
+ if (se->se_wpa_ie != NULL)
+ *ielen += 2+se->se_wpa_ie[1];
+ if (se->se_wme_ie != NULL)
+ *ielen += 2+se->se_wme_ie[1];
+ /*
+ * NB: ie's can be no more than 255 bytes and the max 802.11
+ * packet is <3Kbytes so we are sure this doesn't overflow
+ * 16-bits; if this is a concern we can drop the ie's.
+ */
+ len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
+ return roundup(len, sizeof(uint32_t));
+}
+
static void
-get_scan_result(struct ieee80211req_scan_result *sr,
- const struct ieee80211_node *ni)
+old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
{
- struct ieee80211com *ic = ni->ni_ic;
- u_int ielen = 0;
+ struct oscanreq *req = arg;
+ int ielen;
+
+ req->space += old_scan_space(se, &ielen);
+}
+
+static void
+old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
+{
+ struct oscanreq *req = arg;
+ struct scan_result_old *sr;
+ int ielen, len, nr, nxr;
+ uint8_t *cp;
+
+ len = old_scan_space(se, &ielen);
+ if (len > req->space)
+ return;
+ sr = req->sr;
memset(sr, 0, sizeof(*sr));
- sr->isr_ssid_len = ni->ni_esslen;
- if (ni->ni_wpa_ie != NULL)
- ielen += 2+ni->ni_wpa_ie[1];
- if (ni->ni_wme_ie != NULL)
- ielen += 2+ni->ni_wme_ie[1];
+ sr->isr_ssid_len = se->se_ssid[1];
+ /* NB: beware of overflow, isr_ie_len is 8 bits */
+ sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
+ sr->isr_len = len;
+ sr->isr_freq = se->se_chan->ic_freq;
+ sr->isr_flags = se->se_chan->ic_flags;
+ sr->isr_rssi = se->se_rssi;
+ sr->isr_noise = se->se_noise;
+ sr->isr_intval = se->se_intval;
+ sr->isr_capinfo = se->se_capinfo;
+ sr->isr_erp = se->se_erp;
+ IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
+ nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
+ memcpy(sr->isr_rates, se->se_rates+2, nr);
+ nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
+ memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
+ sr->isr_nrates = nr + nxr;
+
+ cp = (uint8_t *)(sr+1);
+ memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
+ cp += sr->isr_ssid_len;
+ if (sr->isr_ie_len) {
+ cp = copyie(cp, se->se_wpa_ie);
+ cp = copyie(cp, se->se_wme_ie);
+ }
+
+ req->space -= len;
+ req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
+}
+
+static int
+old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct oscanreq req;
+ int error;
+
+ if (ireq->i_len < sizeof(struct scan_result_old))
+ return EFAULT;
+
+ error = 0;
+ req.space = 0;
+ ieee80211_scan_iterate(ic, old_get_scan_space, &req);
+ if (req.space > ireq->i_len)
+ req.space = ireq->i_len;
+ if (req.space > 0) {
+ size_t space;
+ void *p;
+
+ space = req.space;
+ /* XXX M_WAITOK after driver lock released */
+ MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
+ if (p == NULL)
+ return ENOMEM;
+ req.sr = p;
+ ieee80211_scan_iterate(ic, old_get_scan_result, &req);
+ ireq->i_len = space - req.space;
+ error = copyout(p, ireq->i_data, ireq->i_len);
+ FREE(p, M_TEMP);
+ } else
+ ireq->i_len = 0;
+
+ return error;
+}
+#endif /* COMPAT_FREEBSD6 */
+struct scanreq {
+ struct ieee80211req_scan_result *sr;
+ size_t space;
+};
+
+static size_t
+scan_space(const struct ieee80211_scan_entry *se, int *ielen)
+{
+ size_t len;
+
+ *ielen = 0;
+ if (se->se_wpa_ie != NULL)
+ *ielen += 2+se->se_wpa_ie[1];
+ if (se->se_rsn_ie != NULL)
+ *ielen += 2+se->se_rsn_ie[1];
+ if (se->se_wme_ie != NULL)
+ *ielen += 2+se->se_wme_ie[1];
+ if (se->se_ath_ie != NULL)
+ *ielen += 2+se->se_ath_ie[1];
/*
- * The value sr->isr_ie_len is defined as a uint8_t, so we
- * need to be careful to avoid an integer overflow. If the
- * value would overflow, we will set isr_ie_len to zero, and
- * ieee80211_ioctl_getscanresults (below) will avoid copying
- * the (overflowing) data.
+ * NB: ie's can be no more than 255 bytes and the max 802.11
+ * packet is <3Kbytes so we are sure this doesn't overflow
+ * 16-bits; if this is a concern we can drop the ie's.
*/
- if (ielen > 255)
- ielen = 0;
+ len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + *ielen;
+ return roundup(len, sizeof(uint32_t));
+}
+
+static void
+get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
+{
+ struct scanreq *req = arg;
+ int ielen;
+
+ req->space += scan_space(se, &ielen);
+}
+
+static void
+get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
+{
+ struct scanreq *req = arg;
+ struct ieee80211req_scan_result *sr;
+ int ielen, len, nr, nxr;
+ uint8_t *cp;
+
+ len = scan_space(se, &ielen);
+ if (len > req->space)
+ return;
+
+ sr = req->sr;
+ KASSERT(len <= 65535 && ielen <= 65535,
+ ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
+ sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
sr->isr_ie_len = ielen;
- 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);
+ sr->isr_len = len;
+ sr->isr_freq = se->se_chan->ic_freq;
+ sr->isr_flags = se->se_chan->ic_flags;
+ sr->isr_rssi = se->se_rssi;
+ sr->isr_noise = se->se_noise;
+ sr->isr_intval = se->se_intval;
+ sr->isr_capinfo = se->se_capinfo;
+ sr->isr_erp = se->se_erp;
+ IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
+ nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
+ memcpy(sr->isr_rates, se->se_rates+2, nr);
+ nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
+ memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
+ sr->isr_nrates = nr + nxr;
+
+ sr->isr_ssid_len = se->se_ssid[1];
+ cp = ((uint8_t *)sr) + sr->isr_ie_off;
+ memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
+
+ if (ielen) {
+ cp += sr->isr_ssid_len;
+ cp = copyie(cp, se->se_wpa_ie);
+ cp = copyie(cp, se->se_rsn_ie);
+ cp = copyie(cp, se->se_wme_ie);
+ cp = copyie(cp, se->se_ath_ie);
+ cp = copyie(cp, se->se_htcap_ie);
+ }
+
+ req->space -= len;
+ req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
}
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;
+ struct scanreq req;
+ int error;
+
+ if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
+ return EFAULT;
- 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;
- cp = (u_int8_t *)(sr+1);
- memcpy(cp, ni->ni_essid, ni->ni_esslen);
- cp += ni->ni_esslen;
- if (sr->isr_ie_len > 0 && 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 (sr->isr_ie_len > 0 && 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;
- p += sr->isr_len;
- space -= sr->isr_len;
- }
- ireq->i_len -= space;
+ req.space = 0;
+ ieee80211_scan_iterate(ic, get_scan_space, &req);
+ if (req.space > ireq->i_len)
+ req.space = ireq->i_len;
+ if (req.space > 0) {
+ size_t space;
+ void *p;
+
+ space = req.space;
+ /* XXX M_WAITOK after driver lock released */
+ MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
+ if (p == NULL)
+ return ENOMEM;
+ req.sr = p;
+ ieee80211_scan_iterate(ic, get_scan_result, &req);
+ ireq->i_len = space - req.space;
+ error = copyout(p, ireq->i_data, ireq->i_len);
+ FREE(p, M_TEMP);
+ } else
+ ireq->i_len = 0;
+
return error;
}
@@ -1072,10 +510,14 @@
*ielen = 0;
if (ni->ni_wpa_ie != NULL)
*ielen += 2+ni->ni_wpa_ie[1];
+ if (ni->ni_rsn_ie != NULL)
+ *ielen += 2+ni->ni_rsn_ie[1];
if (ni->ni_wme_ie != NULL)
*ielen += 2+ni->ni_wme_ie[1];
+ if (ni->ni_ath_ie != NULL)
+ *ielen += 2+ni->ni_ath_ie[1];
return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
- sizeof(u_int32_t));
+ sizeof(uint32_t));
}
static void
@@ -1098,7 +540,7 @@
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211req_sta_info *si;
size_t ielen, len;
- u_int8_t *cp;
+ uint8_t *cp;
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
ni->ni_associd == 0) /* only associated stations */
@@ -1110,12 +552,14 @@
return;
si = req->si;
si->isi_len = len;
+ si->isi_ie_off = sizeof(struct ieee80211req_sta_info);
si->isi_ie_len = ielen;
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);
+ ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
+ si->isi_noise = 0; /* XXX */
si->isi_capinfo = ni->ni_capinfo;
si->isi_erp = ni->ni_erp;
IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
@@ -1124,6 +568,7 @@
si->isi_nrates = 15;
memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
si->isi_txrate = ni->ni_txrate;
+ si->isi_ie_len = ielen;
si->isi_associd = ni->ni_associd;
si->isi_txpower = ni->ni_txpower;
si->isi_vlan = ni->ni_vlan;
@@ -1131,8 +576,8 @@
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];
+ si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID];
+ si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID];
}
/* NB: leave all cases in case we relax ni_associd == 0 check */
if (ieee80211_node_is_authorized(ni))
@@ -1143,55 +588,95 @@
si->isi_inact = ic->ic_inact_init;
si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
- 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];
+ if (ielen) {
+ cp = ((uint8_t *)si) + si->isi_ie_off;
+ cp = copyie(cp, ni->ni_wpa_ie);
+ cp = copyie(cp, ni->ni_rsn_ie);
+ cp = copyie(cp, ni->ni_wme_ie);
+ cp = copyie(cp, ni->ni_ath_ie);
}
- req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len);
+ req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
req->space -= len;
}
static int
-ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
+ struct ieee80211_node *ni, int off)
{
struct stainforeq req;
+ size_t space;
+ void *p;
int error;
- if (ireq->i_len < sizeof(struct stainforeq))
- return EFAULT;
-
error = 0;
req.space = 0;
- ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
+ if (ni == NULL)
+ ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
+ else
+ get_sta_space(&req, ni);
if (req.space > ireq->i_len)
req.space = ireq->i_len;
if (req.space > 0) {
- size_t space;
- void *p;
-
space = req.space;
/* XXX M_WAITOK after driver lock released */
MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
- if (p == NULL)
- return ENOMEM;
+ if (p == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
req.si = p;
- ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
+ if (ni == NULL)
+ ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
+ else
+ get_sta_info(&req, ni);
ireq->i_len = space - req.space;
- error = copyout(p, ireq->i_data, ireq->i_len);
+ error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len);
FREE(p, M_TEMP);
} else
ireq->i_len = 0;
-
+bad:
+ if (ni != NULL)
+ ieee80211_free_node(ni);
return error;
}
static int
+ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ const int off = __offsetof(struct ieee80211req_sta_req, info);
+ struct ieee80211_node *ni;
+ int error;
+
+ if (ireq->i_len < sizeof(struct ieee80211req_sta_req))
+ return EFAULT;
+ error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
+ if (error != 0)
+ return error;
+ if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
+ ni = NULL;
+ } else {
+ ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ if (ni == NULL)
+ return EINVAL;
+ }
+ return getstainfo_common(ic, ireq, ni, off);
+}
+
+#ifdef COMPAT_FREEBSD6
+#define IEEE80211_IOC_STA_INFO_OLD 45
+
+static int
+old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
+ return EFAULT;
+ return getstainfo_common(ic, ireq, NULL, 0);
+}
+#endif /* COMPAT_FREEBSD6 */
+
+static int
ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -1263,6 +748,28 @@
}
/*
+ * Return the current ``state'' of an Atheros capbility.
+ * If associated in station mode report the negotiated
+ * setting. Otherwise report the current setting.
+ */
+static int
+getathcap(struct ieee80211com *ic, int cap)
+{
+ if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
+ return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
+ else
+ return (ic->ic_flags & cap) != 0;
+}
+
+static int
+ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(struct ieee80211_channel))
+ return EINVAL;
+ return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
+}
+
+/*
* When building the kernel with -O2 on the i386 architecture, gcc
* seems to want to inline this function into ieee80211_ioctl()
* (which is the only routine that calls it). When this happens,
@@ -1287,7 +794,7 @@
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];
+ uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
switch (ireq->i_type) {
@@ -1295,8 +802,8 @@
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);
+ ireq->i_len = ic->ic_des_ssid[0].len;
+ memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
break;
default:
ireq->i_len = ic->ic_bss->ni_esslen;
@@ -1323,7 +830,7 @@
return EINVAL;
len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
/* NB: only root can read WEP keys */
- if (suser(curthread) == 0) {
+ if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
} else {
bzero(tmpkey, len);
@@ -1457,8 +964,16 @@
ireq->i_data, ireq->i_len);
break;
case IEEE80211_IOC_WPAIE:
- error = ieee80211_ioctl_getwpaie(ic, ireq);
+ error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
+ break;
+ case IEEE80211_IOC_WPAIE2:
+ error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
break;
+#ifdef COMPAT_FREEBSD6
+ case IEEE80211_IOC_SCAN_RESULTS_OLD:
+ error = old_getscanresults(ic, ireq);
+ break;
+#endif
case IEEE80211_IOC_SCAN_RESULTS:
error = ieee80211_ioctl_getscanresults(ic, ireq);
break;
@@ -1471,6 +986,11 @@
case IEEE80211_IOC_STA_TXPOW:
error = ieee80211_ioctl_getstatxpow(ic, ireq);
break;
+#ifdef COMPAT_FREEBSD6
+ case IEEE80211_IOC_STA_INFO_OLD:
+ error = old_getstainfo(ic, ireq);
+ break;
+#endif
case IEEE80211_IOC_STA_INFO:
error = ieee80211_ioctl_getstainfo(ic, ireq);
break;
@@ -1482,15 +1002,51 @@
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;
+ 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;
+ case IEEE80211_IOC_PUREG:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+ break;
+ case IEEE80211_IOC_FF:
+ ireq->i_val = getathcap(ic, IEEE80211_F_FF);
+ break;
+ case IEEE80211_IOC_TURBOP:
+ ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
+ break;
+ case IEEE80211_IOC_BGSCAN:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
+ break;
+ case IEEE80211_IOC_BGSCAN_IDLE:
+ ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */
+ break;
+ case IEEE80211_IOC_BGSCAN_INTERVAL:
+ ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */
+ break;
+ case IEEE80211_IOC_SCANVALID:
+ ireq->i_val = ic->ic_scanvalid/hz; /* seconds */
break;
- case IEEE80211_IOC_BEACON_INTERVAL:
- /* NB: get from ic_bss for station mode */
- ireq->i_val = ic->ic_bss->ni_intval;
+ case IEEE80211_IOC_ROAM_RSSI_11A:
+ ireq->i_val = ic->ic_roam.rssi11a;
break;
- case IEEE80211_IOC_PUREG:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+ case IEEE80211_IOC_ROAM_RSSI_11B:
+ ireq->i_val = ic->ic_roam.rssi11bOnly;
+ break;
+ case IEEE80211_IOC_ROAM_RSSI_11G:
+ ireq->i_val = ic->ic_roam.rssi11b;
+ break;
+ case IEEE80211_IOC_ROAM_RATE_11A:
+ ireq->i_val = ic->ic_roam.rate11a;
+ break;
+ case IEEE80211_IOC_ROAM_RATE_11B:
+ ireq->i_val = ic->ic_roam.rate11bOnly;
+ break;
+ case IEEE80211_IOC_ROAM_RATE_11G:
+ ireq->i_val = ic->ic_roam.rate11b;
break;
case IEEE80211_IOC_MCAST_RATE:
ireq->i_val = ic->ic_mcast_rate;
@@ -1504,6 +1060,65 @@
case IEEE80211_IOC_BURST:
ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
break;
+ case IEEE80211_IOC_BMISSTHRESHOLD:
+ ireq->i_val = ic->ic_bmissthreshold;
+ break;
+ case IEEE80211_IOC_CURCHAN:
+ error = ieee80211_ioctl_getcurchan(ic, ireq);
+ break;
+ case IEEE80211_IOC_SHORTGI:
+ ireq->i_val = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
+ break;
+ case IEEE80211_IOC_AMPDU:
+ ireq->i_val = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
+ ireq->i_val |= 1;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
+ ireq->i_val |= 2;
+ break;
+ case IEEE80211_IOC_AMPDU_LIMIT:
+ ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */
+ break;
+ case IEEE80211_IOC_AMPDU_DENSITY:
+ ireq->i_val = ic->ic_ampdu_density;
+ break;
+ case IEEE80211_IOC_AMSDU:
+ ireq->i_val = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
+ ireq->i_val |= 1;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
+ ireq->i_val |= 2;
+ break;
+ case IEEE80211_IOC_AMSDU_LIMIT:
+ ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */
+ break;
+ case IEEE80211_IOC_PUREN:
+ ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
+ break;
+ case IEEE80211_IOC_DOTH:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
+ break;
+ case IEEE80211_IOC_HTCOMPAT:
+ ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ break;
+ case IEEE80211_IOC_INACTIVITY:
+ ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ break;
+ case IEEE80211_IOC_HTPROTMODE:
+ ireq->i_val = ic->ic_htprotmode;
+ break;
+ case IEEE80211_IOC_HTCONF:
+ if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
+ ireq->i_val = 1;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ ireq->i_val |= 2;
+ } else
+ ireq->i_val = 0;
+ break;
default:
error = EINVAL;
break;
@@ -1527,6 +1142,8 @@
return EINVAL;
if (ireq->i_len > IEEE80211_MAX_OPT_IE)
return EINVAL;
+ /* NB: data.length is validated by the wireless extensions code */
+ /* XXX M_WAITOK after driver lock released */
if (ireq->i_len > 0) {
MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
if (ie == NULL)
@@ -1555,7 +1172,7 @@
struct ieee80211req_key ik;
struct ieee80211_node *ni;
struct ieee80211_key *wk;
- u_int16_t kid;
+ uint16_t kid;
int error;
if (ireq->i_len != sizeof(ik))
@@ -1632,8 +1249,8 @@
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) {
+ /* XXX uint8_t -> uint16_t */
+ if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
struct ieee80211_node *ni;
if (ic->ic_opmode == IEEE80211_M_STA) {
@@ -1675,6 +1292,32 @@
ieee80211_node_leave(ic, ni);
}
+struct scanlookup {
+ const uint8_t *mac;
+ int esslen;
+ const uint8_t *essid;
+ const struct ieee80211_scan_entry *se;
+};
+
+/*
+ * Match mac address and any ssid.
+ */
+static void
+mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
+{
+ struct scanlookup *look = arg;
+
+ if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
+ return;
+ if (look->esslen != 0) {
+ if (se->se_ssid[1] != look->esslen)
+ return;
+ if (memcmp(look->essid, se->se_ssid+2, look->esslen))
+ return;
+ }
+ look->se = se;
+}
+
static int
ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
{
@@ -1689,31 +1332,21 @@
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? */
+ /* XXX ibss/ahdemo */
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ struct scanlookup lookup;
- if (mlme.im_ssid_len != 0) {
- /*
- * Desired ssid specified; must match both bssid and
- * ssid to distinguish ap advertising multiple ssid's.
- */
- ni = ieee80211_find_node_with_ssid(&ic->ic_scan,
- mlme.im_macaddr,
- mlme.im_ssid_len, mlme.im_ssid);
- } 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;
+ lookup.se = NULL;
+ lookup.mac = mlme.im_macaddr;
+ /* XXX use revised api w/ explicit ssid */
+ lookup.esslen = ic->ic_des_ssid[0].len;
+ lookup.essid = ic->ic_des_ssid[0].ssid;
+ ieee80211_scan_iterate(ic, mlmelookup, &lookup);
+ if (lookup.se != NULL &&
+ ieee80211_sta_join(ic, lookup.se))
+ return 0;
}
- break;
+ return EINVAL;
case IEEE80211_MLME_DISASSOC:
case IEEE80211_MLME_DEAUTH:
switch (ic->ic_opmode) {
@@ -1761,7 +1394,7 @@
static int
ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
{
- u_int8_t mac[IEEE80211_ADDR_LEN];
+ uint8_t mac[IEEE80211_ADDR_LEN];
const struct ieee80211_aclator *acl = ic->ic_acl;
int error;
@@ -1825,7 +1458,7 @@
{
struct ieee80211req_chanlist list;
u_char chanlist[IEEE80211_CHAN_BYTES];
- int i, j, error;
+ int i, j, nchan, error;
if (ireq->i_len != sizeof(list))
return EINVAL;
@@ -1841,34 +1474,31 @@
i = 1;
else
i = 0;
+ nchan = 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))
+ if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i)) {
setbit(chanlist, i);
+ nchan++;
+ }
}
- 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;
- }
- return EINVAL; /* no active channels */
-found:
- ;
- }
+ if (nchan == 0)
+ return EINVAL;
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */
+ isclr(chanlist, ic->ic_bsschan->ic_ieee))
+ ic->ic_bsschan = IEEE80211_CHAN_ANYC;
memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
- return IS_UP_AUTO(ic) ? ENETRESET : 0;
+ return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
}
static int
ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
- u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
int error;
/*
@@ -1998,15 +1628,273 @@
}
static int
+find11gchannel(struct ieee80211com *ic, int start, int freq)
+{
+ const struct ieee80211_channel *c;
+ int i;
+
+ for (i = start+1; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+ return 1;
+ }
+ /* NB: should not be needed but in case things are mis-sorted */
+ for (i = 0; i < start; i++) {
+ c = &ic->ic_channels[i];
+ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+ return 1;
+ }
+ return 0;
+}
+
+static struct ieee80211_channel *
+findchannel(struct ieee80211com *ic, int ieee, int mode)
+{
+ static const u_int chanflags[IEEE80211_MODE_MAX] = {
+ 0, /* IEEE80211_MODE_AUTO */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
+ IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */
+ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
+ IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */
+ IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
+ IEEE80211_CHAN_STURBO, /* IEEE80211_MODE_STURBO_A */
+ /* NB: handled specially below */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */
+ };
+ u_int modeflags;
+ int i;
+
+ KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
+ modeflags = chanflags[mode];
+ KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
+ ("no chanflags for mode %u", mode));
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+
+ if (c->ic_ieee != ieee)
+ continue;
+ if (mode == IEEE80211_MODE_AUTO) {
+ /* ignore turbo channels for autoselect */
+ if (IEEE80211_IS_CHAN_TURBO(c))
+ continue;
+ /*
+ * XXX special-case 11b/g channels so we
+ * always select the g channel if both
+ * are present.
+ * XXX prefer HT to non-HT?
+ */
+ if (!IEEE80211_IS_CHAN_B(c) ||
+ !find11gchannel(ic, i, c->ic_freq))
+ return c;
+ } else {
+ /* must check HT specially */
+ if ((mode == IEEE80211_MODE_11NA ||
+ mode == IEEE80211_MODE_11NG) &&
+ !IEEE80211_IS_CHAN_HT(c))
+ continue;
+ if ((c->ic_flags & modeflags) == modeflags)
+ return c;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Check the specified against any desired mode (aka netband).
+ * This is only used (presently) when operating in hostap mode
+ * to enforce consistency.
+ */
+static int
+check_mode_consistency(const struct ieee80211_channel *c, int mode)
+{
+ KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
+
+ switch (mode) {
+ case IEEE80211_MODE_11B:
+ return (IEEE80211_IS_CHAN_B(c));
+ case IEEE80211_MODE_11G:
+ return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
+ case IEEE80211_MODE_11A:
+ return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
+ case IEEE80211_MODE_STURBO_A:
+ return (IEEE80211_IS_CHAN_STURBO(c));
+ case IEEE80211_MODE_11NA:
+ return (IEEE80211_IS_CHAN_HTA(c));
+ case IEEE80211_MODE_11NG:
+ return (IEEE80211_IS_CHAN_HTG(c));
+ }
+ return 1;
+
+}
+
+/*
+ * Common code to set the current channel. If the device
+ * is up and running this may result in an immediate channel
+ * change or a kick of the state machine.
+ */
+static int
+setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+ int error;
+
+ if (c != IEEE80211_CHAN_ANYC) {
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ !check_mode_consistency(c, ic->ic_des_mode))
+ return EINVAL;
+ if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
+ return 0; /* NB: nothing to do */
+ }
+ ic->ic_des_chan = c;
+
+ error = 0;
+ if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
+ ic->ic_opmode == IEEE80211_M_WDS) &&
+ ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ /*
+ * Monitor and wds modes can switch directly.
+ */
+ ic->ic_curchan = ic->ic_des_chan;
+ if (ic->ic_state == IEEE80211_S_RUN)
+ ic->ic_set_channel(ic);
+ } else {
+ /*
+ * Need to go through the state machine in case we
+ * need to reassociate or the like. The state machine
+ * will pickup the desired channel and avoid scanning.
+ */
+ if (IS_UP_AUTO(ic))
+ error = ieee80211_init(ic, RESCAN);
+ else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ /*
+ * When not up+running and a real channel has
+ * been specified fix the current channel so
+ * there is immediate feedback; e.g. via ifconfig.
+ */
+ ic->ic_curchan = ic->ic_des_chan;
+ }
+ }
+ return error;
+}
+
+/*
+ * Old api for setting the current channel; this is
+ * deprecated because channel numbers are ambiguous.
+ */
+static int
+ieee80211_ioctl_setchannel(struct ieee80211com *ic,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_channel *c;
+
+ /* XXX 0xffff overflows 16-bit signed */
+ if (ireq->i_val == 0 ||
+ ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
+ c = IEEE80211_CHAN_ANYC;
+ } else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX) {
+ return EINVAL;
+ } else {
+ struct ieee80211_channel *c2;
+
+ c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
+ if (c == NULL) {
+ c = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_AUTO);
+ if (c == NULL)
+ return EINVAL;
+ }
+ /*
+ * Fine tune channel selection based on desired mode:
+ * if 11b is requested, find the 11b version of any
+ * 11g channel returned,
+ * if static turbo, find the turbo version of any
+ * 11a channel return,
+ * if 11na is requested, find the ht version of any
+ * 11a channel returned,
+ * if 11ng is requested, find the ht version of any
+ * 11g channel returned,
+ * otherwise we should be ok with what we've got.
+ */
+ switch (ic->ic_des_mode) {
+ case IEEE80211_MODE_11B:
+ if (IEEE80211_IS_CHAN_ANYG(c)) {
+ c2 = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_11B);
+ /* NB: should not happen, =>'s 11g w/o 11b */
+ if (c2 != NULL)
+ c = c2;
+ }
+ break;
+ case IEEE80211_MODE_TURBO_A:
+ if (IEEE80211_IS_CHAN_A(c)) {
+ c2 = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_TURBO_A);
+ if (c2 != NULL)
+ c = c2;
+ }
+ break;
+ case IEEE80211_MODE_11NA:
+ if (IEEE80211_IS_CHAN_A(c)) {
+ c2 = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_11NA);
+ if (c2 != NULL)
+ c = c2;
+ }
+ break;
+ case IEEE80211_MODE_11NG:
+ if (IEEE80211_IS_CHAN_ANYG(c)) {
+ c2 = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_11NG);
+ if (c2 != NULL)
+ c = c2;
+ }
+ break;
+ default: /* NB: no static turboG */
+ break;
+ }
+ }
+ return setcurchan(ic, c);
+}
+
+/*
+ * New/current api for setting the current channel; a complete
+ * channel description is provide so there is no ambiguity in
+ * identifying the channel.
+ */
+static int
+ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_channel chan, *c;
+ int error;
+
+ if (ireq->i_len != sizeof(chan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &chan, sizeof(chan));
+ if (error != 0)
+ return error;
+ /* XXX 0xffff overflows 16-bit signed */
+ if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
+ c = IEEE80211_CHAN_ANYC;
+ } else {
+ c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
+ if (c == NULL)
+ return EINVAL;
+ }
+ return setcurchan(ic, c);
+}
+
+static int
ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
{
- static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+ static const uint8_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];
+ uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
- u_int8_t tmpbssid[IEEE80211_ADDR_LEN];
+ uint8_t tmpbssid[IEEE80211_ADDR_LEN];
struct ieee80211_key *k;
int j, caps;
u_int kid;
@@ -2020,10 +1908,12 @@
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;
+ memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
+ ic->ic_des_ssid[0].len = ireq->i_len;
+ memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
+ ic->ic_des_nssid = (ireq->i_len > 0);
+ if (IS_UP_AUTO(ic))
+ error = ieee80211_init(ic, RESCAN);
break;
case IEEE80211_IOC_WEP:
switch (ireq->i_val) {
@@ -2040,7 +1930,8 @@
ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
break;
}
- error = ENETRESET;
+ if (IS_UP_AUTO(ic))
+ error = ieee80211_init(ic, RESCAN);
break;
case IEEE80211_IOC_WEPKEY:
kid = (u_int) ireq->i_val;
@@ -2069,16 +1960,13 @@
} else
error = EINVAL;
ieee80211_key_update_end(ic);
- if (!error) /* NB: for compatibility */
- error = ENETRESET;
break;
case IEEE80211_IOC_WEPTXKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID &&
- (u_int16_t) kid != IEEE80211_KEYIX_NONE)
+ (uint16_t) kid != IEEE80211_KEYIX_NONE)
return EINVAL;
ic->ic_def_txkey = kid;
- error = ENETRESET; /* push to hardware */
break;
case IEEE80211_IOC_AUTHMODE:
switch (ireq->i_val) {
@@ -2118,48 +2006,11 @@
ic->ic_bss->ni_authmode = ireq->i_val;
/* XXX mixed/mode/usage? */
ic->ic_auth = auth;
- error = ENETRESET;
+ if (IS_UP_AUTO(ic))
+ error = ieee80211_init(ic, RESCAN);
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;
- 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;
- }
- if (error == ENETRESET &&
- ic->ic_opmode == IEEE80211_M_MONITOR) {
- if (IS_UP(ic)) {
- /*
- * Monitor mode can switch directly.
- */
- if (ic->ic_des_chan != IEEE80211_CHAN_ANYC)
- ic->ic_curchan = ic->ic_des_chan;
- error = ic->ic_reset(ic->ic_ifp);
- } else
- error = 0;
- }
+ error = ieee80211_ioctl_setchannel(ic, ireq);
break;
case IEEE80211_IOC_POWERSAVE:
switch (ireq->i_val) {
@@ -2181,6 +2032,13 @@
error = EINVAL;
break;
}
+ if (error == ENETRESET) {
+ /*
+ * Switching in+out of power save mode
+ * should not require a state change.
+ */
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ }
break;
case IEEE80211_IOC_POWERSAVESLEEP:
if (ireq->i_val < 0)
@@ -2200,14 +2058,15 @@
return EINVAL;
ic->ic_protmode = ireq->i_val;
/* NB: if not operating in 11g this can wait */
- if (ic->ic_curmode == IEEE80211_MODE_11G)
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
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))
+ 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;
@@ -2268,7 +2127,7 @@
ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
break;
}
- error = ENETRESET; /* XXX? */
+ error = ENETRESET;
break;
case IEEE80211_IOC_WME:
if (ireq->i_val) {
@@ -2277,7 +2136,8 @@
ic->ic_flags |= IEEE80211_F_WME;
} else
ic->ic_flags &= ~IEEE80211_F_WME;
- error = ENETRESET; /* XXX maybe not for station? */
+ if (IS_UP_AUTO(ic))
+ error = ieee80211_init(ic, 0);
break;
case IEEE80211_IOC_HIDESSID:
if (ireq->i_val)
@@ -2340,8 +2200,8 @@
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);
+ ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
+ ((uint16_t) ireq->i_len);
break;
case IEEE80211_IOC_KEYMGTALGS:
/* XXX check */
@@ -2364,17 +2224,21 @@
ic->ic_flags &= ~IEEE80211_F_DESBSSID;
else
ic->ic_flags |= IEEE80211_F_DESBSSID;
- error = ENETRESET;
+ if (IS_UP_AUTO(ic))
+ error = ieee80211_init(ic, RESCAN);
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);
+ if (!IS_UP(ic))
+ return EINVAL;
+ (void) ieee80211_start_scan(ic,
+ IEEE80211_SCAN_ACTIVE |
+ IEEE80211_SCAN_NOPICK |
+ IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
+ /* XXX use ioctl params */
+ ic->ic_des_nssid, ic->ic_des_ssid);
break;
case IEEE80211_IOC_ADDMAC:
case IEEE80211_IOC_DELMAC:
@@ -2425,9 +2289,72 @@
else
ic->ic_flags &= ~IEEE80211_F_PUREG;
/* NB: reset only if we're operating on an 11g channel */
- if (ic->ic_curmode == IEEE80211_MODE_11G)
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
error = ENETRESET;
break;
+ case IEEE80211_IOC_FF:
+ if (ireq->i_val) {
+ if ((ic->ic_caps & IEEE80211_C_FF) == 0)
+ return EINVAL;
+ ic->ic_flags |= IEEE80211_F_FF;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_FF;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_TURBOP:
+ if (ireq->i_val) {
+ if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
+ return EINVAL;
+ ic->ic_flags |= IEEE80211_F_TURBOP;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_TURBOP;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_BGSCAN:
+ if (ireq->i_val) {
+ if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
+ return EINVAL;
+ ic->ic_flags |= IEEE80211_F_BGSCAN;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ break;
+ case IEEE80211_IOC_BGSCAN_IDLE:
+ if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
+ ic->ic_bgscanidle = ireq->i_val*hz/1000;
+ else
+ error = EINVAL;
+ break;
+ case IEEE80211_IOC_BGSCAN_INTERVAL:
+ if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
+ ic->ic_bgscanintvl = ireq->i_val*hz;
+ else
+ error = EINVAL;
+ break;
+ case IEEE80211_IOC_SCANVALID:
+ if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
+ ic->ic_scanvalid = ireq->i_val*hz;
+ else
+ error = EINVAL;
+ break;
+ case IEEE80211_IOC_ROAM_RSSI_11A:
+ ic->ic_roam.rssi11a = ireq->i_val;
+ break;
+ case IEEE80211_IOC_ROAM_RSSI_11B:
+ ic->ic_roam.rssi11bOnly = ireq->i_val;
+ break;
+ case IEEE80211_IOC_ROAM_RSSI_11G:
+ ic->ic_roam.rssi11b = ireq->i_val;
+ break;
+ case IEEE80211_IOC_ROAM_RATE_11A:
+ ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
+ break;
+ case IEEE80211_IOC_ROAM_RATE_11B:
+ ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
+ break;
+ case IEEE80211_IOC_ROAM_RATE_11G:
+ ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
+ break;
case IEEE80211_IOC_MCAST_RATE:
ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
break;
@@ -2450,12 +2377,146 @@
ic->ic_flags &= ~IEEE80211_F_BURST;
error = ENETRESET; /* XXX maybe not for station? */
break;
+ case IEEE80211_IOC_BMISSTHRESHOLD:
+ if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HWBMISS_MAX))
+ return EINVAL;
+ ic->ic_bmissthreshold = ireq->i_val;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_CURCHAN:
+ error = ieee80211_ioctl_setcurchan(ic, ireq);
+ break;
+ case IEEE80211_IOC_SHORTGI:
+ if (ireq->i_val) {
+#define IEEE80211_HTCAP_SHORTGI \
+ (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
+ if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
+ return EINVAL;
+ if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
+ ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
+ ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+#undef IEEE80211_HTCAP_SHORTGI
+ } else
+ ic->ic_flags_ext &=
+ ~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
+ /* XXX kick state machine? */
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_AMPDU:
+ if (ireq->i_val) {
+ if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ if (ireq->i_val & 2)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ } else
+ ic->ic_flags_ext &=
+ ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
+ /* NB: reset only if we're operating on an 11n channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_AMPDU_LIMIT:
+ /* XXX validate */
+ ic->ic_ampdu_limit = ireq->i_val;
+ break;
+ case IEEE80211_IOC_AMPDU_DENSITY:
+ /* XXX validate */
+ ic->ic_ampdu_density = ireq->i_val;
+ break;
+ case IEEE80211_IOC_AMSDU:
+ if (ireq->i_val) {
+ if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ if (ireq->i_val & 2)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ } else
+ ic->ic_flags_ext &=
+ ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
+ /* NB: reset only if we're operating on an 11n channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_AMSDU_LIMIT:
+ /* XXX validate */
+ ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */
+ break;
+ case IEEE80211_IOC_PUREN:
+ if (ireq->i_val) {
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+ return EINVAL;
+ ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
+ } else
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
+ /* NB: reset only if we're operating on an 11n channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_DOTH:
+ if (ireq->i_val) {
+#if 0
+ /* XXX no capability */
+ if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
+ return EINVAL;
+#endif
+ ic->ic_flags |= IEEE80211_F_DOTH;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_DOTH;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_HTCOMPAT:
+ if (ireq->i_val) {
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+ return EINVAL;
+ ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
+ } else
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
+ /* NB: reset only if we're operating on an 11n channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_INACTIVITY:
+ if (ireq->i_val)
+ ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
+ else
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
+ break;
+ case IEEE80211_IOC_HTPROTMODE:
+ if (ireq->i_val > IEEE80211_PROT_RTSCTS)
+ return EINVAL;
+ ic->ic_htprotmode = ireq->i_val ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
+ /* NB: if not operating in 11n this can wait */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ error = ERESTART;
+ break;
+ case IEEE80211_IOC_HTCONF:
+ if (ireq->i_val & 1)
+ ic->ic_flags_ext |= IEEE80211_FEXT_HT;
+ else
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
+ if (ireq->i_val & 2)
+ ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
+ else
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
+ error = ENETRESET;
+ break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET && !IS_UP_AUTO(ic))
- error = 0;
+ if (error == ENETRESET)
+ error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
return error;
}
@@ -2478,20 +2539,11 @@
(struct ieee80211req *) data);
break;
case SIOCS80211:
- error = suser(curthread);
+ error = priv_check(curthread, PRIV_NET80211_MANAGE);
if (error == 0)
error = ieee80211_ioctl_set80211(ic, cmd,
(struct ieee80211req *) data);
break;
- case SIOCGIFGENERIC:
- error = ieee80211_cfgget(ic, cmd, data);
- break;
- case SIOCSIFGENERIC:
- error = suser(curthread);
- if (error)
- break;
- error = ieee80211_cfgset(ic, cmd, data);
- break;
case SIOCG80211STATS:
ifr = (struct ifreq *)data;
copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
@@ -2533,10 +2585,10 @@
if (ipx_nullhost(*ina))
ina->x_host = *(union ipx_host *)
- IFP2ENADDR(ifp);
+ IF_LLADDR(ifp);
else
bcopy((caddr_t) ina->x_host.c_host,
- (caddr_t) IFP2ENADDR(ifp),
+ (caddr_t) IF_LLADDR(ifp),
ETHER_ADDR_LEN);
/* fall thru... */
}
Index: ieee80211_freebsd.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_freebsd.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_freebsd.h -L sys/net80211/ieee80211_freebsd.h -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_freebsd.h
+++ sys/net80211/ieee80211_freebsd.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,8 +10,6 @@
* 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
@@ -24,11 +22,24 @@
* (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: src/sys/net80211/ieee80211_freebsd.h,v 1.5.2.1 2005/09/03 22:40:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_freebsd.h,v 1.15.2.1 2007/11/11 17:44:35 sam Exp $
*/
#ifndef _NET80211_IEEE80211_FREEBSD_H_
#define _NET80211_IEEE80211_FREEBSD_H_
+#ifdef _KERNEL
+/*
+ * Common state locking definitions.
+ */
+typedef struct mtx ieee80211_com_lock_t;
+#define IEEE80211_LOCK_INIT(_ic, _name) \
+ mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF)
+#define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock)
+#define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_comlock)
+#define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_comlock)
+#define IEEE80211_LOCK_ASSERT(_ic) \
+ mtx_assert(&(_ic)->ic_comlock, MA_OWNED)
+
/*
* Beacon locking definitions.
*/
@@ -61,7 +72,7 @@
*/
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)
+ mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node 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)
@@ -114,6 +125,28 @@
(_qlen) = ++(_ni)->ni_savedq.ifq_len; \
} while (0)
+#define IEEE80211_TAPQ_INIT(_tap) do { \
+ mtx_init(&(tap)->txa_q.ifq_mtx, "ampdu tx queue", NULL, MTX_DEF); \
+ (_tap)->txa_q.ifq_maxlen = IEEE80211_AGGR_BAWMAX; \
+} while (0)
+#define IEEE80211_TAPQ_DESTROY(_tap) \
+ mtx_destroy(&(_tap)->txa_q.ifq_mtx)
+
+#ifndef IF_PREPEND_LIST
+#define _IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do { \
+ (mtail)->m_nextpkt = (ifq)->ifq_head; \
+ if ((ifq)->ifq_tail == NULL) \
+ (ifq)->ifq_tail = (mtail); \
+ (ifq)->ifq_head = (mhead); \
+ (ifq)->ifq_len += (mcount); \
+} while (0)
+#define IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do { \
+ IF_LOCK(ifq); \
+ _IF_PREPEND_LIST(ifq, mhead, mtail, mcount); \
+ IF_UNLOCK(ifq); \
+} while (0)
+#endif /* IF_PREPEND_LIST */
+
/*
* 802.1x MAC ACL database locking definitions.
*/
@@ -148,10 +181,30 @@
int ieee80211_node_dectestref(struct ieee80211_node *ni);
#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
-struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
+struct ifqueue;
+void ieee80211_drain_ifq(struct ifqueue *);
+
+#define msecs_to_ticks(ms) (((ms)*hz)/1000)
+#define ticks_to_msecs(t) ((t) / hz)
+#define time_after(a,b) ((long)(b) - (long)(a) < 0)
+#define time_before(a,b) time_after(b,a)
+#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
+#define time_before_eq(a,b) time_after_eq(b,a)
+
+struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
+
+/* tx path usage */
#define M_LINK0 M_PROTO1 /* WEP requested */
#define M_PWR_SAV M_PROTO4 /* bypass PS handling */
#define M_MORE_DATA M_PROTO5 /* more data frames to follow */
+#define M_FF 0x20000 /* fast frame */
+#define M_TXCB 0x40000 /* do tx complete callback */
+#define M_80211_TX (0x60000|M_PROTO1|M_WME_AC_MASK|M_PROTO4|M_PROTO5)
+
+/* rx path usage */
+#define M_AMPDU M_PROTO1 /* A-MPDU processing done */
+#define M_WEP M_PROTO2 /* WEP done by hardware */
+#define M_80211_RX (M_AMPDU|M_WEP)
/*
* Encode WME access control bits in the PROTO flags.
* This is safe since it's passed directly in to the
@@ -176,6 +229,17 @@
#define M_AGE_GET(m) (m->m_pkthdr.csum_data)
#define M_AGE_SUB(m,adj) (m->m_pkthdr.csum_data -= adj)
+#define MTAG_ABI_NET80211 1132948340 /* net80211 ABI */
+
+struct ieee80211_cb {
+ void (*func)(struct ieee80211_node *, void *, int status);
+ void *arg;
+};
+#define NET80211_TAG_CALLBACK 0 /* xmit complete callback */
+int ieee80211_add_callback(struct mbuf *m,
+ void (*func)(struct ieee80211_node *, void *, int), void *arg);
+void ieee80211_process_callback(struct ieee80211_node *, struct mbuf *, int);
+
void get_random_bytes(void *, size_t);
struct ieee80211com;
@@ -185,6 +249,37 @@
void ieee80211_load_module(const char *);
+#define IEEE80211_CRYPTO_MODULE(name, version) \
+static int \
+name##_modevent(module_t mod, int type, void *unused) \
+{ \
+ switch (type) { \
+ case MOD_LOAD: \
+ ieee80211_crypto_register(&name); \
+ return 0; \
+ case MOD_UNLOAD: \
+ case MOD_QUIESCE: \
+ if (nrefs) { \
+ printf("wlan_##name: still in use (%u dynamic refs)\n",\
+ nrefs); \
+ return EBUSY; \
+ } \
+ if (type == MOD_UNLOAD) \
+ ieee80211_crypto_unregister(&name); \
+ return 0; \
+ } \
+ return EINVAL; \
+} \
+static moduledata_t name##_mod = { \
+ "wlan_" #name, \
+ name##_modevent, \
+ 0 \
+}; \
+DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
+MODULE_VERSION(wlan_##name, version); \
+MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1)
+#endif /* _KERNEL */
+
/* XXX this stuff belongs elsewhere */
/*
* Message formats for messages from the net80211 layer to user
@@ -225,4 +320,37 @@
#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */
#define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */
+/*
+ * Structure prepended to raw packets sent through the bpf
+ * interface when set to DLT_IEEE802_11_RADIO. This allows
+ * user applications to specify pretty much everything in
+ * an Atheros tx descriptor. XXX need to generalize.
+ *
+ * XXX cannot be more than 14 bytes as it is copied to a sockaddr's
+ * XXX sa_data area.
+ */
+struct ieee80211_bpf_params {
+ uint8_t ibp_vers; /* version */
+#define IEEE80211_BPF_VERSION 0
+ uint8_t ibp_len; /* header length in bytes */
+ uint8_t ibp_flags;
+#define IEEE80211_BPF_SHORTPRE 0x01 /* tx with short preamble */
+#define IEEE80211_BPF_NOACK 0x02 /* tx with no ack */
+#define IEEE80211_BPF_CRYPTO 0x04 /* tx with h/w encryption */
+#define IEEE80211_BPF_FCS 0x10 /* frame incldues FCS */
+#define IEEE80211_BPF_DATAPAD 0x20 /* frame includes data padding */
+#define IEEE80211_BPF_RTS 0x40 /* tx with RTS/CTS */
+#define IEEE80211_BPF_CTS 0x80 /* tx with CTS only */
+ uint8_t ibp_pri; /* WME/WMM AC+tx antenna */
+ uint8_t ibp_try0; /* series 1 try count */
+ uint8_t ibp_rate0; /* series 1 IEEE tx rate */
+ uint8_t ibp_power; /* tx power (device units) */
+ uint8_t ibp_ctsrate; /* IEEE tx rate for CTS */
+ uint8_t ibp_try1; /* series 2 try count */
+ uint8_t ibp_rate1; /* series 2 IEEE tx rate */
+ uint8_t ibp_try2; /* series 3 try count */
+ uint8_t ibp_rate2; /* series 3 IEEE tx rate */
+ uint8_t ibp_try3; /* series 4 try count */
+ uint8_t ibp_rate3; /* series 4 IEEE tx rate */
+};
#endif /* _NET80211_IEEE80211_FREEBSD_H_ */
Index: ieee80211_input.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_input.c -L sys/net80211/ieee80211_input.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.62.2.12 2006/03/14 23:24:02 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.113.2.1.2.1 2008/02/05 18:29:03 sam Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -73,48 +67,17 @@
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 *,
+static const uint8_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 *, int);
static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int);
static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
- const u_int8_t *mac, int subtype, int arg);
-static void ieee80211_deliver_data(struct ieee80211com *,
+ const uint8_t *mac, int subtype, int arg);
+static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *,
struct ieee80211_node *, struct mbuf *);
-static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
static void ieee80211_recv_pspoll(struct ieee80211com *,
struct ieee80211_node *, struct mbuf *);
@@ -130,7 +93,7 @@
*/
int
ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+ struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp)
{
#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define HAS_SEQ(type) ((type & 0x4) == 0)
@@ -138,19 +101,33 @@
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct ether_header *eh;
- int hdrspace;
- u_int8_t dir, type, subtype;
- u_int8_t *bssid;
- u_int16_t rxseq;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ need_tap = 0;
+ goto resubmit_ampdu;
+ }
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) {
- m_adj(m, -IEEE80211_CRC_LEN);
- m->m_flags &= ~M_HASFCS;
- }
+ need_tap = 1; /* mbuf need to be tapped. */
type = -1; /* undefined */
/*
* In monitor mode, send everything directly to bpf.
@@ -251,9 +228,10 @@
goto out;
}
ni->ni_rssi = rssi;
+ ni->ni_noise = noise;
ni->ni_rstamp = rstamp;
if (HAS_SEQ(type)) {
- u_int8_t tid;
+ uint8_t tid;
if (IEEE80211_QOS_HAS_SEQ(wh)) {
tid = ((struct ieee80211_qosframe *)wh)->
i_qos[0] & IEEE80211_QOS_TID;
@@ -261,9 +239,10 @@
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) &&
+ tid = IEEE80211_NONQOS_TID;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
/* duplicate, discard */
IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
@@ -299,7 +278,7 @@
case IEEE80211_M_STA:
if (dir != IEEE80211_FC1_DIR_FROMDS) {
IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown dir 0x%x", dir);
+ wh, "data", "unknown dir 0x%x", dir);
ic->ic_stats.is_rx_wrongdir++;
goto out;
}
@@ -322,7 +301,7 @@
case IEEE80211_M_AHDEMO:
if (dir != IEEE80211_FC1_DIR_NODS) {
IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown dir 0x%x", dir);
+ wh, "data", "unknown dir 0x%x", dir);
ic->ic_stats.is_rx_wrongdir++;
goto out;
}
@@ -331,7 +310,7 @@
case IEEE80211_M_HOSTAP:
if (dir != IEEE80211_FC1_DIR_TODS) {
IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown dir 0x%x", dir);
+ wh, "data", "unknown dir 0x%x", dir);
ic->ic_stats.is_rx_wrongdir++;
goto out;
}
@@ -357,6 +336,7 @@
/*
* Check for power save state change.
+ * XXX out-of-order A-MPDU frames?
*/
if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
(ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
@@ -369,6 +349,23 @@
}
/*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
* Handle privacy requirements. Note that we
* must not be preempted from here until after
* we (potentially) call ieee80211_crypto_demic;
@@ -396,10 +393,21 @@
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
} else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
}
/*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
@@ -417,13 +425,15 @@
if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
ni->ni_macaddr, "data", "%s", "demic error");
+ ic->ic_stats.is_rx_demicfail++;
IEEE80211_NODE_STAT(ni, rx_demicfail);
goto out;
}
/* copy to listener after decrypt */
- if (ic->ic_rawbpf)
+ if (bpf_peers_present(ic->ic_rawbpf))
bpf_mtap(ic->ic_rawbpf, m);
+ need_tap = 0;
/*
* Finally, strip the 802.11 header.
@@ -431,7 +441,8 @@
m = ieee80211_decap(ic, m, hdrspace);
if (m == NULL) {
/* don't count Null data frames as errors */
- if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
goto out;
IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
ni->ni_macaddr, "data", "%s", "decap error");
@@ -464,7 +475,7 @@
* any non-PAE frames received without encryption.
*/
if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
- key == NULL &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
@@ -474,18 +485,46 @@
goto out;
}
}
- ifp->if_ipackets++;
- IEEE80211_NODE_STAT(ni, rx_data);
- IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ ic->ic_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ic, ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
ieee80211_deliver_data(ic, ni, m);
return IEEE80211_FC0_TYPE_DATA;
case IEEE80211_FC0_TYPE_MGT:
+ ic->ic_stats.is_rx_mgmt++;
IEEE80211_NODE_STAT(ni, rx_mgmt);
if (dir != IEEE80211_FC1_DIR_NODS) {
IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown dir 0x%x", dir);
+ wh, "data", "unknown dir 0x%x", dir);
ic->ic_stats.is_rx_wrongdir++;
goto err;
}
@@ -536,20 +575,23 @@
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
}
- if (ic->ic_rawbpf)
+ if (bpf_peers_present(ic->ic_rawbpf))
bpf_mtap(ic->ic_rawbpf, m);
- (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
+ (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp);
m_freem(m);
- return type;
+ return IEEE80211_FC0_TYPE_MGT;
case IEEE80211_FC0_TYPE_CTL:
- IEEE80211_NODE_STAT(ni, rx_ctrl);
ic->ic_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PS_POLL:
ieee80211_recv_pspoll(ic, ni, m);
break;
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ ieee80211_recv_bar(ni, m);
+ break;
}
}
goto out;
@@ -563,7 +605,7 @@
ifp->if_ierrors++;
out:
if (m != NULL) {
- if (ic->ic_rawbpf)
+ if (bpf_peers_present(ic->ic_rawbpf) && need_tap)
bpf_mtap(ic->ic_rawbpf, m);
m_freem(m);
}
@@ -580,14 +622,14 @@
{
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;
+ uint16_t rxseq;
+ uint8_t fragno;
+ uint8_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);
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
/* Quick way out, if there's nothing to defragment */
@@ -620,10 +662,10 @@
* related to the previous ones.
*/
if (mfrag != NULL) {
- u_int16_t last_rxseq;
+ uint16_t last_rxseq;
lwh = mtod(mfrag, struct ieee80211_frame *);
- last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
+ last_rxseq = le16toh(*(uint16_t *)lwh->i_seq);
/* NB: check seq # and frag together */
if (rxseq != last_rxseq+1 ||
!IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
@@ -639,6 +681,7 @@
if (mfrag == NULL) {
if (fragno != 0) { /* !first fragment, discard */
+ ic->ic_stats.is_rx_defrag++;
IEEE80211_NODE_STAT(ni, rx_defrag);
m_freem(m);
return NULL;
@@ -651,7 +694,7 @@
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;
+ *(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq;
}
if (more_frag) { /* more to come, save */
ni->ni_rxfragstamp = ticks;
@@ -661,19 +704,34 @@
return mfrag;
}
-static void
+void
ieee80211_deliver_data(struct ieee80211com *ic,
struct ieee80211_node *ni, struct mbuf *m)
{
struct ether_header *eh = mtod(m, struct ether_header *);
struct ifnet *ifp = ic->ic_ifp;
+ /*
+ * Do accounting.
+ */
+ ifp->if_ipackets++;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ m->m_flags |= M_MCAST; /* XXX M_BCAST? */
+ IEEE80211_NODE_STAT(ni, rx_mcast);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_ucast);
+
+ /* clear driver/net80211 flags before passing up */
+ m->m_flags &= ~M_80211_RX;
+
/* perform as a bridge within the AP */
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)) {
+ if (m->m_flags & M_MCAST) {
m1 = m_dup(m, M_DONTWAIT);
if (m1 == NULL)
ifp->if_oerrors++;
@@ -704,25 +762,22 @@
ieee80211_free_node(sta);
}
}
- if (m1 != NULL)
- IF_HANDOFF(&ifp->if_snd, m1, ifp);
+ if (m1 != NULL) {
+ int error;
+
+ /* XXX does not work well with WME */
+ IFQ_HANDOFF(ifp, m1, error);
+ }
}
if (m != NULL) {
+ m->m_pkthdr.rcvif = ifp;
if (ni->ni_vlan != 0) {
/* attach vlan tag */
- VLAN_INPUT_TAG_NEW(ifp, m, ni->ni_vlan);
- if (m == NULL)
- goto out; /* XXX goto err? */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
}
(*ifp->if_input)(ifp, m);
}
- return;
- out:
- if (m != NULL) {
- if (ic->ic_rawbpf)
- bpf_mtap(ic->ic_rawbpf, m);
- m_freem(m);
- }
}
static struct mbuf *
@@ -767,7 +822,7 @@
break;
}
#ifdef ALIGNED_POINTER
- if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
+ if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
struct mbuf *n, *n0, **np;
caddr_t newdata;
int off, pktlen;
@@ -825,11 +880,109 @@
}
/*
+ * Decap a frame encapsulated in a fast-frame/A-MSDU.
+ */
+struct mbuf *
+ieee80211_decap1(struct mbuf *m, int *framelen)
+{
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ struct ether_header *eh;
+ struct llc *llc;
+
+ /*
+ * The frame has an 802.3 header followed by an 802.2
+ * LLC header. The encapsulated frame length is in the
+ * first header type field; save that and overwrite it
+ * with the true type field found in the second. Then
+ * copy the 802.3 header up to where it belongs and
+ * adjust the mbuf contents to remove the void.
+ */
+ if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL)
+ return NULL;
+ eh = mtod(m, struct ether_header *); /* 802.3 header is first */
+ llc = (struct llc *)&eh[1]; /* 802.2 header follows */
+ *framelen = ntohs(eh->ether_type) /* encap'd frame size */
+ + sizeof(struct ether_header) - sizeof(struct llc);
+ eh->ether_type = llc->llc_un.type_snap.ether_type;
+ ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc),
+ sizeof(struct ether_header));
+ m_adj(m, sizeof(struct llc));
+ return m;
+#undef FF_LLC_SIZE
+}
+
+/*
+ * Decap the encapsulated frame pair and dispatch the first
+ * for delivery. The second frame is returned for delivery
+ * via the normal path.
+ */
+static struct mbuf *
+ieee80211_decap_fastframe(struct ieee80211com *ic,
+ struct ieee80211_node *ni, struct mbuf *m)
+{
+#define MS(x,f) (((x) & f) >> f##_S)
+ uint32_t ath;
+ struct mbuf *n;
+ int framelen;
+
+ m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath);
+ if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "unsupport tunnel protocol, header 0x%x", ath);
+ ic->ic_stats.is_ff_badhdr++;
+ m_freem(m);
+ return NULL;
+ }
+ /* NB: skip header and alignment padding */
+ m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2);
+
+ ic->ic_stats.is_ff_decap++;
+
+ /*
+ * Decap the first frame, bust it apart from the
+ * second and deliver; then decap the second frame
+ * and return it to the caller for normal delivery.
+ */
+ m = ieee80211_decap1(m, &framelen);
+ if (m == NULL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame", "%s", "first decap failed");
+ ic->ic_stats.is_ff_tooshort++;
+ return NULL;
+ }
+ n = m_split(m, framelen, M_NOWAIT);
+ if (n == NULL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "unable to split encapsulated frames");
+ ic->ic_stats.is_ff_split++;
+ m_freem(m); /* NB: must reclaim */
+ return NULL;
+ }
+ ieee80211_deliver_data(ic, ni, m); /* 1st of pair */
+
+ /*
+ * Decap second frame.
+ */
+ m_adj(n, roundup2(framelen, 4) - framelen); /* padding */
+ n = ieee80211_decap1(n, &framelen);
+ if (n == NULL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame", "%s", "second decap failed");
+ ic->ic_stats.is_ff_tooshort++;
+ }
+ /* XXX verify framelen against mbuf contents */
+ return n; /* 2nd delivered by caller */
+#undef MS
+}
+
+/*
* Install received rate set information in the node's state block.
*/
int
ieee80211_setup_rates(struct ieee80211_node *ni,
- const u_int8_t *rates, const u_int8_t *xrates, int flags)
+ const uint8_t *rates, const uint8_t *xrates, int flags)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_rateset *rs = &ni->ni_rates;
@@ -838,7 +991,7 @@
rs->rs_nrates = rates[1];
memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
if (xrates != NULL) {
- u_int8_t nxrates;
+ uint8_t nxrates;
/*
* Tack on 11g extended supported rate element.
*/
@@ -854,13 +1007,13 @@
memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
rs->rs_nrates += nxrates;
}
- return ieee80211_fix_rate(ni, flags);
+ return ieee80211_fix_rate(ni, rs, 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)
+ struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
{
if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
@@ -869,6 +1022,15 @@
"bad sta auth mode %u", ni->ni_authmode);
ic->ic_stats.is_rx_bad_auth++; /* XXX */
if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * Clear any challenge text that may be there if
+ * a previous shared key auth failed and then an
+ * open auth is attempted.
+ */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
/* XXX hack to workaround calling convention */
ieee80211_send_error(ic, ni, wh->i_addr2,
IEEE80211_FC0_SUBTYPE_AUTH,
@@ -880,6 +1042,7 @@
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
case IEEE80211_M_MONITOR:
+ case IEEE80211_M_WDS:
/* should not come here */
IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
ni->ni_macaddr, "open auth",
@@ -934,10 +1097,10 @@
if (ni != ic->ic_bss)
ni->ni_fails++;
ic->ic_stats.is_rx_auth_fail++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
} else
- ieee80211_new_state(ic, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
break;
}
}
@@ -951,7 +1114,7 @@
*/
static void
ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
- const u_int8_t *mac, int subtype, int arg)
+ const uint8_t *mac, int subtype, int arg)
{
int istmp;
@@ -973,8 +1136,8 @@
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);
+ MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN,
+ M_80211_NODE, M_NOWAIT);
if (ni->ni_challenge == NULL) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
"[%s] shared key challenge alloc failed\n",
@@ -987,10 +1150,10 @@
/* 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)
+ uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
{
- u_int8_t *challenge;
+ uint8_t *challenge;
int allocbs, estatus;
/*
@@ -1064,6 +1227,7 @@
case IEEE80211_M_MONITOR:
case IEEE80211_M_AHDEMO:
case IEEE80211_M_IBSS:
+ case IEEE80211_M_WDS:
IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
ni->ni_macaddr, "shared key auth",
"bad operating mode %u", ic->ic_opmode);
@@ -1097,6 +1261,7 @@
*/
ni->ni_flags |= IEEE80211_NODE_AREF;
ni->ni_rssi = rssi;
+ ni->ni_noise = noise;
ni->ni_rstamp = rstamp;
if (!alloc_challenge(ic, ni)) {
/* NB: don't return error so they rexmit */
@@ -1159,7 +1324,7 @@
switch (seq) {
case IEEE80211_AUTH_SHARED_PASS:
if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_DEVBUF);
+ FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
if (status != 0) {
@@ -1174,8 +1339,7 @@
ic->ic_stats.is_rx_auth_fail++;
return;
}
- ieee80211_new_state(ic, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
break;
case IEEE80211_AUTH_SHARED_CHALLENGE:
if (!alloc_challenge(ic, ni))
@@ -1210,7 +1374,8 @@
* state transition.
*/
if (ic->ic_state == IEEE80211_S_AUTH)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
}
}
@@ -1234,21 +1399,22 @@
} \
} while (0)
-#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
if ((_len) < (_minlen)) { \
IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
wh, ieee80211_mgt_subtype_name[subtype >> \
IEEE80211_FC0_SUBTYPE_SHIFT], \
- "%s", "ie too short"); \
+ "ie too short, got %d, expected %d", \
+ (_len), (_minlen)); \
ic->ic_stats.is_rx_elem_toosmall++; \
- return; \
+ _action; \
} \
} 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)
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
{
printf("[%s] discard %s frame, ssid mismatch: ",
ether_sprintf(mac), tag);
@@ -1282,58 +1448,70 @@
/* 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)))
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_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)))
+ ((uint32_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8) | \
+ (((const uint8_t *)(p))[2] << 16) | \
+ (((const uint8_t *)(p))[3] << 24)))
-static int __inline
-iswpaoui(const u_int8_t *frm)
+static __inline int
+iswpaoui(const uint8_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)
+static __inline int
+iswmeoui(const uint8_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)
+static __inline int
+iswmeparam(const uint8_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)
+static __inline int
+iswmeinfo(const uint8_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)
+static __inline int
+isatherosoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
}
+static __inline int
+ishtcapoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
+}
+
+static __inline int
+ishtinfooui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_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)
+wpa_cipher(uint8_t *sel, uint8_t *keylen)
{
#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- u_int32_t w = LE_READ_4(sel);
+ uint32_t w = LE_READ_4(sel);
switch (w) {
case WPA_SEL(WPA_CSE_NULL):
@@ -1360,10 +1538,10 @@
* to an internal code.
*/
static int
-wpa_keymgmt(u_int8_t *sel)
+wpa_keymgmt(uint8_t *sel)
{
#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- u_int32_t w = LE_READ_4(sel);
+ uint32_t w = LE_READ_4(sel);
switch (w) {
case WPA_SEL(WPA_ASE_8021X_UNSPEC):
@@ -1383,11 +1561,11 @@
* configured for the system.
*/
static int
-ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm,
+ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm,
struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
{
- u_int8_t len = frm[1];
- u_int32_t w;
+ uint8_t len = frm[1];
+ uint32_t w;
int n;
/*
@@ -1495,10 +1673,10 @@
* record any key length.
*/
static int
-rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
+rsn_cipher(uint8_t *sel, uint8_t *keylen)
{
#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- u_int32_t w = LE_READ_4(sel);
+ uint32_t w = LE_READ_4(sel);
switch (w) {
case RSN_SEL(RSN_CSE_NULL):
@@ -1527,10 +1705,10 @@
* to an internal code.
*/
static int
-rsn_keymgmt(u_int8_t *sel)
+rsn_keymgmt(uint8_t *sel)
{
#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- u_int32_t w = LE_READ_4(sel);
+ uint32_t w = LE_READ_4(sel);
switch (w) {
case RSN_SEL(RSN_ASE_8021X_UNSPEC):
@@ -1550,11 +1728,11 @@
* configured for the system.
*/
static int
-ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm,
+ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm,
struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
{
- u_int8_t len = frm[1];
- u_int32_t w;
+ uint8_t len = frm[1];
+ uint32_t w;
int n;
/*
@@ -1658,7 +1836,7 @@
}
static int
-ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm,
+ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
const struct ieee80211_frame *wh)
{
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
@@ -1694,8 +1872,65 @@
#undef MS
}
+static int
+ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_ath_ie *ath;
+ u_int len = frm[1];
+ int capschanged;
+ uint16_t defkeyix;
+
+ if (len < sizeof(struct ieee80211_ath_ie)-2) {
+ IEEE80211_DISCARD_IE(ic,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
+ wh, "Atheros", "too short, len %u", len);
+ return -1;
+ }
+ ath = (const struct ieee80211_ath_ie *)frm;
+ capschanged = (ni->ni_ath_flags != ath->ath_capability);
+ defkeyix = LE_READ_2(ath->ath_defkeyix);
+ if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
+ ni->ni_ath_flags = ath->ath_capability;
+ ni->ni_ath_defkeyix = defkeyix;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n",
+ ether_sprintf(ni->ni_macaddr),
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ }
+ if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) {
+ uint16_t curflags, newflags;
+
+ /*
+ * Check for turbo mode switch. Calculate flags
+ * for the new mode and effect the switch.
+ */
+ newflags = curflags = ic->ic_bsschan->ic_flags;
+ /* NB: BOOST is not in ic_flags, so get it from the ie */
+ if (ath->ath_capability & ATHEROS_CAP_BOOST)
+ newflags |= IEEE80211_CHAN_TURBO;
+ else
+ newflags &= ~IEEE80211_CHAN_TURBO;
+ if (newflags != curflags)
+ ieee80211_dturbo_switch(ic, newflags);
+ }
+ return capschanged;
+}
+
+void
+ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie)
+{
+ const struct ieee80211_ath_ie *ath =
+ (const struct ieee80211_ath_ie *) ie;
+
+ ni->ni_ath_flags = ath->ath_capability;
+ ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
+ ieee80211_saveie(&ni->ni_ath_ie, ie);
+}
+
void
-ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
+ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
{
u_int ielen = ie[1]+2;
/*
@@ -1703,8 +1938,8 @@
*/
if (*iep == NULL || (*iep)[1] != ie[1]) {
if (*iep != NULL)
- FREE(*iep, M_DEVBUF);
- MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT);
+ FREE(*iep, M_80211_NODE);
+ MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT);
}
if (*iep != NULL)
memcpy(*iep, ie, ielen);
@@ -1714,10 +1949,10 @@
/* XXX find a better place for definition */
struct l2_update_frame {
struct ether_header eh;
- u_int8_t dsap;
- u_int8_t ssap;
- u_int8_t control;
- u_int8_t xid[3];
+ uint8_t dsap;
+ uint8_t ssap;
+ uint8_t control;
+ uint8_t xid[3];
} __packed;
/*
@@ -1757,26 +1992,84 @@
l2uf->xid[2] = 0x00;
m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
- m->m_pkthdr.rcvif = ifp;
ieee80211_deliver_data(ic, ni, m);
}
+static __inline int
+contbgscan(struct ieee80211com *ic)
+{
+ return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+ time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
+}
+
+static __inline int
+startbgscan(struct ieee80211com *ic)
+{
+ return ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+ time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) &&
+ time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
+}
+
+static void
+ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n",
+ ether_sprintf(wh->i_addr2),
+ reassoc ? "reassoc" : "assoc", tag, rate);
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
+ ieee80211_node_leave(ic, ni);
+ ic->ic_stats.is_rx_assoc_norate++;
+}
+
+static void
+capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int capinfo)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "[%s] deny %s request, %s mismatch 0x%x\n",
+ ether_sprintf(wh->i_addr2),
+ reassoc ? "reassoc" : "assoc", tag, capinfo);
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
+ ieee80211_node_leave(ic, ni);
+ ic->ic_stats.is_rx_assoc_capmismatch++;
+}
+
+static void
+htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
+ /* XXX no better code */
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER);
+ ieee80211_node_leave(ic, ni);
+}
+
void
ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
struct ieee80211_node *ni,
- int subtype, int rssi, u_int32_t rstamp)
+ int subtype, int rssi, int noise, uint32_t rstamp)
{
#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, *wpa, *wme;
+ uint8_t *frm, *efrm;
+ uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo;
int reassoc, resp, allocbs;
- u_int8_t rate;
+ uint8_t rate;
wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON: {
@@ -1809,17 +2102,20 @@
* [tlv] extended supported rates
* [tlv] WME
* [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Atheros capabilities
*/
- IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return);
memset(&scan, 0, sizeof(scan));
scan.tstamp = frm; frm += 8;
- scan.bintval = le16toh(*(u_int16_t *)frm); frm += 2;
- scan.capinfo = le16toh(*(u_int16_t *)frm); frm += 2;
- scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
- scan.chan = scan.bchan;
+ scan.bintval = le16toh(*(uint16_t *)frm); frm += 2;
+ scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ scan.bchan = IEEE80211_CHAN2IEEE(ic->ic_curchan);
+ scan.curchan = ic->ic_curchan;
- while (frm < efrm) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
case IEEE80211_ELEMID_SSID:
scan.ssid = frm;
@@ -1833,7 +2129,7 @@
case IEEE80211_ELEMID_FHPARMS:
if (ic->ic_phytype == IEEE80211_T_FH) {
scan.fhdwell = LE_READ_2(&frm[2]);
- scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+ scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]);
scan.fhindex = frm[6];
}
break;
@@ -1843,12 +2139,12 @@
* is problematic for multi-mode devices.
*/
if (ic->ic_phytype != IEEE80211_T_FH)
- scan.chan = frm[2];
+ scan.bchan = frm[2];
break;
case IEEE80211_ELEMID_TIM:
/* XXX ATIM? */
scan.tim = frm;
- scan.timoff = frm - mtod(m0, u_int8_t *);
+ scan.timoff = frm - mtod(m0, uint8_t *);
break;
case IEEE80211_ELEMID_IBSSPARMS:
break;
@@ -1865,15 +2161,35 @@
}
scan.erp = frm[2];
break;
+ case IEEE80211_ELEMID_HTCAP:
+ scan.htcap = frm;
+ break;
case IEEE80211_ELEMID_RSN:
- scan.wpa = frm;
+ scan.rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ scan.htinfo = frm;
break;
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
scan.wpa = frm;
else if (iswmeparam(frm) || iswmeinfo(frm))
scan.wme = frm;
- /* XXX Atheros OUI support */
+ else if (isatherosoui(frm))
+ scan.ath = frm;
+ else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (scan.htcap == NULL)
+ scan.htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (scan.htinfo == NULL)
+ scan.htcap = frm;
+ }
+ }
break;
default:
IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
@@ -1885,21 +2201,21 @@
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE);
+ if (scan.xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(scan.xrates,
+ IEEE80211_RATE_MAXSIZE - scan.rates[1]);
IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN);
- if (
#if IEEE80211_CHAN_MAX < 255
- scan.chan > IEEE80211_CHAN_MAX ||
-#endif
- isclr(ic->ic_chan_active, scan.chan)) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ if (scan.chan > IEEE80211_CHAN_MAX) {
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
wh, ieee80211_mgt_subtype_name[subtype >>
IEEE80211_FC0_SUBTYPE_SHIFT],
"invalid channel %u", scan.chan);
ic->ic_stats.is_rx_badchan++;
return;
}
- if (scan.chan != scan.bchan &&
+#endif
+ if (IEEE80211_CHAN2IEEE(scan.curchan) != scan.bchan &&
ic->ic_phytype != IEEE80211_T_FH) {
/*
* Frame was received on a channel different from the
@@ -1915,7 +2231,8 @@
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
wh, ieee80211_mgt_subtype_name[subtype >>
IEEE80211_FC0_SUBTYPE_SHIFT],
- "for off-channel %u", scan.chan);
+ "for off-channel %u",
+ IEEE80211_CHAN2IEEE(scan.curchan));
ic->ic_stats.is_rx_chanmismatch++;
return;
}
@@ -1929,6 +2246,25 @@
ic->ic_stats.is_rx_badbintval++;
return;
}
+ /*
+ * Process HT ie's. This is complicated by our
+ * accepting both the standard ie's and the pre-draft
+ * vendor OUI ie's that some vendors still use/require.
+ */
+ if (scan.htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan.htcap[1],
+ scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ scan.htcap = NULL);
+ }
+ if (scan.htinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
+ scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
+ sizeof(struct ieee80211_ie_htinfo)-2,
+ scan.htinfo = NULL);
+ }
/*
* Count frame now that we know it's to be processed.
@@ -1959,7 +2295,7 @@
"[%s] erp change: was 0x%x, now 0x%x\n",
ether_sprintf(wh->i_addr2),
ni->ni_erp, scan.erp);
- if (ic->ic_curmode == IEEE80211_MODE_11G &&
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
(ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
ic->ic_flags |= IEEE80211_F_USEPROT;
else
@@ -1978,25 +2314,79 @@
* change dynamically
*/
ieee80211_set_shortslottime(ic,
- ic->ic_curmode == IEEE80211_MODE_11A ||
+ IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
(scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
- ni->ni_capinfo = scan.capinfo;
+ ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
+ | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
/* XXX statistic */
}
if (scan.wme != NULL &&
(ni->ni_flags & IEEE80211_NODE_QOS) &&
ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0)
ieee80211_wme_updateparams(ic);
+ if (scan.ath != NULL)
+ ieee80211_parse_athparams(ni, scan.ath, wh);
+ if (scan.htcap != NULL)
+ ieee80211_parse_htcap(ni, scan.htcap);
+ if (scan.htinfo != NULL) {
+ ieee80211_parse_htinfo(ni, scan.htinfo);
+ if (ni->ni_chan != ic->ic_bsschan) {
+ /*
+ * Channel has been adjusted based on
+ * negotiated HT parameters; force the
+ * channel state to follow.
+ */
+ ieee80211_setbsschan(ic, ni->ni_chan);
+ }
+ }
if (scan.tim != NULL) {
- struct ieee80211_tim_ie *ie =
+ struct ieee80211_tim_ie *tim =
(struct ieee80211_tim_ie *) scan.tim;
-
- ni->ni_dtim_count = ie->tim_count;
- ni->ni_dtim_period = ie->tim_period;
+#if 0
+ int aid = IEEE80211_AID(ni->ni_associd);
+ int ix = aid / NBBY;
+ int min = tim->tim_bitctl &~ 1;
+ int max = tim->tim_len + min - 4;
+ if ((tim->tim_bitctl&1) ||
+ (min <= ix && ix <= max &&
+ isset(tim->tim_bitmap - min, aid))) {
+ /*
+ * XXX Do not let bg scan kick off
+ * we are expecting data.
+ */
+ ic->ic_lastdata = ticks;
+ ieee80211_sta_pwrsave(ic, 0);
+ }
+#endif
+ ni->ni_dtim_count = tim->tim_count;
+ ni->ni_dtim_period = tim->tim_period;
}
- if (ic->ic_flags & IEEE80211_F_SCAN)
+ /*
+ * If scanning, pass the info to the scan module.
+ * Otherwise, check if it's the right time to do
+ * a background scan. Background scanning must
+ * be enabled and we must not be operating in the
+ * turbo phase of dynamic turbo mode. Then,
+ * it's been a while since the last background
+ * scan and if no data frames have come through
+ * recently, kick off a scan. Note that this
+ * is the mechanism by which a background scan
+ * is started _and_ continued each time we
+ * return on-channel to receive a beacon from
+ * our ap.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, rstamp);
+ subtype, rssi, noise, rstamp);
+ } else if (contbgscan(ic)) {
+ ieee80211_bg_scan(ic);
+ } else if (startbgscan(ic)) {
+#if 0
+ /* wakeup if we are sleeing */
+ ieee80211_set_pwrsave(ic, 0);
+#endif
+ ieee80211_bg_scan(ic);
+ }
return;
}
/*
@@ -2016,7 +2406,7 @@
ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
}
ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, rstamp);
+ subtype, rssi, noise, rstamp);
return;
}
if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
@@ -2040,6 +2430,7 @@
}
if (ni != NULL) {
ni->ni_rssi = rssi;
+ ni->ni_noise = noise;
ni->ni_rstamp = rstamp;
}
}
@@ -2063,10 +2454,11 @@
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
+ * [tlv] Atheros capabilities
*/
- ssid = rates = xrates = NULL;
- while (frm < efrm) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+ ssid = rates = xrates = ath = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
case IEEE80211_ELEMID_SSID:
ssid = frm;
@@ -2077,10 +2469,17 @@
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (isatherosoui(frm))
+ ath = frm;
+ break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1]);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
@@ -2132,11 +2531,12 @@
* response, reclaim immediately.
*/
ieee80211_free_node(ni);
- }
+ } else if (ath != NULL)
+ ieee80211_saveath(ni, ath);
break;
case IEEE80211_FC0_SUBTYPE_AUTH: {
- u_int16_t algo, seq, status;
+ uint16_t algo, seq, status;
/*
* auth frame format
* [2] algorithm
@@ -2144,10 +2544,10 @@
* [2] status
* [tlv*] challenge
*/
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
- algo = le16toh(*(u_int16_t *)frm);
- seq = le16toh(*(u_int16_t *)(frm + 2));
- status = le16toh(*(u_int16_t *)(frm + 4));
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
"[%s] recv auth frame with algorithm %d seq %d\n",
ether_sprintf(wh->i_addr2), algo, seq);
@@ -2180,10 +2580,10 @@
}
if (algo == IEEE80211_AUTH_ALG_SHARED)
ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
- rstamp, seq, status);
+ noise, rstamp, seq, status);
else if (algo == IEEE80211_AUTH_ALG_OPEN)
- ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,
- status);
+ ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp,
+ seq, status);
else {
IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
wh, "auth", "unsupported alg %d", algo);
@@ -2201,9 +2601,10 @@
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
- u_int16_t capinfo, lintval;
- struct ieee80211_rsnparms rsn;
- u_int8_t reason;
+ uint16_t capinfo, lintval;
+ struct ieee80211_rsnparms rsnparms;
+ uint8_t reason;
+ int badwparsn;
if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
ic->ic_state != IEEE80211_S_RUN) {
@@ -2227,8 +2628,10 @@
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] Atheros capabilities
*/
- IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
+ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
wh, ieee80211_mgt_subtype_name[subtype >>
@@ -2237,13 +2640,13 @@
ic->ic_stats.is_rx_assoc_bss++;
return;
}
- capinfo = le16toh(*(u_int16_t *)frm); frm += 2;
- lintval = le16toh(*(u_int16_t *)frm); frm += 2;
+ capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ lintval = le16toh(*(uint16_t *)frm); frm += 2;
if (reassoc)
frm += 6; /* ignore current AP info */
- ssid = rates = xrates = wpa = wme = NULL;
- while (frm < efrm) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+ ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
case IEEE80211_ELEMID_SSID:
ssid = frm;
@@ -2256,21 +2659,39 @@
break;
/* XXX verify only one of RSN and WPA ie's? */
case IEEE80211_ELEMID_RSN:
- wpa = frm;
+ rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
break;
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
wpa = frm;
else if (iswmeinfo(frm))
wme = frm;
- /* XXX Atheros OUI support */
+ else if (isatherosoui(frm))
+ ath = frm;
+ else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ if (ishtcapoui(frm) && htcap == NULL)
+ htcap = frm;
+ }
break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1]);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+ if (htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(htcap[1],
+ htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ return); /* XXX just NULL out? */
+ }
if (ni == ic->ic_bss) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
@@ -2283,33 +2704,47 @@
ic->ic_stats.is_rx_assoc_notauth++;
return;
}
- /* assert right associstion security credentials */
- if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
+ /* assert right association security credentials */
+ badwparsn = 0;
+ switch (ic->ic_flags & IEEE80211_F_WPA) {
+ case IEEE80211_F_WPA1:
+ if (wpa == NULL)
+ badwparsn = 1;
+ break;
+ case IEEE80211_F_WPA2:
+ if (rsn == NULL)
+ badwparsn = 1;
+ break;
+ case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
+ if (wpa == NULL && rsn == NULL)
+ badwparsn = 1;
+ break;
+ }
+ if (badwparsn) {
IEEE80211_DPRINTF(ic,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
"[%s] no WPA/RSN IE in association request\n",
ether_sprintf(wh->i_addr2));
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_RSN_REQUIRED);
+ IEEE80211_REASON_IE_INVALID);
ieee80211_node_leave(ic, ni);
- /* XXX distinguish WPA/RSN? */
ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
+ return;
}
- if (wpa != NULL) {
+ if (wpa != NULL || rsn != NULL) {
/*
- * Parse WPA information element. Note that
+ * Parse WPA/RSN 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);
+ rsnparms = ni->ni_rsn;
+ if (wpa != NULL)
+ reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh);
else
- reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh);
+ reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh);
if (reason != 0) {
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
@@ -2322,71 +2757,133 @@
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);
+ wpa != NULL ? "WPA" : "RSN",
+ rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen,
+ rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen,
+ rsnparms.rsn_keymgmt, rsnparms.rsn_caps);
}
/* discard challenge after association */
if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_DEVBUF);
+ FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
/* NB: 802.11 spec says to ignore station's privacy bit */
if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
- 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++;
+ capinfomismatch(ni, wh, reassoc, resp,
+ "capability", capinfo);
+ return;
+ }
+ /*
+ * Disallow re-associate w/ invalid slot time setting.
+ */
+ if (ni->ni_associd != 0 &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+ ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "slot time", capinfo);
return;
}
rate = ieee80211_setup_rates(ni, rates, xrates,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp, "basic", rate);
+ return;
+ }
/*
* If constrained to 11g-only stations reject an
* 11b-only station. We cheat a bit here by looking
* at the max negotiated xmit rate and assuming anyone
* with a best rate <24Mb/s is an 11b station.
*/
- if ((rate & IEEE80211_RATE_BASIC) ||
- ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) {
- 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++;
+ if ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) {
+ ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
+ return;
+ }
+ /* XXX enforce PUREN */
+ /* 802.11n-specific rateset handling */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) {
+ rate = ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
+ IEEE80211_F_DOBRS);
+ if (rate & IEEE80211_RATE_BASIC) {
+ /* XXX 11n-specific stat */
+ ratesetmismatch(ni, wh, reassoc, resp,
+ "HT", rate);
+ return;
+ }
+ ieee80211_ht_node_init(ni, htcap);
+ } else if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_ht_node_cleanup(ni);
+ /*
+ * Allow AMPDU operation only with unencrypted traffic
+ * or AES-CCM; the 11n spec only specifies these ciphers
+ * so permitting any others is undefined and can lead
+ * to interoperability problems.
+ *
+ * NB: We check for AES by looking at the GTK cipher
+ * since the WPA/11i specs say the PTK cipher has
+ * to be "as good or better".
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ (((ic->ic_flags & IEEE80211_F_WPA) &&
+ rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) ||
+ (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+ "disallow HT use because WEP or TKIP requested, "
+ "capinfo 0x%x mcastcipher %d", capinfo,
+ rsnparms.rsn_mcastcipher);
+ ieee80211_ht_node_cleanup(ni);
+ ic->ic_stats.is_ht_assoc_downgrade++;
+ }
+ /*
+ * If constrained to 11n-only stations reject legacy stations.
+ */
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) &&
+ (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+ htcapmismatch(ni, wh, reassoc, resp);
+ ic->ic_stats.is_ht_assoc_nohtcap++;
return;
}
ni->ni_rssi = rssi;
+ ni->ni_noise = noise;
ni->ni_rstamp = rstamp;
ni->ni_intval = lintval;
ni->ni_capinfo = capinfo;
- ni->ni_chan = ic->ic_bss->ni_chan;
+ ni->ni_chan = ic->ic_bsschan;
ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
ni->ni_fhindex = ic->ic_bss->ni_fhindex;
if (wpa != NULL) {
/*
- * Record WPA/RSN parameters for station, mark
+ * Record WPA parameters for station, mark
* node as using WPA and record information element
* for applications that require it.
*/
- ni->ni_rsn = rsn;
+ ni->ni_rsn = rsnparms;
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);
+ FREE(ni->ni_wpa_ie, M_80211_NODE);
ni->ni_wpa_ie = NULL;
}
+ if (rsn != NULL) {
+ /*
+ * Record RSN parameters for station, mark
+ * node as using WPA and record information element
+ * for applications that require it.
+ */
+ ni->ni_rsn = rsnparms;
+ ieee80211_saveie(&ni->ni_rsn_ie, rsn);
+ } else if (ni->ni_rsn_ie != NULL) {
+ /*
+ * Flush any state from a previous association.
+ */
+ FREE(ni->ni_rsn_ie, M_80211_NODE);
+ ni->ni_rsn_ie = NULL;
+ }
if (wme != NULL) {
/*
* Record WME parameters for station, mark node
@@ -2399,19 +2896,35 @@
/*
* Flush any state from a previous association.
*/
- FREE(ni->ni_wme_ie, M_DEVBUF);
+ FREE(ni->ni_wme_ie, M_80211_NODE);
ni->ni_wme_ie = NULL;
ni->ni_flags &= ~IEEE80211_NODE_QOS;
}
- ieee80211_deliver_l2uf(ni);
+ if (ath != NULL) {
+ /*
+ * Record ATH parameters for station, mark
+ * node with appropriate capabilities, and
+ * record the information element for
+ * applications that require it.
+ */
+ ieee80211_saveath(ni, ath);
+ } else if (ni->ni_ath_ie != NULL) {
+ /*
+ * Flush any state from a previous association.
+ */
+ FREE(ni->ni_ath_ie, M_80211_NODE);
+ ni->ni_ath_ie = NULL;
+ ni->ni_ath_flags = 0;
+ }
ieee80211_node_join(ic, ni, resp);
+ ieee80211_deliver_l2uf(ni);
break;
}
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
- u_int16_t capinfo, associd;
- u_int16_t status;
+ uint16_t capinfo, associd;
+ uint16_t status;
if (ic->ic_opmode != IEEE80211_M_STA ||
ic->ic_state != IEEE80211_S_ASSOC) {
@@ -2427,12 +2940,14 @@
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] HT info
*/
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
ni = ic->ic_bss;
- capinfo = le16toh(*(u_int16_t *)frm);
+ capinfo = le16toh(*(uint16_t *)frm);
frm += 2;
- status = le16toh(*(u_int16_t *)frm);
+ status = le16toh(*(uint16_t *)frm);
frm += 2;
if (status != 0) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
@@ -2444,12 +2959,12 @@
ic->ic_stats.is_rx_auth_fail++; /* XXX */
return;
}
- associd = le16toh(*(u_int16_t *)frm);
+ associd = le16toh(*(uint16_t *)frm);
frm += 2;
- rates = xrates = wpa = wme = NULL;
- while (frm < efrm) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+ rates = xrates = wme = htcap = htinfo = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
case IEEE80211_ELEMID_RATES:
rates = frm;
@@ -2457,9 +2972,28 @@
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ htinfo = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (iswmeoui(frm))
wme = frm;
+ else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (htcap == NULL)
+ htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (htinfo == NULL)
+ htcap = frm;
+ }
+ }
/* XXX Atheros OUI support */
break;
}
@@ -2467,7 +3001,11 @@
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1]);
rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_JOIN |
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
if (rate & IEEE80211_RATE_BASIC) {
@@ -2478,7 +3016,8 @@
if (ni != ic->ic_bss) /* XXX never true? */
ni->ni_fails++;
ic->ic_stats.is_rx_assoc_norate++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
return;
}
@@ -2491,11 +3030,30 @@
} else
ni->ni_flags &= ~IEEE80211_NODE_QOS;
/*
+ * Setup HT state according to the negotiation.
+ */
+ if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
+ htcap != NULL && htinfo != NULL) {
+ ieee80211_ht_node_init(ni, htcap);
+ ieee80211_parse_htinfo(ni, htinfo);
+ ieee80211_setup_htrates(ni,
+ htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(ni, htinfo);
+ if (ni->ni_chan != ic->ic_bsschan) {
+ /*
+ * Channel has been adjusted based on
+ * negotiated HT parameters; force the
+ * channel state to follow.
+ */
+ ieee80211_setbsschan(ic, ni->ni_chan);
+ }
+ }
+ /*
* Configure state now that we are associated.
*
* XXX may need different/additional driver callbacks?
*/
- if (ic->ic_curmode == IEEE80211_MODE_11A ||
+ if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
@@ -2504,34 +3062,40 @@
ic->ic_flags |= IEEE80211_F_USEBARKER;
}
ieee80211_set_shortslottime(ic,
- ic->ic_curmode == IEEE80211_MODE_11A ||
+ IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(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 (ic->ic_curmode == IEEE80211_MODE_11G &&
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
(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",
+ "[%s] %sassoc success: %s preamble, %s slot time%s%s%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" : ""
+ ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+ ni->ni_flags & IEEE80211_NODE_HT ?
+ (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+ ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+ IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+ ", fast-frames" : "",
+ IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+ ", turbo" : ""
);
ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
break;
}
case IEEE80211_FC0_SUBTYPE_DEAUTH: {
- u_int16_t reason;
+ uint16_t reason;
if (ic->ic_state == IEEE80211_S_SCAN) {
ic->ic_stats.is_rx_mgtdiscard++;
@@ -2541,18 +3105,23 @@
* deauth frame format
* [2] reason
*/
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
- reason = le16toh(*(u_int16_t *)frm);
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
ic->ic_stats.is_rx_deauth++;
IEEE80211_NODE_STAT(ni, rx_deauth);
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ ic->ic_stats.is_rx_mgtdiscard++;
+ break;
+ }
IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
"[%s] recv deauthenticate (reason %d)\n",
ether_sprintf(ni->ni_macaddr), reason);
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
ieee80211_new_state(ic, IEEE80211_S_AUTH,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
break;
case IEEE80211_M_HOSTAP:
if (ni != ic->ic_bss)
@@ -2566,7 +3135,7 @@
}
case IEEE80211_FC0_SUBTYPE_DISASSOC: {
- u_int16_t reason;
+ uint16_t reason;
if (ic->ic_state != IEEE80211_S_RUN &&
ic->ic_state != IEEE80211_S_ASSOC &&
@@ -2578,18 +3147,22 @@
* disassoc frame format
* [2] reason
*/
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
- reason = le16toh(*(u_int16_t *)frm);
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
ic->ic_stats.is_rx_disassoc++;
IEEE80211_NODE_STAT(ni, rx_disassoc);
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ ic->ic_stats.is_rx_mgtdiscard++;
+ break;
+ }
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
"[%s] recv disassociate (reason %d)\n",
ether_sprintf(ni->ni_macaddr), reason);
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
break;
case IEEE80211_M_HOSTAP:
if (ni != ic->ic_bss)
@@ -2601,6 +3174,65 @@
}
break;
}
+
+ case IEEE80211_FC0_SUBTYPE_ACTION: {
+ const struct ieee80211_action *ia;
+
+ if (ic->ic_state != IEEE80211_S_RUN &&
+ ic->ic_state != IEEE80211_S_ASSOC &&
+ ic->ic_state != IEEE80211_S_AUTH) {
+ ic->ic_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return);
+ ia = (const struct ieee80211_action *) frm;
+
+ ic->ic_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return);
+ break;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return);
+ break;
+ }
+ break;
+ }
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ }
+
default:
IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
wh, "mgt", "subtype 0x%x not handled", subtype);
@@ -2614,66 +3246,6 @@
#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) {
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(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 mbuf so ieee80211_encap will mark the 802.11
- * head to indicate more data frames will follow.
- */
- if (qlen != 0)
- m->m_flags |= M_MORE_DATA;
- /* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
- }
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
-}
-
-/*
* Process a received ps-poll frame.
*/
static void
@@ -2682,7 +3254,7 @@
{
struct ieee80211_frame_min *wh;
struct mbuf *m;
- u_int16_t aid;
+ uint16_t aid;
int qlen;
wh = mtod(m0, struct ieee80211_frame_min *);
@@ -2696,7 +3268,7 @@
return;
}
- aid = le16toh(*(u_int16_t *)wh->i_dur);
+ aid = le16toh(*(uint16_t *)wh->i_dur);
if (aid != ni->ni_associd) {
IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
(struct ieee80211_frame *) wh, "ps-poll",
@@ -2749,7 +3321,7 @@
/*
* Return the bssid of a frame.
*/
-static const u_int8_t *
+static const uint8_t *
ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
{
if (ic->ic_opmode == IEEE80211_M_STA)
@@ -2791,7 +3363,7 @@
void
ieee80211_note_mac(struct ieee80211com *ic,
- const u_int8_t mac[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN],
const char *fmt, ...)
{
char buf[128]; /* XXX */
@@ -2803,7 +3375,7 @@
if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
}
-static void
+void
ieee80211_discard_frame(struct ieee80211com *ic,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
@@ -2822,7 +3394,7 @@
printf("\n");
}
-static void
+void
ieee80211_discard_ie(struct ieee80211com *ic,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
@@ -2841,9 +3413,9 @@
printf("\n");
}
-static void
+void
ieee80211_discard_mac(struct ieee80211com *ic,
- const u_int8_t mac[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN],
const char *type, const char *fmt, ...)
{
va_list ap;
Index: ieee80211_crypto_none.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_none.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_none.c -L sys/net80211/ieee80211_crypto_none.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_crypto_none.c
+++ sys/net80211/ieee80211_crypto_none.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
* 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
@@ -30,7 +24,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_none.c,v 1.5 2005/06/10 16:11:24 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_none.c,v 1.7 2007/06/11 03:36:54 sam Exp $");
/*
* IEEE 802.11 NULL crypto support.
@@ -51,7 +45,7 @@
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_encap(struct ieee80211_key *, struct mbuf *, uint8_t);
static int none_decap(struct ieee80211_key *, struct mbuf *, int);
static int none_enmic(struct ieee80211_key *, struct mbuf *, int);
static int none_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -91,7 +85,7 @@
}
static int
-none_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct ieee80211com *ic = k->wk_private;
#ifdef IEEE80211_DEBUG
@@ -115,7 +109,7 @@
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];
+ const uint8_t *ivp = (const uint8_t *)&wh[1];
#endif
/*
Index: _ieee80211.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/_ieee80211.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/_ieee80211.h -L sys/net80211/_ieee80211.h -u -r1.1.1.1 -r1.2
--- sys/net80211/_ieee80211.h
+++ sys/net80211/_ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,7 +23,7 @@
* (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: src/sys/net80211/_ieee80211.h,v 1.2.2.1 2005/09/03 22:40:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/_ieee80211.h,v 1.14 2007/09/18 20:46:36 sam Exp $
*/
#ifndef _NET80211__IEEE80211_H_
#define _NET80211__IEEE80211_H_
@@ -39,6 +33,7 @@
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
+ IEEE80211_T_HT, /* high throughput, full GI */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
@@ -51,19 +46,24 @@
IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */
IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */
IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */
+ IEEE80211_MODE_STURBO_A = 7, /* 5GHz, OFDM, 2x clock, static */
+ IEEE80211_MODE_11NA = 8, /* 5GHz, w/ HT */
+ IEEE80211_MODE_11NG = 9, /* 2GHz, w/ HT */
};
-#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO_G+1)
+#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+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 */
+ IEEE80211_M_MONITOR = 8, /* Monitor mode */
+ IEEE80211_M_WDS = 2 /* WDS link */
};
+#define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1)
/*
- * 802.11g protection mode.
+ * 802.11g/802.11n protection mode.
*/
enum ieee80211_protmode {
IEEE80211_PROT_NONE = 0, /* no protection */
@@ -103,8 +103,14 @@
* 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 */
+ uint32_t ic_flags; /* see below */
+ uint16_t ic_freq; /* setting in Mhz */
+ uint8_t ic_ieee; /* IEEE channel number */
+ int8_t ic_maxregpower; /* maximum regulatory tx power in dBm */
+ int8_t ic_maxpower; /* maximum tx power in .5 dBm */
+ int8_t ic_minpower; /* minimum tx power in .5 dBm */
+ uint8_t ic_state; /* dynamic state */
+ uint8_t ic_extieee; /* HT40 extension channel number */
};
#define IEEE80211_CHAN_MAX 255
@@ -115,14 +121,29 @@
/* 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) */
+#define IEEE80211_CHAN_TURBO 0x00000010 /* Turbo channel */
+#define IEEE80211_CHAN_CCK 0x00000020 /* CCK channel */
+#define IEEE80211_CHAN_OFDM 0x00000040 /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ 0x00000080 /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ 0x00000100 /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE 0x00000200 /* Only passive scan allowed */
+#define IEEE80211_CHAN_DYN 0x00000400 /* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_GFSK 0x00000800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GSM 0x00001000 /* 900 MHz spectrum channel */
+#define IEEE80211_CHAN_STURBO 0x00002000 /* 11a static turbo channel only */
+#define IEEE80211_CHAN_HALF 0x00004000 /* Half rate channel */
+#define IEEE80211_CHAN_QUARTER 0x00008000 /* Quarter rate channel */
+#define IEEE80211_CHAN_HT20 0x00010000 /* HT 20 channel */
+#define IEEE80211_CHAN_HT40U 0x00020000 /* HT 40 channel w/ ext above */
+#define IEEE80211_CHAN_HT40D 0x00040000 /* HT 40 channel w/ ext below */
+#define IEEE80211_CHAN_DFS 0x00080000 /* DFS required */
+#define IEEE80211_CHAN_4MSXMIT 0x00100000 /* 4ms limit on frame length */
+#define IEEE80211_CHAN_NOADHOC 0x00200000 /* adhoc mode not allowed */
+#define IEEE80211_CHAN_NOHOSTAP 0x00400000 /* hostap mode not allowed */
+#define IEEE80211_CHAN_11D 0x00800000 /* 802.11d required */
+
+#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
+#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
/*
* Useful combinations of channel characteristics.
@@ -137,10 +158,19 @@
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
#define IEEE80211_CHAN_G \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
-#define IEEE80211_CHAN_T \
+#define IEEE80211_CHAN_108A \
(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
#define IEEE80211_CHAN_108G \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+#define IEEE80211_CHAN_ST \
+ (IEEE80211_CHAN_108A | IEEE80211_CHAN_STURBO)
+
+#define IEEE80211_CHAN_ALL \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_GFSK | \
+ IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \
+ IEEE80211_CHAN_HT)
+#define IEEE80211_CHAN_ALLTURBO \
+ (IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
#define IEEE80211_IS_CHAN_FHSS(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
@@ -154,8 +184,10 @@
(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
#define IEEE80211_IS_CHAN_ANYG(_c) \
(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
-#define IEEE80211_IS_CHAN_T(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+#define IEEE80211_IS_CHAN_ST(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)
+#define IEEE80211_IS_CHAN_108A(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)
#define IEEE80211_IS_CHAN_108G(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
@@ -163,12 +195,65 @@
(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
#define IEEE80211_IS_CHAN_5GHZ(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
+#define IEEE80211_IS_CHAN_PASSIVE(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE) != 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)
+#define IEEE80211_IS_CHAN_TURBO(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0)
+#define IEEE80211_IS_CHAN_STURBO(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_STURBO) != 0)
+#define IEEE80211_IS_CHAN_DTURBO(_c) \
+ (((_c)->ic_flags & \
+ (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO)
+#define IEEE80211_IS_CHAN_HALF(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HALF) != 0)
+#define IEEE80211_IS_CHAN_QUARTER(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_QUARTER) != 0)
+#define IEEE80211_IS_CHAN_FULL(_c) \
+ (((_c)->ic_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF)) == 0)
+#define IEEE80211_IS_CHAN_GSM(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_GSM) != 0)
+#define IEEE80211_IS_CHAN_HT(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define IEEE80211_IS_CHAN_HT20(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT20) != 0)
+#define IEEE80211_IS_CHAN_HT40(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT40) != 0)
+#define IEEE80211_IS_CHAN_HT40U(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT40U) != 0)
+#define IEEE80211_IS_CHAN_HT40D(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_HT40D) != 0)
+#define IEEE80211_IS_CHAN_HTA(_c) \
+ (IEEE80211_IS_CHAN_5GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define IEEE80211_IS_CHAN_HTG(_c) \
+ (IEEE80211_IS_CHAN_2GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
+#define IEEE80211_IS_CHAN_DFS(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_DFS) != 0)
+#define IEEE80211_IS_CHAN_NOADHOC(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_NOADHOC) != 0)
+#define IEEE80211_IS_CHAN_NOHOSTAP(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_NOHOSTAP) != 0)
+#define IEEE80211_IS_CHAN_11D(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_11D) != 0)
+
+#define IEEE80211_CHAN2IEEE(_c) (_c)->ic_ieee
+
+/* dynamic state */
+#define IEEE80211_CHANSTATE_RADAR 0x01 /* radar detected */
+#define IEEE80211_CHANSTATE_CACDONE 0x02 /* CAC completed */
+#define IEEE80211_CHANSTATE_NORADAR 0x10 /* post notify on radar clear */
+
+#define IEEE80211_IS_CHAN_RADAR(_c) \
+ (((_c)->ic_state & IEEE80211_CHANSTATE_RADAR) != 0)
+#define IEEE80211_IS_CHAN_CACDONE(_c) \
+ (((_c)->ic_state & IEEE80211_CHANSTATE_CACDONE) != 0)
/* ni_chan encoding for FH phy */
#define IEEE80211_FH_CHANMOD 80
@@ -176,6 +261,9 @@
#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1)
#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD)
+#define IEEE80211_TID_SIZE (WME_NUM_TID+1) /* WME TID's +1 for non-QoS */
+#define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */
+
/*
* 802.11 rate set.
*/
@@ -183,8 +271,40 @@
#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];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
+/*
+ * 802.11n variant of ieee80211_rateset. Instead
+ * legacy rates the entries are MCS rates. We define
+ * the structure such that it can be used interchangeably
+ * with an ieee80211_rateset (modulo structure size).
+ */
+#define IEEE80211_HTRATE_MAXSIZE 127
+
+struct ieee80211_htrateset {
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
+};
+
+#define IEEE80211_RATE_MCS 0x80
+
+/*
+ * Roaming state visible to user space. There are two
+ * thresholds that control whether roaming is considered;
+ * when either is exceeded the 802.11 layer will check
+ * the scan cache for another AP. If the cache is stale
+ * then a scan may be triggered.
+ */
+struct ieee80211_roam {
+ int8_t rssi11a; /* rssi thresh for 11a bss */
+ int8_t rssi11b; /* for 11g sta in 11b bss */
+ int8_t rssi11bOnly; /* for 11b sta */
+ uint8_t pad1;
+ uint8_t rate11a; /* rate thresh for 11a bss */
+ uint8_t rate11b; /* for 11g sta in 11b bss */
+ uint8_t rate11bOnly; /* for 11b sta */
+ uint8_t pad2;
+};
#endif /* _NET80211__IEEE80211_H_ */
Index: ieee80211_xauth.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_xauth.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_xauth.c -L sys/net80211/ieee80211_xauth.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_xauth.c
+++ sys/net80211/ieee80211_xauth.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_xauth.c,v 1.2 2004/12/31 22:42:38 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_xauth.c,v 1.3 2007/06/06 04:56:04 sam Exp $");
/*
* External authenticator placeholder module.
Index: ieee80211_radiotap.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_radiotap.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_radiotap.h -L sys/net80211/ieee80211_radiotap.h -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_radiotap.h
+++ sys/net80211/ieee80211_radiotap.h
@@ -1,5 +1,5 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5.2.1 2006/01/29 07:13:58 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.10 2005/01/04 00:34:58 dyoung Exp $ */
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.10 2007/07/01 06:59:30 thompsa Exp $ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.16 2007/01/06 05:51:15 dyoung Exp $ */
/*-
* Copyright (c) 2003, 2004 David Young. All rights reserved.
@@ -29,14 +29,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
-#ifndef _NET_IF_IEEE80211RADIOTAP_H_
-#define _NET_IF_IEEE80211RADIOTAP_H_
+#ifndef _NET80211_IEEE80211_RADIOTAP_H_
+#define _NET80211_IEEE80211_RADIOTAP_H_
-/* A generic radio capture format is desirable. There is one for
- * Linux, but it is neither rigidly defined (there were not even
- * units given for some fields) nor easily extensible.
+/* A generic radio capture format is desirable. It must be
+ * rigidly defined (e.g., units for fields should be given),
+ * and easily extensible.
*
- * I suggest the following extensible radio capture format. It is
+ * The following is an extensible radio capture format. It is
* based on a bitmap indicating which fields are present.
*
* I am trying to describe precisely what the application programmer
@@ -52,54 +52,57 @@
#endif
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
-/* XXX tcpdump/libpcap do not tolerate variable-length headers,
- * yet, so we pad every radiotap header to 64 bytes. Ugh.
- */
-#define IEEE80211_RADIOTAP_HDRLEN 64
+#define IEEE80211_RADIOTAP_HDRLEN 64 /* XXX deprecated */
-/* The radio capture header precedes the 802.11 header. */
+/*
+ * The radio capture header precedes the 802.11 header.
+ *
+ * Note well: all radiotap fields are little-endian.
+ */
struct ieee80211_radiotap_header {
- u_int8_t it_version; /* Version 0. Only increases
+ uint8_t it_version; /* Version 0. Only increases
* for drastic changes,
* introduction of compatible
* new fields does not count.
*/
- u_int8_t it_pad;
- u_int16_t it_len; /* length of the whole
+ uint8_t it_pad;
+ uint16_t it_len; /* length of the whole
* header in bytes, including
* it_version, it_pad,
* it_len, and data fields.
*/
- u_int32_t it_present; /* A bitmap telling which
+ uint32_t it_present; /* A bitmap telling which
* fields are present. Set bit 31
* (0x80000000) to extend the
* bitmap by another 32 bits.
* Additional extensions are made
* by setting bit 31.
*/
-} __attribute__((__packed__));
+} __packed;
-/* Name Data type Units
+/*
+ * Name Data type Units
* ---- --------- -----
*
- * IEEE80211_RADIOTAP_TSFT u_int64_t microseconds
+ * IEEE80211_RADIOTAP_TSFT uint64_t microseconds
*
* Value in microseconds of the MAC's 64-bit 802.11 Time
* Synchronization Function timer when the first bit of the
* MPDU arrived at the MAC. For received frames, only.
*
- * IEEE80211_RADIOTAP_CHANNEL 2 x u_int16_t MHz, bitmap
+ * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap
*
* Tx/Rx frequency in MHz, followed by flags (see below).
*
- * IEEE80211_RADIOTAP_FHSS u_int16_t see below
+ * IEEE80211_RADIOTAP_FHSS uint16_t see below
*
* For frequency-hopping radios, the hop set (first byte)
* and pattern (second byte).
*
- * IEEE80211_RADIOTAP_RATE u_int8_t 500kb/s
+ * IEEE80211_RADIOTAP_RATE uint8_t 500kb/s or index
*
- * Tx/Rx data rate
+ * Tx/Rx data rate. If bit 0x80 is set then it represents an
+ * an MCS index and not an IEEE rate.
*
* IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from
* one milliwatt (dBm)
@@ -113,30 +116,30 @@
* RF noise power at the antenna, decibel difference from one
* milliwatt.
*
- * IEEE80211_RADIOTAP_DB_ANTSIGNAL u_int8_t decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL uint8_t decibel (dB)
*
* RF signal power at the antenna, decibel difference from an
* arbitrary, fixed reference.
*
- * IEEE80211_RADIOTAP_DB_ANTNOISE u_int8_t decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTNOISE uint8_t decibel (dB)
*
* RF noise power at the antenna, decibel difference from an
* arbitrary, fixed reference point.
*
- * IEEE80211_RADIOTAP_LOCK_QUALITY u_int16_t unitless
+ * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless
*
* Quality of Barker code lock. Unitless. Monotonically
* nondecreasing with "better" lock strength. Called "Signal
* Quality" in datasheets. (Is there a standard way to measure
* this?)
*
- * IEEE80211_RADIOTAP_TX_ATTENUATION u_int16_t unitless
+ * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless
*
* Transmit power expressed as unitless distance from max
* power set at factory calibration. 0 is max power.
* Monotonically nondecreasing with lower power levels.
*
- * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t decibels (dB)
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB)
*
* Transmit power expressed as decibel distance from max power
* set at factory calibration. 0 is max power. Monotonically
@@ -149,19 +152,26 @@
* reference). This is the absolute power level measured at
* the antenna port.
*
- * IEEE80211_RADIOTAP_FLAGS u_int8_t bitmap
+ * IEEE80211_RADIOTAP_FLAGS uint8_t bitmap
*
* Properties of transmitted and received frames. See flags
* defined below.
*
- * IEEE80211_RADIOTAP_ANTENNA u_int8_t antenna index
+ * IEEE80211_RADIOTAP_ANTENNA uint8_t antenna index
*
* Unitless indication of the Rx/Tx antenna for this packet.
* The first antenna is antenna 0.
*
- * IEEE80211_RADIOTAP_FCS u_int32_t data
- *
- * FCS from frame in network byte order.
+ * IEEE80211_RADIOTAP_XCHANNEL uint32_t bitmap
+ * uint16_t MHz
+ * uint8_t channel number
+ * int8_t .5 dBm
+ *
+ * Extended channel specification: flags (see below) followed by
+ * frequency in MHz, the corresponding IEEE channel number, and
+ * finally the maximum regulatory transmit power cap in .5 dBm
+ * units. This property supersedes IEEE80211_RADIOTAP_CHANNEL
+ * and only one of the two should be present.
*/
enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TSFT = 0,
@@ -178,19 +188,28 @@
IEEE80211_RADIOTAP_ANTENNA = 11,
IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+ /* NB: gap for netbsd definitions */
+ IEEE80211_RADIOTAP_XCHANNEL = 18,
IEEE80211_RADIOTAP_EXT = 31,
};
#ifndef _KERNEL
/* Channel flags. */
-#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) */
+#define IEEE80211_CHAN_TURBO 0x00010 /* Turbo channel */
+#define IEEE80211_CHAN_CCK 0x00020 /* CCK channel */
+#define IEEE80211_CHAN_OFDM 0x00040 /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ 0x00080 /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ 0x00100 /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE 0x00200 /* Only passive scan allowed */
+#define IEEE80211_CHAN_DYN 0x00400 /* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_GFSK 0x00800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GSM 0x01000 /* 900 MHz spectrum channel */
+#define IEEE80211_CHAN_STURBO 0x02000 /* 11a static turbo channel only */
+#define IEEE80211_CHAN_HALF 0x04000 /* Half rate channel */
+#define IEEE80211_CHAN_QUARTER 0x08000 /* Quarter rate channel */
+#define IEEE80211_CHAN_HT20 0x10000 /* HT 20 channel */
+#define IEEE80211_CHAN_HT40U 0x20000 /* HT 40 channel w/ ext above */
+#define IEEE80211_CHAN_HT40D 0x40000 /* HT 40 channel w/ ext below */
#endif /* !_KERNEL */
/* For IEEE80211_RADIOTAP_FLAGS */
@@ -213,5 +232,6 @@
* (to 32-bit boundary)
*/
#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* does not pass FCS check */
+#define IEEE80211_RADIOTAP_F_SHORTGI 0x80 /* HT short GI */
-#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */
+#endif /* !_NET80211_IEEE80211_RADIOTAP_H_ */
Index: ieee80211_ioctl.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_ioctl.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_ioctl.h -L sys/net80211/ieee80211_ioctl.h -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_ioctl.h
+++ sys/net80211/ieee80211_ioctl.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,7 +23,7 @@
* (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: src/sys/net80211/ieee80211_ioctl.h,v 1.10.2.4 2005/12/22 19:18:23 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.24.2.2 2007/11/11 17:44:36 sam Exp $
*/
#ifndef _NET80211_IEEE80211_IOCTL_H_
#define _NET80211_IEEE80211_IOCTL_H_
@@ -42,146 +36,179 @@
#include <net80211/ieee80211_crypto.h>
/*
- * Per/node (station) statistics available when operating as an AP.
+ * Per/node (station) statistics.
*/
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 */
+ uint32_t ns_rx_data; /* rx data frames */
+ uint32_t ns_rx_mgmt; /* rx management frames */
+ uint32_t ns_rx_ctrl; /* rx control frames */
+ uint32_t ns_rx_ucast; /* rx unicast frames */
+ uint32_t ns_rx_mcast; /* rx multi/broadcast frames */
+ uint64_t ns_rx_bytes; /* rx data count (bytes) */
+ uint64_t ns_rx_beacons; /* rx beacon frames */
+ uint32_t ns_rx_proberesp; /* rx probe response frames */
+
+ uint32_t ns_rx_dup; /* rx discard 'cuz dup */
+ uint32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */
+ uint32_t ns_rx_wepfail; /* rx wep processing failed */
+ uint32_t ns_rx_demicfail; /* rx demic failed */
+ uint32_t ns_rx_decap; /* rx decapsulation failed */
+ uint32_t ns_rx_defrag; /* rx defragmentation failed */
+ uint32_t ns_rx_disassoc; /* rx disassociation */
+ uint32_t ns_rx_deauth; /* rx deauthentication */
+ uint32_t ns_rx_action; /* rx action */
+ uint32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */
+ uint32_t ns_rx_unauth; /* rx on unauthorized port */
+ uint32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */
+ uint32_t ns_rx_drop; /* rx discard other reason */
+
+ uint32_t ns_tx_data; /* tx data frames */
+ uint32_t ns_tx_mgmt; /* tx management frames */
+ uint32_t ns_tx_ucast; /* tx unicast frames */
+ uint32_t ns_tx_mcast; /* tx multi/broadcast frames */
+ uint64_t ns_tx_bytes; /* tx data count (bytes) */
+ uint32_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 */
+ uint32_t ns_tx_novlantag; /* tx discard 'cuz no tag */
+ uint32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */
- u_int32_t ns_ps_discard; /* ps discard 'cuz of age */
+ uint32_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 */
+ uint32_t ns_tx_assoc; /* [re]associations */
+ uint32_t ns_tx_assoc_fail; /* [re]association failures */
+ uint32_t ns_tx_auth; /* [re]authentications */
+ uint32_t ns_tx_auth_fail; /* [re]authentication failures*/
+ uint32_t ns_tx_deauth; /* deauthentications */
+ uint32_t ns_tx_deauth_code; /* last deauth reason */
+ uint32_t ns_tx_disassoc; /* disassociations */
+ uint32_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 */
- u_int32_t is_rx_wrongbss; /* rx from wrong bssid */
- u_int32_t is_rx_dup; /* rx discard 'cuz dup */
- 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_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 */
- u_int32_t is_rx_elem_toosmall; /* rx element too small */
- u_int32_t is_rx_elem_unknown; /* rx element unknown */
- u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */
- u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */
- u_int32_t is_rx_nodealloc; /* rx frame dropped */
- 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_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_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_tx_fragframes; /* tx frames fragmented */
- u_int32_t is_tx_frags; /* tx fragments created */
- 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_crypto_enmicfail; /* en-MIC 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 */
- u_int32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */
- u_int32_t is_ff_tooshort; /* fast frame rx decap error */
- u_int32_t is_ff_split; /* fast frame rx split error */
- u_int32_t is_ff_decap; /* fast frames decap'd */
- u_int32_t is_ff_encap; /* fast frames encap'd for tx */
- u_int32_t is_rx_badbintval; /* rx frame w/ bogus bintval */
- u_int32_t is_spare[9];
+ uint32_t is_rx_badversion; /* rx frame with bad version */
+ uint32_t is_rx_tooshort; /* rx frame too short */
+ uint32_t is_rx_wrongbss; /* rx from wrong bssid */
+ uint32_t is_rx_dup; /* rx discard 'cuz dup */
+ uint32_t is_rx_wrongdir; /* rx w/ wrong direction */
+ uint32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */
+ uint32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */
+ uint32_t is_rx_noprivacy; /* rx w/ wep but privacy off */
+ uint32_t is_rx_unencrypted; /* rx w/o wep and privacy on */
+ uint32_t is_rx_wepfail; /* rx wep processing failed */
+ uint32_t is_rx_decap; /* rx decapsulation failed */
+ uint32_t is_rx_mgtdiscard; /* rx discard mgt frames */
+ uint32_t is_rx_ctl; /* rx discard ctrl frames */
+ uint32_t is_rx_beacon; /* rx beacon frames */
+ uint32_t is_rx_rstoobig; /* rx rate set truncated */
+ uint32_t is_rx_elem_missing; /* rx required element missing*/
+ uint32_t is_rx_elem_toobig; /* rx element too big */
+ uint32_t is_rx_elem_toosmall; /* rx element too small */
+ uint32_t is_rx_elem_unknown; /* rx element unknown */
+ uint32_t is_rx_badchan; /* rx frame w/ invalid chan */
+ uint32_t is_rx_chanmismatch; /* rx frame chan mismatch */
+ uint32_t is_rx_nodealloc; /* rx frame dropped */
+ uint32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */
+ uint32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */
+ uint32_t is_rx_auth_fail; /* rx sta auth failure */
+ uint32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */
+ uint32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */
+ uint32_t is_rx_assoc_notauth; /* rx assoc w/o auth */
+ uint32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
+ uint32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */
+ uint32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */
+ uint32_t is_rx_deauth; /* rx deauthentication */
+ uint32_t is_rx_disassoc; /* rx disassociation */
+ uint32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/
+ uint32_t is_rx_nobuf; /* rx failed for lack of buf */
+ uint32_t is_rx_decryptcrc; /* rx decrypt failed on crc */
+ uint32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/
+ uint32_t is_rx_bad_auth; /* rx bad auth request */
+ uint32_t is_rx_unauth; /* rx on unauthorized port */
+ uint32_t is_rx_badkeyid; /* rx w/ incorrect keyid */
+ uint32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */
+ uint32_t is_rx_ccmpformat; /* rx format bad (CCMP) */
+ uint32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */
+ uint32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */
+ uint32_t is_rx_tkipformat; /* rx format bad (TKIP) */
+ uint32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */
+ uint32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */
+ uint32_t is_rx_badcipher; /* rx failed 'cuz key type */
+ uint32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */
+ uint32_t is_rx_acl; /* rx discard 'cuz acl policy */
+ uint32_t is_tx_nobuf; /* tx failed for lack of buf */
+ uint32_t is_tx_nonode; /* tx failed for no node */
+ uint32_t is_tx_unknownmgt; /* tx of unknown mgt frame */
+ uint32_t is_tx_badcipher; /* tx failed 'cuz key type */
+ uint32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */
+ uint32_t is_tx_noheadroom; /* tx failed 'cuz no space */
+ uint32_t is_tx_fragframes; /* tx frames fragmented */
+ uint32_t is_tx_frags; /* tx fragments created */
+ uint32_t is_scan_active; /* active scans started */
+ uint32_t is_scan_passive; /* passive scans started */
+ uint32_t is_node_timeout; /* nodes timed out inactivity */
+ uint32_t is_crypto_nomem; /* no memory for crypto ctx */
+ uint32_t is_crypto_tkip; /* tkip crypto done in s/w */
+ uint32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */
+ uint32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */
+ uint32_t is_crypto_tkipcm; /* tkip counter measures */
+ uint32_t is_crypto_ccmp; /* ccmp crypto done in s/w */
+ uint32_t is_crypto_wep; /* wep crypto done in s/w */
+ uint32_t is_crypto_setkey_cipher;/* cipher rejected key */
+ uint32_t is_crypto_setkey_nokey; /* no key index for setkey */
+ uint32_t is_crypto_delkey; /* driver key delete failed */
+ uint32_t is_crypto_badcipher; /* unknown cipher */
+ uint32_t is_crypto_nocipher; /* cipher not available */
+ uint32_t is_crypto_attachfail; /* cipher attach failed */
+ uint32_t is_crypto_swfallback; /* cipher fallback to s/w */
+ uint32_t is_crypto_keyfail; /* driver key alloc failed */
+ uint32_t is_crypto_enmicfail; /* en-MIC failed */
+ uint32_t is_ibss_capmismatch; /* merge failed-cap mismatch */
+ uint32_t is_ibss_norate; /* merge failed-rate mismatch */
+ uint32_t is_ps_unassoc; /* ps-poll for unassoc. sta */
+ uint32_t is_ps_badaid; /* ps-poll w/ incorrect aid */
+ uint32_t is_ps_qempty; /* ps-poll w/ nothing to send */
+ uint32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */
+ uint32_t is_ff_tooshort; /* fast frame rx decap error */
+ uint32_t is_ff_split; /* fast frame rx split error */
+ uint32_t is_ff_decap; /* fast frames decap'd */
+ uint32_t is_ff_encap; /* fast frames encap'd for tx */
+ uint32_t is_rx_badbintval; /* rx frame w/ bogus bintval */
+ uint32_t is_rx_demicfail; /* rx demic failed */
+ uint32_t is_rx_defrag; /* rx defragmentation failed */
+ uint32_t is_rx_mgmt; /* rx management frames */
+ uint32_t is_rx_action; /* rx action mgt frames */
+ uint32_t is_amsdu_tooshort; /* A-MSDU rx decap error */
+ uint32_t is_amsdu_split; /* A-MSDU rx split error */
+ uint32_t is_amsdu_decap; /* A-MSDU decap'd */
+ uint32_t is_amsdu_encap; /* A-MSDU encap'd for tx */
+ uint32_t is_ampdu_bar_bad; /* A-MPDU BAR out of window */
+ uint32_t is_ampdu_bar_oow; /* A-MPDU BAR before ADDBA */
+ uint32_t is_ampdu_bar_move; /* A-MPDU BAR moved window */
+ uint32_t is_ampdu_bar_rx; /* A-MPDU BAR frames handled */
+ uint32_t is_ampdu_rx_flush; /* A-MPDU frames flushed */
+ uint32_t is_ampdu_rx_oor; /* A-MPDU frames out-of-order */
+ uint32_t is_ampdu_rx_copy; /* A-MPDU frames copied down */
+ uint32_t is_ampdu_rx_drop; /* A-MPDU frames dropped */
+ uint32_t is_tx_badstate; /* tx discard state != RUN */
+ uint32_t is_tx_notassoc; /* tx failed, sta not assoc */
+ uint32_t is_tx_classify; /* tx classification failed */
+ uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */
+ uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */
+ uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */
+ uint32_t is_ampdu_rx_age; /* A-MPDU sent up 'cuz of age */
+ uint32_t is_ampdu_rx_move; /* A-MPDU MSDU moved window */
+ uint32_t is_addba_reject; /* ADDBA reject 'cuz disabled */
+ uint32_t is_addba_norequest; /* ADDBA response w/o ADDBA */
+ uint32_t is_addba_badtoken; /* ADDBA response w/ wrong
+ dialogtoken */
+ uint32_t is_ampdu_stop; /* A-MPDU stream stopped */
+ uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */
+ uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */
+ uint32_t is_spare[16];
};
/*
@@ -199,20 +226,20 @@
* 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.
+ * 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;
+ uint8_t ik_type; /* key/cipher type */
+ uint8_t ik_pad;
+ uint16_t ik_keyix; /* key index */
+ uint8_t ik_keylen; /* key length in bytes */
+ uint8_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];
+ uint8_t ik_macaddr[IEEE80211_ADDR_LEN];
+ uint64_t ik_keyrsc; /* key receive sequence counter */
+ uint64_t ik_keytsc; /* key transmit sequence counter */
+ uint8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
};
/*
@@ -220,8 +247,8 @@
* 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];
+ uint8_t idk_keyix; /* key index */
+ uint8_t idk_macaddr[IEEE80211_ADDR_LEN];
};
/*
@@ -231,16 +258,16 @@
* ap (to effect a station).
*/
struct ieee80211req_mlme {
- u_int8_t im_op; /* operation to perform */
+ uint8_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_int8_t im_ssid_len; /* length of optional ssid */
- u_int16_t im_reason; /* 802.11 reason code */
- u_int8_t im_macaddr[IEEE80211_ADDR_LEN];
- u_int8_t im_ssid[IEEE80211_NWID_LEN];
+ uint8_t im_ssid_len; /* length of optional ssid */
+ uint16_t im_reason; /* 802.11 reason code */
+ uint8_t im_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t im_ssid[IEEE80211_NWID_LEN];
};
/*
@@ -257,7 +284,7 @@
};
struct ieee80211req_maclist {
- u_int8_t ml_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t ml_macaddr[IEEE80211_ADDR_LEN];
};
/*
@@ -267,7 +294,7 @@
* scanning.
*/
struct ieee80211req_chanlist {
- u_int8_t ic_channels[IEEE80211_CHAN_BYTES];
+ uint8_t ic_channels[IEEE80211_CHAN_BYTES];
};
/*
@@ -281,9 +308,14 @@
/*
* 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];
+struct ieee80211req_wpaie { /* old version w/ only one ie */
+ uint8_t wpa_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
+};
+struct ieee80211req_wpaie2 {
+ uint8_t wpa_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
+ uint8_t rsn_ie[IEEE80211_MAX_OPT_IE];
};
/*
@@ -292,8 +324,8 @@
struct ieee80211req_sta_stats {
union {
/* NB: explicitly force 64-bit alignment */
- u_int8_t macaddr[IEEE80211_ADDR_LEN];
- u_int64_t pad;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint64_t pad;
} is_u;
struct ieee80211_nodestats is_stats;
};
@@ -303,26 +335,29 @@
* 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;
+ uint16_t isi_len; /* length (mult of 4) */
+ uint16_t isi_ie_off; /* offset to IE data */
+ uint16_t isi_ie_len; /* IE length */
+ uint16_t isi_freq; /* MHz */
+ uint32_t isi_flags; /* channel flags */
+ uint16_t isi_state; /* state flags */
+ uint8_t isi_authmode; /* authentication algorithm */
+ int8_t isi_rssi; /* receive signal strength */
+ int8_t isi_noise; /* noise floor */
+ uint8_t isi_capinfo; /* capabilities */
+ uint8_t isi_erp; /* ERP element */
+ uint8_t isi_macaddr[IEEE80211_ADDR_LEN];
+ uint8_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 */
+ uint8_t isi_rates[IEEE80211_RATE_MAXSIZE];
+ uint8_t isi_txrate; /* index to isi_rates[] */
+ uint16_t isi_associd; /* assoc response */
+ uint16_t isi_txpower; /* current tx power */
+ uint16_t isi_vlan; /* vlan tag */
+ /* NB: [IEEE80211_NONQOS_TID] holds seq#'s for non-QoS stations */
+ uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
+ uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
+ uint16_t isi_inact; /* inactivity timer */
/* XXX frag state? */
/* variable length IE data */
};
@@ -334,8 +369,8 @@
struct ieee80211req_sta_req {
union {
/* NB: explicitly force 64-bit alignment */
- u_int8_t macaddr[IEEE80211_ADDR_LEN];
- u_int64_t pad;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint64_t pad;
} is_u;
struct ieee80211req_sta_info info[1]; /* variable length */
};
@@ -344,8 +379,8 @@
* Get/set per-station tx power cap.
*/
struct ieee80211req_sta_txpow {
- u_int8_t it_macaddr[IEEE80211_ADDR_LEN];
- u_int8_t it_txpow;
+ uint8_t it_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t it_txpow;
};
/*
@@ -365,13 +400,14 @@
/* the first member must be matched with struct ifreq */
struct ieee80211req {
char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
- u_int16_t i_type; /* req type */
+ uint16_t i_type; /* req type */
int16_t i_val; /* Index or simple value */
int16_t i_len; /* Index or simple value */
void *i_data; /* Extra data */
};
#define SIOCS80211 _IOW('i', 234, struct ieee80211req)
#define SIOCG80211 _IOWR('i', 235, struct ieee80211req)
+#define SIOCG80211STATS _IOWR('i', 236, struct ifreq)
#define IEEE80211_IOC_SSID 1
#define IEEE80211_IOC_NUMSSIDS 2
@@ -409,7 +445,7 @@
#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
+/* 24 was IEEE80211_IOC_SCAN_RESULTS */
#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 */
@@ -430,7 +466,7 @@
#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 */
+/* 45 was IEEE80211_IOC_STA_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 */
@@ -442,32 +478,72 @@
#define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */
#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */
#define IEEE80211_IOC_PUREG 56 /* pure 11g (no 11b stations) */
+#define IEEE80211_IOC_FF 57 /* ATH fast frames (on, off) */
+#define IEEE80211_IOC_TURBOP 58 /* ATH turbo' (on, off) */
+#define IEEE80211_IOC_BGSCAN 59 /* bg scanning (on, off) */
+#define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */
+#define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */
+#define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */
+#define IEEE80211_IOC_ROAM_RSSI_11A 66 /* rssi threshold in 11a */
+#define IEEE80211_IOC_ROAM_RSSI_11B 67 /* rssi threshold in 11b */
+#define IEEE80211_IOC_ROAM_RSSI_11G 68 /* rssi threshold in 11g */
+#define IEEE80211_IOC_ROAM_RATE_11A 69 /* tx rate threshold in 11a */
+#define IEEE80211_IOC_ROAM_RATE_11B 70 /* tx rate threshold in 11b */
+#define IEEE80211_IOC_ROAM_RATE_11G 71 /* tx rate threshold in 11g */
#define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */
#define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */
#define IEEE80211_IOC_BURST 75 /* packet bursting */
+#define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */
+#define IEEE80211_IOC_BMISSTHRESHOLD 77 /* beacon miss threshold */
+#define IEEE80211_IOC_STA_INFO 78 /* station/neighbor info */
+#define IEEE80211_IOC_WPAIE2 79 /* WPA+RSN info elements */
+#define IEEE80211_IOC_CURCHAN 80 /* current channel */
+#define IEEE80211_IOC_SHORTGI 81 /* 802.11n half GI */
+#define IEEE80211_IOC_AMPDU 82 /* 802.11n A-MPDU (on, off) */
+#define IEEE80211_IOC_AMPDU_LIMIT 83 /* A-MPDU length limit */
+#define IEEE80211_IOC_AMPDU_DENSITY 84 /* A-MPDU density */
+#define IEEE80211_IOC_AMSDU 85 /* 802.11n A-MSDU (on, off) */
+#define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */
+#define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */
+#define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */
+#define IEEE80211_IOC_REGDOMAIN 89 /* regulatory domain */
+#define IEEE80211_IOC_COUNTRYCODE 90 /* ISO country code */
+#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */
+#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */
+#define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */
+#define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */
+#define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/
/*
* Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
+ * Each result is a fixed size structure followed by a variable
+ * length SSID and one or more variable length information elements.
+ * The size of each variable length item is found in the fixed
+ * size structure and the entire length of the record is specified
+ * in isr_len. Result records are rounded to a multiple of 4 bytes.
*/
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];
+ uint16_t isr_len; /* length (mult of 4) */
+ uint16_t isr_ie_off; /* offset to IE data */
+ uint16_t isr_ie_len; /* IE length */
+ uint16_t isr_freq; /* MHz */
+ uint16_t isr_flags; /* channel flags */
+ int8_t isr_noise;
+ int8_t isr_rssi;
+ uint8_t isr_intval; /* beacon interval */
+ uint8_t isr_capinfo; /* capabilities */
+ uint8_t isr_erp; /* ERP element */
+ uint8_t isr_bssid[IEEE80211_ADDR_LEN];
+ uint8_t isr_nrates;
+ uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
+ uint8_t isr_ssid_len; /* SSID length */
/* variable length SSID followed by IE data */
};
-#define SIOCG80211STATS _IOWR('i', 236, struct ifreq)
+struct ieee80211_clone_params {
+ char icp_parent[IFNAMSIZ]; /* parent device */
+ int icp_opmode; /* operating mode */
+};
#endif /* __FreeBSD__ */
#endif /* _NET80211_IEEE80211_IOCTL_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_scan.c
@@ -0,0 +1,994 @@
+/*-
+ * Copyright (c) 2002-2007 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.
+ *
+ * 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>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_scan.c,v 1.3.2.1 2007/10/31 16:53:44 sam Exp $");
+
+/*
+ * IEEE 802.11 scanning support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+struct scan_state {
+ struct ieee80211_scan_state base; /* public state */
+
+ u_int ss_iflags; /* flags used internally */
+#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */
+#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */
+#define ISCAN_CANCEL 0x0004 /* cancel current scan */
+#define ISCAN_START 0x0008 /* 1st time through next_scan */
+ unsigned long ss_chanmindwell; /* min dwell on curchan */
+ unsigned long ss_scanend; /* time scan must stop */
+ u_int ss_duration; /* duration for next scan */
+ struct callout ss_scan_timer; /* scan timer */
+};
+#define SCAN_PRIVATE(ss) ((struct scan_state *) ss)
+
+/*
+ * Amount of time to go off-channel during a background
+ * scan. This value should be large enough to catch most
+ * ap's but short enough that we can return on-channel
+ * before our listen interval expires.
+ *
+ * XXX tunable
+ * XXX check against configured listen interval
+ */
+#define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150)
+
+/*
+ * Roaming-related defaults. RSSI thresholds are as returned by the
+ * driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e
+ * .5M units).
+ */
+#define ROAM_RSSI_11A_DEFAULT 14 /* rssi threshold for 11a bss */
+#define ROAM_RSSI_11B_DEFAULT 14 /* rssi threshold for 11b bss */
+#define ROAM_RSSI_11BONLY_DEFAULT 14 /* rssi threshold for 11b-only bss */
+#define ROAM_RATE_11A_DEFAULT 2*12 /* tx rate thresh for 11a bss */
+#define ROAM_RATE_11B_DEFAULT 2*5 /* tx rate thresh for 11b bss */
+#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* tx rate thresh for 11b-only bss */
+
+static void scan_restart_pwrsav(void *);
+static void scan_curchan(struct ieee80211com *, unsigned long);
+static void scan_mindwell(struct ieee80211com *);
+static void scan_next(void *);
+
+MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
+
+void
+ieee80211_scan_attach(struct ieee80211com *ic)
+{
+ struct scan_state *ss;
+
+ ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+ MALLOC(ss, struct scan_state *, sizeof(struct scan_state),
+ M_80211_SCAN, M_NOWAIT | M_ZERO);
+ if (ss == NULL) {
+ ic->ic_scan = NULL;
+ return;
+ }
+ callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE);
+ ic->ic_scan = &ss->base;
+
+ ic->ic_scan_curchan = scan_curchan;
+ ic->ic_scan_mindwell = scan_mindwell;
+
+ ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
+ ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
+ ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
+ ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT;
+ ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT;
+ ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT;
+ ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT;
+ ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT;
+ ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;
+}
+
+void
+ieee80211_scan_detach(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss != NULL) {
+ callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer);
+ if (ss->ss_ops != NULL) {
+ ss->ss_ops->scan_detach(ss);
+ ss->ss_ops = NULL;
+ }
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ ic->ic_scan = NULL;
+ FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
+ }
+}
+
+/*
+ * Simple-minded scanner module support.
+ */
+#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1)
+
+static const char *scan_modnames[IEEE80211_SCANNER_MAX] = {
+ "wlan_scan_sta", /* IEEE80211_M_IBSS */
+ "wlan_scan_sta", /* IEEE80211_M_STA */
+ "wlan_scan_wds", /* IEEE80211_M_WDS */
+ "wlan_scan_sta", /* IEEE80211_M_AHDEMO */
+ "wlan_scan_4", /* n/a */
+ "wlan_scan_5", /* n/a */
+ "wlan_scan_ap", /* IEEE80211_M_HOSTAP */
+ "wlan_scan_7", /* n/a */
+ "wlan_scan_monitor", /* IEEE80211_M_MONITOR */
+};
+static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];
+
+const struct ieee80211_scanner *
+ieee80211_scanner_get(enum ieee80211_opmode mode)
+{
+ if (mode >= IEEE80211_SCANNER_MAX)
+ return NULL;
+ /* NB: avoid monitor mode; there is no scan support */
+ if (mode != IEEE80211_M_MONITOR && scanners[mode] == NULL)
+ ieee80211_load_module(scan_modnames[mode]);
+ return scanners[mode];
+}
+
+void
+ieee80211_scanner_register(enum ieee80211_opmode mode,
+ const struct ieee80211_scanner *scan)
+{
+ if (mode >= IEEE80211_SCANNER_MAX)
+ return;
+ scanners[mode] = scan;
+}
+
+void
+ieee80211_scanner_unregister(enum ieee80211_opmode mode,
+ const struct ieee80211_scanner *scan)
+{
+ if (mode >= IEEE80211_SCANNER_MAX)
+ return;
+ if (scanners[mode] == scan)
+ scanners[mode] = NULL;
+}
+
+void
+ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
+{
+ int m;
+
+ for (m = 0; m < IEEE80211_SCANNER_MAX; m++)
+ if (scanners[m] == scan)
+ scanners[m] = NULL;
+}
+
+/*
+ * Update common scanner state to reflect the current
+ * operating mode. This is called when the state machine
+ * is transitioned to RUN state w/o scanning--e.g. when
+ * operating in monitor mode. The purpose of this is to
+ * ensure later callbacks find ss_ops set to properly
+ * reflect current operating mode.
+ */
+int
+ieee80211_scan_update(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ const struct ieee80211_scanner *scan;
+
+ scan = ieee80211_scanner_get(ic->ic_opmode);
+ IEEE80211_LOCK(ic);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for mode %u\n",
+ __func__, ic->ic_opmode);
+ /* XXX stat */
+ }
+ ss->ss_ic = ic;
+ if (ss->ss_ops != scan) {
+ /* switch scanners; detach old, attach new */
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_detach(ss);
+ if (scan != NULL && !scan->scan_attach(ss)) {
+ /* XXX attach failure */
+ /* XXX stat+msg */
+ ss->ss_ops = NULL;
+ } else
+ ss->ss_ops = scan;
+ }
+ IEEE80211_UNLOCK(ic);
+
+ return (scan != NULL);
+}
+
+static void
+change_channel(struct ieee80211com *ic,
+ struct ieee80211_channel *chan)
+{
+ ic->ic_curchan = chan;
+ ic->ic_set_channel(ic);
+}
+
+static char
+channel_type(const struct ieee80211_channel *c)
+{
+ if (IEEE80211_IS_CHAN_ST(c))
+ return 'S';
+ if (IEEE80211_IS_CHAN_108A(c))
+ return 'T';
+ if (IEEE80211_IS_CHAN_108G(c))
+ return 'G';
+ if (IEEE80211_IS_CHAN_HT(c))
+ return 'n';
+ if (IEEE80211_IS_CHAN_A(c))
+ return 'a';
+ if (IEEE80211_IS_CHAN_ANYG(c))
+ return 'g';
+ if (IEEE80211_IS_CHAN_B(c))
+ return 'b';
+ return 'f';
+}
+
+void
+ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
+{
+ struct ieee80211com *ic = ss->ss_ic;
+ const char *sep;
+ int i;
+
+ sep = "";
+ for (i = ss->ss_next; i < ss->ss_last; i++) {
+ const struct ieee80211_channel *c = ss->ss_chans[i];
+
+ printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c),
+ channel_type(c));
+ sep = ", ";
+ }
+}
+
+/*
+ * Enable station power save mode and start/restart the scanning thread.
+ */
+static void
+scan_restart_pwrsav(void *arg)
+{
+ struct scan_state *ss = (struct scan_state *) arg;
+ struct ieee80211com *ic = ss->base.ss_ic;
+ int delay;
+
+ ieee80211_sta_pwrsave(ic, 1);
+ /*
+ * Use an initial 1ms delay to insure the null
+ * data frame has a chance to go out.
+ * XXX 1ms is a lot, better to trigger scan
+ * on tx complete.
+ */
+ delay = hz/1000;
+ if (delay < 1)
+ delay = 1;
+ ic->ic_scan_start(ic); /* notify driver */
+ ss->ss_scanend = ticks + delay + ss->ss_duration;
+ ss->ss_iflags |= ISCAN_START;
+ callout_reset(&ss->ss_scan_timer, delay, scan_next, ss);
+}
+
+/*
+ * Start/restart scanning. If we're operating in station mode
+ * and associated notify the ap we're going into power save mode
+ * and schedule a callback to initiate the work (where there's a
+ * better context for doing the work). Otherwise, start the scan
+ * directly.
+ */
+static int
+scan_restart(struct scan_state *ss, u_int duration)
+{
+ struct ieee80211com *ic = ss->base.ss_ic;
+ int defer = 0;
+
+ if (ss->base.ss_next == ss->base.ss_last) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no channels to scan\n", __func__);
+ return 0;
+ }
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ ic->ic_state == IEEE80211_S_RUN) {
+ if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ /*
+ * Initiate power save before going off-channel.
+ * Note that we cannot do this directly because
+ * of locking issues; instead we defer it to a
+ * tasklet.
+ */
+ ss->ss_duration = duration;
+ defer = 1;
+ }
+ }
+
+ if (!defer) {
+ ic->ic_scan_start(ic); /* notify driver */
+ ss->ss_scanend = ticks + duration;
+ ss->ss_iflags |= ISCAN_START;
+ callout_reset(&ss->ss_scan_timer, 0, scan_next, ss);
+ } else
+ scan_restart_pwrsav(ss);
+ return 1;
+}
+
+static void
+copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
+ int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ if (nssid > IEEE80211_SCAN_MAX_SSID) {
+ /* XXX printf */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: too many ssid %d, ignoring all of them\n",
+ __func__, nssid);
+ return;
+ }
+ memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0]));
+ ss->ss_nssid = nssid;
+}
+
+/*
+ * Start a scan unless one is already going.
+ */
+int
+ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ const struct ieee80211_scanner *scan;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ scan = ieee80211_scanner_get(ic->ic_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for mode %u\n",
+ __func__, ic->ic_opmode);
+ /* XXX stat */
+ return 0;
+ }
+
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ , __func__
+ , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
+ , duration
+ , ieee80211_phymode_name[ic->ic_des_mode]
+ , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
+ , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
+ , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
+ );
+
+ ss->ss_ic = ic;
+ if (ss->ss_ops != scan) {
+ /* switch scanners; detach old, attach new */
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_detach(ss);
+ if (!scan->scan_attach(ss)) {
+ /* XXX attach failure */
+ /* XXX stat+msg */
+ ss->ss_ops = NULL;
+ } else
+ ss->ss_ops = scan;
+ }
+ if (ss->ss_ops != NULL) {
+ if ((flags & IEEE80211_SCAN_NOSSID) == 0)
+ copy_ssid(ic, ss, nssid, ssids);
+
+ /* NB: top 4 bits for internal use */
+ ss->ss_flags = flags & 0xfff;
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ic->ic_stats.is_scan_active++;
+ else
+ ic->ic_stats.is_scan_passive++;
+ if (flags & IEEE80211_SCAN_FLUSH)
+ ss->ss_ops->scan_flush(ss);
+
+ /* NB: flush frames rx'd before 1st channel change */
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+ ss->ss_ops->scan_start(ss, ic);
+ if (scan_restart(SCAN_PRIVATE(ss), duration))
+ ic->ic_flags |= IEEE80211_F_SCAN;
+ }
+ } else {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: %s scan already in progress\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
+ }
+ IEEE80211_UNLOCK(ic);
+
+ /* NB: racey, does it matter? */
+ return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that
+ * fails then kick off a new scan.
+ */
+int
+ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ int checkscanlist = 0;
+
+ /*
+ * Check if there's a list of scan candidates already.
+ * XXX want more than the ap we're currently associated with
+ */
+
+ IEEE80211_LOCK(ic);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ , __func__
+ , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
+ , duration
+ , ieee80211_phymode_name[ic->ic_des_mode]
+ , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
+ , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
+ , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
+ );
+
+ if (ss->ss_ops != NULL) {
+ /* XXX verify ss_ops matches ic->ic_opmode */
+ if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
+ /*
+ * Update the ssid list and mark flags so if
+ * we call start_scan it doesn't duplicate work.
+ */
+ copy_ssid(ic, ss, nssid, ssids);
+ flags |= IEEE80211_SCAN_NOSSID;
+ }
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
+ time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ /*
+ * We're not currently scanning and the cache is
+ * deemed hot enough to consult. Lock out others
+ * by marking IEEE80211_F_SCAN while we decide if
+ * something is already in the scan cache we can
+ * use. Also discard any frames that might come
+ * in while temporarily marked as scanning.
+ */
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+ ic->ic_flags |= IEEE80211_F_SCAN;
+ checkscanlist = 1;
+ }
+ }
+ IEEE80211_UNLOCK(ic);
+ if (checkscanlist) {
+ const struct ieee80211_scanner *scan;
+
+ scan = ieee80211_scanner_get(ic->ic_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for mode %u\n",
+ __func__, ic->ic_opmode);
+ /* XXX stat */
+ return 0;
+ }
+ if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) {
+ /* found an ap, just clear the flag */
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ return 1;
+ }
+ /* no ap, clear the flag before starting a scan */
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ }
+ return ieee80211_start_scan(ic, flags, duration, nssid, ssids);
+}
+
+/*
+ * Restart a previous scan. If the previous scan completed
+ * then we start again using the existing channel list.
+ */
+int
+ieee80211_bg_scan(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ u_int duration;
+ /*
+ * Go off-channel for a fixed interval that is large
+ * enough to catch most ap's but short enough that
+ * we can return on-channel before our listen interval
+ * expires.
+ */
+ duration = IEEE80211_SCAN_OFFCHANNEL;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: %s scan, ticks %u duration %lu\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
+ ticks, duration);
+
+ if (ss->ss_ops != NULL) {
+ ss->ss_ic = ic;
+ /*
+ * A background scan does not select a new sta; it
+ * just refreshes the scan cache. Also, indicate
+ * the scan logic should follow the beacon schedule:
+ * we go off-channel and scan for a while, then
+ * return to the bss channel to receive a beacon,
+ * then go off-channel again. All during this time
+ * we notify the ap we're in power save mode. When
+ * the scan is complete we leave power save mode.
+ * If any beacon indicates there are frames pending
+ * for us then we drop out of power save mode
+ * (and background scan) automatically by way of the
+ * usual sta power save logic.
+ */
+ ss->ss_flags |= IEEE80211_SCAN_NOPICK
+ | IEEE80211_SCAN_BGSCAN;
+ /* if previous scan completed, restart */
+ if (ss->ss_next >= ss->ss_last) {
+ ss->ss_next = 0;
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ic->ic_stats.is_scan_active++;
+ else
+ ic->ic_stats.is_scan_passive++;
+ ss->ss_ops->scan_restart(ss, ic);
+ }
+ /* NB: flush frames rx'd before 1st channel change */
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+ ss->ss_maxdwell = duration;
+ if (scan_restart(SCAN_PRIVATE(ss), duration)) {
+ ic->ic_flags |= IEEE80211_F_SCAN;
+ ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
+ }
+ } else {
+ /* XXX msg+stat */
+ }
+ } else {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: %s scan already in progress\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
+ }
+ IEEE80211_UNLOCK(ic);
+
+ /* NB: racey, does it matter? */
+ return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Cancel any scan currently going on.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: cancel %s scan\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
+ "active" : "passive");
+
+ /* clear bg scan NOPICK and mark cancel request */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
+ /* force it to fire asap */
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+ 0, scan_next, ss);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Public access to scan_next for drivers that manage
+ * scanning themselves (e.g. for firmware-based devices).
+ */
+void
+ieee80211_scan_next(struct ieee80211com *ic)
+{
+ /*
+ * XXX: We might need/want to decouple context here by either:
+ * callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
+ * or using a taskqueue. Let's see what kind of problems direct
+ * dispatch has for now.
+ */
+ scan_next(ic->ic_scan);
+}
+
+/*
+ * Public access to scan_next for drivers that are not able to scan single
+ * channels (e.g. for firmware-based devices).
+ */
+void
+ieee80211_scan_done(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ ss->ss_next = ss->ss_last; /* all channels are complete */
+ scan_next(ss);
+}
+
+/*
+ * Scan curchan. If this is an active scan and the channel
+ * is not marked passive then send probe request frame(s).
+ * Arrange for the channel change after maxdwell ticks.
+ */
+static void
+scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
+ (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
+ struct ifnet *ifp = ic->ic_ifp;
+ int i;
+
+ /*
+ * Send a broadcast probe request followed by
+ * any specified directed probe requests.
+ * XXX suppress broadcast probe req?
+ * XXX remove dependence on ic/ic->ic_bss
+ * XXX move to policy code?
+ */
+ ieee80211_send_probereq(ic->ic_bss,
+ ic->ic_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ "", 0,
+ ic->ic_opt_ie, ic->ic_opt_ie_len);
+ for (i = 0; i < ss->ss_nssid; i++)
+ ieee80211_send_probereq(ic->ic_bss,
+ ic->ic_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ ss->ss_ssid[i].ssid,
+ ss->ss_ssid[i].len,
+ ic->ic_opt_ie, ic->ic_opt_ie_len);
+ }
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+ maxdwell, scan_next, ss);
+}
+
+/*
+ * Handle mindwell requirements completed; initiate a channel
+ * change to the next channel asap.
+ */
+static void
+scan_mindwell(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
+}
+
+/*
+ * Switch to the next channel marked for scanning.
+ */
+static void
+scan_next(void *arg)
+{
+#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
+ struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
+ struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211_channel *chan;
+ unsigned long maxdwell, scanend;
+ int scanning, scandone;
+
+ IEEE80211_LOCK(ic);
+ scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0;
+ IEEE80211_UNLOCK(ic);
+ if (!scanning) /* canceled */
+ return;
+
+again:
+ scandone = (ss->ss_next >= ss->ss_last) ||
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
+ scanend = SCAN_PRIVATE(ss)->ss_scanend;
+ if (!scandone &&
+ (ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 &&
+ ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) ||
+ time_before(ticks + ss->ss_mindwell, scanend))) {
+ chan = ss->ss_chans[ss->ss_next++];
+
+ /*
+ * Watch for truncation due to the scan end time.
+ */
+ if (time_after(ticks + ss->ss_maxdwell, scanend))
+ maxdwell = scanend - ticks;
+ else
+ maxdwell = ss->ss_maxdwell;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n",
+ __func__,
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ channel_type(ic->ic_curchan),
+ ieee80211_chan2ieee(ic, chan), channel_type(chan),
+ (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
+ (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
+ "active" : "passive",
+ ss->ss_mindwell, maxdwell);
+
+ /*
+ * Potentially change channel and phy mode.
+ */
+ change_channel(ic, chan);
+
+ /*
+ * Scan curchan. Drivers for "intelligent hardware"
+ * override ic_scan_curchan to tell the device to do
+ * the work. Otherwise we manage the work outselves;
+ * sending a probe request (as needed), and arming the
+ * timeout to switch channels after maxdwell ticks.
+ */
+ ic->ic_scan_curchan(ic, maxdwell);
+
+ SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
+ /* clear mindwell lock and initial channel change flush */
+ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+ } else {
+ ic->ic_scan_end(ic); /* notify driver */
+ /*
+ * Record scan complete time. Note that we also do
+ * this when canceled so any background scan will
+ * not be restarted for a while.
+ */
+ if (scandone)
+ ic->ic_lastscan = ticks;
+ /* return to the bss channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ ic->ic_curchan != ic->ic_bsschan)
+ change_channel(ic, ic->ic_bsschan);
+ /* clear internal flags and any indication of a pick */
+ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+ ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
+
+ /*
+ * If not canceled and scan completed, do post-processing.
+ * If the callback function returns 0, then it wants to
+ * continue/restart scanning. Unfortunately we needed to
+ * notify the driver to end the scan above to avoid having
+ * rx frames alter the scan candidate list.
+ */
+ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
+ !ss->ss_ops->scan_end(ss, ic) &&
+ (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
+ time_before(ticks + ss->ss_mindwell, scanend)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: done, restart "
+ "[ticks %u, dwell min %lu scanend %lu]\n",
+ __func__,
+ ticks, ss->ss_mindwell, scanend);
+ ss->ss_next = 0; /* reset to begining */
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ic->ic_stats.is_scan_active++;
+ else
+ ic->ic_stats.is_scan_passive++;
+
+ ic->ic_scan_start(ic); /* notify driver */
+ goto again;
+ } else {
+ /* past here, scandone is ``true'' if not in bg mode */
+ if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
+ scandone = 1;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: %s, "
+ "[ticks %u, dwell min %lu scanend %lu]\n",
+ __func__, scandone ? "done" : "stopped",
+ ticks, ss->ss_mindwell, scanend);
+
+ /*
+ * Clear the SCAN bit first in case frames are
+ * pending on the station power save queue. If
+ * we defer this then the dispatch of the frames
+ * may generate a request to cancel scanning.
+ */
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ /*
+ * Drop out of power save mode when a scan has
+ * completed. If this scan was prematurely terminated
+ * because it is a background scan then don't notify
+ * the ap; we'll either return to scanning after we
+ * receive the beacon frame or we'll drop out of power
+ * save mode because the beacon indicates we have frames
+ * waiting for us.
+ */
+ if (scandone) {
+ ieee80211_sta_pwrsave(ic, 0);
+ if (ss->ss_next >= ss->ss_last) {
+ ieee80211_notify_scan_done(ic);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
+ }
+ }
+ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL;
+ ss->ss_flags &=
+ ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
+ }
+ }
+#undef ISCAN_REP
+}
+
+#ifdef IEEE80211_DEBUG
+static void
+dump_probe_beacon(uint8_t subtype, int isnew,
+ const uint8_t mac[IEEE80211_ADDR_LEN],
+ const struct ieee80211_scanparams *sp)
+{
+
+ printf("[%s] %s%s on chan %u (bss chan %u) ",
+ ether_sprintf(mac), isnew ? "new " : "",
+ ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
+ IEEE80211_CHAN2IEEE(sp->curchan), sp->bchan);
+ ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
+ printf("\n");
+
+ if (isnew) {
+ printf("[%s] caps 0x%x bintval %u erp 0x%x",
+ ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
+ if (sp->country != NULL) {
+#ifdef __FreeBSD__
+ printf(" country info %*D",
+ sp->country[1], sp->country+2, " ");
+#else
+ int i;
+ printf(" country info");
+ for (i = 0; i < sp->country[1]; i++)
+ printf(" %02x", sp->country[i+2]);
+#endif
+ }
+ printf("\n");
+ }
+}
+#endif /* IEEE80211_DEBUG */
+
+/*
+ * Process a beacon or probe response frame.
+ */
+void
+ieee80211_add_scan(struct ieee80211com *ic,
+ const struct ieee80211_scanparams *sp,
+ const struct ieee80211_frame *wh,
+ int subtype, int rssi, int noise, int rstamp)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ /*
+ * Frames received during startup are discarded to avoid
+ * using scan state setup on the initial entry to the timer
+ * callback. This can occur because the device may enable
+ * rx prior to our doing the initial channel change in the
+ * timer routine (we defer the channel change to the timer
+ * code to simplify locking on linux).
+ */
+ if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
+ return;
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
+ dump_probe_beacon(subtype, 1, wh->i_addr2, sp);
+#endif
+ if (ss->ss_ops != NULL &&
+ ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) {
+ /*
+ * If we've reached the min dwell time terminate
+ * the timer so we'll switch to the next channel.
+ */
+ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
+ time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: chan %3d%c min dwell met (%u > %lu)\n",
+ __func__,
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ channel_type(ic->ic_curchan),
+ ticks, SCAN_PRIVATE(ss)->ss_chanmindwell);
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
+ /*
+ * NB: trigger at next clock tick or wait for the
+ * hardware
+ */
+ ic->ic_scan_mindwell(ic);
+ }
+ }
+}
+
+/*
+ * Timeout/age scan cache entries; called from sta timeout
+ * timer (XXX should be self-contained).
+ */
+void
+ieee80211_scan_timeout(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_age(ss);
+}
+
+/*
+ * Mark a scan cache entry after a successful associate.
+ */
+void
+ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss->ss_ops != NULL) {
+ IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN,
+ mac, "%s", __func__);
+ ss->ss_ops->scan_assoc_success(ss, mac);
+ }
+}
+
+/*
+ * Demerit a scan cache entry after failing to associate.
+ */
+void
+ieee80211_scan_assoc_fail(struct ieee80211com *ic,
+ const uint8_t mac[], int reason)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss->ss_ops != NULL) {
+ IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac,
+ "%s: reason %u", __func__, reason);
+ ss->ss_ops->scan_assoc_fail(ss, mac, reason);
+ }
+}
+
+/*
+ * Iterate over the contents of the scan cache.
+ */
+void
+ieee80211_scan_iterate(struct ieee80211com *ic,
+ ieee80211_scan_iter_func *f, void *arg)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_iterate(ss, f, arg);
+}
+
+/*
+ * Flush the contents of the scan cache.
+ */
+void
+ieee80211_scan_flush(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss->ss_ops != NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s\n", __func__);
+ ss->ss_ops->scan_flush(ss);
+ }
+}
Index: ieee80211_var.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_var.h -L sys/net80211/ieee80211_var.h -u -r1.2 -r1.3
--- sys/net80211/ieee80211_var.h
+++ sys/net80211/ieee80211_var.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,7 +23,7 @@
* (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: src/sys/net80211/ieee80211_var.h,v 1.22.2.11 2006/03/13 03:05:48 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.52.2.2 2007/11/29 21:23:22 sam Exp $
*/
#ifndef _NET80211_IEEE80211_VAR_H_
#define _NET80211_IEEE80211_VAR_H_
@@ -56,24 +50,28 @@
#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
#include <net80211/ieee80211_node.h>
+#include <net80211/ieee80211_power.h>
#include <net80211/ieee80211_proto.h>
+#include <net80211/ieee80211_scan.h>
#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */
#define IEEE80211_TXPOWER_MIN 0 /* kill radio */
-#define IEEE80211_DTIM_MAX 15 /* max DTIM period */
-#define IEEE80211_DTIM_MIN 1 /* min DTIM period */
#define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */
-
-/* NB: min+max come from WiFi requirements */
-#define IEEE80211_BINTVAL_MAX 1000 /* max beacon interval (TU's) */
-#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval (TU's) */
#define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TU's) */
#define IEEE80211_BMISS_MAX 2 /* maximum consecutive bmiss allowed */
-#define IEEE80211_SWBMISS_THRESHOLD 50 /* s/w bmiss threshold (TU's) */
#define IEEE80211_HWBMISS_DEFAULT 7 /* h/w bmiss threshold (beacons) */
+#define IEEE80211_BGSCAN_INTVAL_MIN 15 /* min bg scan intvl (secs) */
+#define IEEE80211_BGSCAN_INTVAL_DEFAULT (5*60) /* default bg scan intvl */
+
+#define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */
+#define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */
+
+#define IEEE80211_SCAN_VALID_MIN 10 /* min scan valid time (secs) */
+#define IEEE80211_SCAN_VALID_DEFAULT 60 /* default scan valid time */
+
#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
@@ -83,9 +81,9 @@
#define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX
#define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX
-#define IEEE80211_MS_TO_TU(x) (((x) * 1024) / 1000)
-#define IEEE80211_TU_TO_MS(x) (((x) * 1000) / 1024)
-#define IEEE80211_TU_TO_TICKS(x)(((x) * hz) / 1024)
+#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
+#define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000)
+#define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000))
struct ieee80211_aclator;
struct sysctl_ctx_list;
@@ -93,108 +91,139 @@
struct ieee80211com {
SLIST_ENTRY(ieee80211com) ic_next;
struct ifnet *ic_ifp; /* associated device */
+ ieee80211_com_lock_t ic_comlock; /* state update lock */
+ ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */
struct ieee80211_stats ic_stats; /* statistics */
struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */
- u_int32_t ic_debug; /* debug msg flags */
+ uint32_t ic_debug; /* debug msg flags */
int ic_vap; /* virtual AP index */
- ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */
+ int ic_headroom; /* driver tx headroom needs */
+ enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */
+ enum ieee80211_opmode ic_opmode; /* operation mode */
+ struct ifmedia ic_media; /* interface media config */
+ uint8_t ic_myaddr[IEEE80211_ADDR_LEN];
- int (*ic_reset)(struct ifnet *);
- void (*ic_recv_mgmt)(struct ieee80211com *,
- struct mbuf *, struct ieee80211_node *,
- int, int, u_int32_t);
- int (*ic_send_mgmt)(struct ieee80211com *,
- struct ieee80211_node *, int, int);
- int (*ic_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
- void (*ic_newassoc)(struct ieee80211_node *, int);
- void (*ic_updateslot)(struct ifnet *);
- void (*ic_set_tim)(struct ieee80211_node *, int);
- u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ uint32_t ic_flags; /* state flags */
+ uint32_t ic_flags_ext; /* extended state flags */
+ uint32_t ic_flags_ven; /* vendor state flags */
+ uint32_t ic_caps; /* capabilities */
+ uint32_t ic_htcaps; /* HT capabilities */
+ uint8_t ic_modecaps[2]; /* set of mode capabilities */
+ uint16_t ic_curmode; /* current mode */
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ uint16_t ic_bintval; /* beacon interval */
+ uint16_t ic_lintval; /* listen interval */
+ uint16_t ic_holdover; /* PM hold over duration */
+ uint16_t ic_txpowlimit; /* global tx power limit */
+ int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
+ int ic_ampdu_density;/* A-MPDU density */
+ int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */
+ int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */
+
+ /*
+ * Channel state:
+ *
+ * ic_channels is the set of available channels for the device;
+ * it is setup by the driver
+ * ic_nchans is the number of valid entries in ic_channels
+ * ic_chan_avail is a bit vector of these channels used to check
+ * whether a channel is available w/o searching the channel table.
+ * ic_chan_active is a (potentially) constrained subset of
+ * ic_chan_avail that reflects any mode setting or user-specified
+ * limit on the set of channels to use/scan
+ * ic_curchan is the current channel the device is set to; it may
+ * be different from ic_bsschan when we are off-channel scanning
+ * or otherwise doing background work
+ * ic_bsschan is the channel selected for operation; it may
+ * be undefined (IEEE80211_CHAN_ANYC)
+ * ic_prevchan is a cached ``previous channel'' used to optimize
+ * lookups when switching back+forth between two channels
+ * (e.g. for dynamic turbo)
+ */
+ int ic_nchans; /* # entries in ic_channels */
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
- 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_flags_ext; /* extended state flags */
- u_int32_t ic_caps; /* capabilities */
- u_int16_t ic_modecaps; /* set of mode capabilities */
- u_int16_t ic_curmode; /* current mode */
- enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */
- enum ieee80211_opmode ic_opmode; /* operation mode */
- enum ieee80211_state ic_state; /* 802.11 state */
- enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
+ uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_active[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
+ struct ieee80211_channel *ic_curchan; /* current channel */
+ struct ieee80211_channel *ic_bsschan; /* bss channel */
+ struct ieee80211_channel *ic_prevchan; /* previous channel */
+ int ic_countrycode; /* ISO country code */
+ uint16_t ic_regdomain; /* regulatory domain */
+ uint8_t ic_location; /* unknown, indoor, outdoor */
+
+ struct ieee80211_scan_state *ic_scan; /* scan state */
enum ieee80211_roamingmode ic_roaming; /* roaming mode */
+ int ic_lastdata; /* time of last data frame */
+ int ic_lastscan; /* time last scan completed */
+ int ic_des_nssid; /* # desired ssids */
+ struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */
+ uint8_t ic_des_bssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_channel *ic_des_chan; /* desired channel */
+ int ic_des_mode; /* desired phymode */
+ u_int ic_bgscanidle; /* bg scan idle threshold */
+ u_int ic_bgscanintvl; /* bg scan min interval */
+ u_int ic_scanvalid; /* scan cache valid threshold */
+ struct ieee80211_roam ic_roam; /* sta-mode roaming state */
+
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_int8_t ic_dtim_period; /* DTIM period */
- u_int8_t ic_dtim_count; /* DTIM count for last bcn */
- struct ifmedia ic_media; /* interface media config */
+
+ struct ieee80211_wme_state ic_wme; /* WME/WMM state */
+ const struct ieee80211_aclator *ic_acl; /* aclator glue */
+ void *ic_as; /* private aclator state */
+
+ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
+ uint16_t ic_nonerpsta; /* # non-ERP stations */
+ uint16_t ic_longslotsta; /* # long slot time stations */
+ uint16_t ic_sta_assoc; /* stations associated */
+ uint16_t ic_ht_sta_assoc;/* HT stations associated */
+ uint16_t ic_ht40_sta_assoc;/* HT40 stations associated */
+ uint8_t ic_curhtprotmode;/* HTINFO bss state */
+ enum ieee80211_protmode ic_htprotmode; /* HT protection mode */
+ int ic_lastnonerp; /* last time non-ERP sta noted*/
+ int ic_lastnonht; /* last time non-HT sta noted */
+
+ struct ifqueue ic_mgtq;
+ enum ieee80211_state ic_state; /* 802.11 state */
+ struct callout ic_mgtsend; /* mgmt frame response timer */
+ uint32_t *ic_aid_bitmap; /* association id map */
+ uint16_t ic_max_aid;
+ uint16_t ic_ps_sta; /* stations in power save */
+ uint16_t ic_ps_pending; /* ps sta's w/ pending frames */
+ uint8_t *ic_tim_bitmap; /* power-save stations w/ data*/
+ uint16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */
+ uint8_t ic_dtim_period; /* DTIM period */
+ uint8_t ic_dtim_count; /* DTIM count for last bcn */
struct bpf_if *ic_rawbpf; /* packet filter structure */
struct ieee80211_node *ic_bss; /* information for this node */
- struct ieee80211_channel *ic_ibss_chan;
- struct ieee80211_channel *ic_curchan; /* current channel */
- int ic_fixed_rate; /* index to ic_sup_rates[] */
+ int ic_fixed_rate; /* 802.11 rate or -1 */
int ic_mcast_rate; /* rate for mcast frames */
- u_int16_t ic_rtsthreshold;
- u_int16_t ic_fragthreshold;
- u_int8_t ic_bmissthreshold;
- u_int8_t ic_bmiss_count; /* current beacon miss count */
+ uint16_t ic_rtsthreshold;
+ uint16_t ic_fragthreshold;
+ uint8_t ic_bmissthreshold;
+ uint8_t ic_bmiss_count; /* current beacon miss count */
int ic_bmiss_max; /* max bmiss before scan */
- u_int16_t ic_swbmiss_count;/* beacons in last period */
- u_int16_t ic_swbmiss_period;/* s/w bmiss period */
+ uint16_t ic_swbmiss_count;/* beacons in last period */
+ uint16_t ic_swbmiss_period;/* s/w bmiss period */
struct callout ic_swbmiss; /* s/w beacon miss timer */
- 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_bintval; /* beacon 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_txpowlimit; /* global tx power limit */
- u_int16_t ic_pad0; /* was ic_bmisstimeout */
- 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];
+
+ uint16_t ic_txmin; /* min tx retry count */
+ uint16_t ic_txmax; /* max tx retry count */
+ uint16_t ic_txlifetime; /* tx lifetime */
+ struct callout ic_inact; /* inactivity timer wait */
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.
- */
+ uint16_t ic_opt_ie_len; /* length of ni_opt_ie */
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
@@ -203,14 +232,66 @@
const struct ieee80211_authenticator *ic_auth;
struct eapolcom *ic_ec;
+ /* send/recv 802.11 management frame */
+ int (*ic_send_mgmt)(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+ void (*ic_recv_mgmt)(struct ieee80211com *,
+ struct mbuf *, struct ieee80211_node *,
+ int, int, int, uint32_t);
+ /* send raw 802.11 frame */
+ int (*ic_raw_xmit)(struct ieee80211_node *,
+ struct mbuf *,
+ const struct ieee80211_bpf_params *);
+ /* reset device state after 802.11 parameter/state change */
+ int (*ic_reset)(struct ifnet *);
+ /* [schedule] beacon frame update */
+ void (*ic_update_beacon)(struct ieee80211com *, int);
+ /* update device state for 802.11 slot time change */
+ void (*ic_updateslot)(struct ifnet *);
+ /* new station association callback/notification */
+ void (*ic_newassoc)(struct ieee80211_node *, int);
+ /* node state management */
+ struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*);
+ void (*ic_node_free)(struct ieee80211_node *);
+ void (*ic_node_cleanup)(struct ieee80211_node *);
+ int8_t (*ic_node_getrssi)(const struct ieee80211_node*);
+ void (*ic_node_getsignal)(const struct ieee80211_node*,
+ int8_t *, int8_t *);
+ /* scanning support */
+ void (*ic_scan_start)(struct ieee80211com *);
+ void (*ic_scan_end)(struct ieee80211com *);
+ void (*ic_set_channel)(struct ieee80211com *);
+ void (*ic_scan_curchan)(struct ieee80211com *,
+ unsigned long);
+ void (*ic_scan_mindwell)(struct ieee80211com *);
+ /* per-vap eventually... */
+ int (*ic_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+ void (*ic_set_tim)(struct ieee80211_node *, int);
+
/*
- * 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.
+ * 802.11n ADDBA support. A simple/generic implementation
+ * of A-MPDU tx aggregation is provided; the driver may
+ * override these methods to provide their own support.
+ * A-MPDU rx re-ordering happens automatically if the
+ * driver passes out-of-order frames to ieee80211_input
+ * from an assocated HT station.
*/
- const struct ieee80211_aclator *ic_acl;
- void *ic_as;
- u_int32_t ic_pad[56]; /* future expansion */
+ void (*ic_recv_action)(struct ieee80211_node *,
+ const uint8_t *frm, const uint8_t *efrm);
+ int (*ic_send_action)(struct ieee80211_node *,
+ int category, int action,
+ uint16_t args[4]);
+ /* start/stop doing A-MPDU tx aggregation for a station */
+ int (*ic_addba_request)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *,
+ int dialogtoken, int baparamset,
+ int batimeout);
+ int (*ic_addba_response)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *,
+ int status, int baparamset, int batimeout);
+ void (*ic_addba_stop)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
};
#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
@@ -218,9 +299,10 @@
/* ic_flags */
/* NB: bits 0x4c available */
-#define IEEE80211_F_FF 0x00000001 /* CONF: ATH FF enabled */
-#define IEEE80211_F_TURBOP 0x00000002 /* CONF: ATH Turbo enabled*/
-#define IEEE80211_F_BURST 0x00000004 /* CONF: bursting enabled */
+#define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/
+#define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */
+#define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */
+#define IEEE80211_F_BURST 0x00000008 /* CONF: bursting enabled */
/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */
#define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */
@@ -240,7 +322,6 @@
#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 */
@@ -248,15 +329,33 @@
#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 */
+#define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */
+
+/* Atheros protocol-specific flags */
+#define IEEE80211_F_ATHEROS \
+ (IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP)
+/* Check if an Atheros capability was negotiated for use */
+#define IEEE80211_ATH_CAP(ic, ni, bit) \
+ ((ic)->ic_flags & (ni)->ni_ath_flags & (bit))
/* ic_flags_ext */
-#define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */
+#define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
+#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
/* 0x00000006 reserved */
-#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: enable full bgscan completion */
-#define IEEE80211_FEXT_ERPUPDATE 0x00000200 /* STATUS: update ERP element */
-#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
+#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */
+#define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/
+#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
+#define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */
+#define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
+#define IEEE80211_FEXT_AMPDU_RX 0x00200000 /* CONF: A-MPDU tx supported */
+#define IEEE80211_FEXT_AMSDU_TX 0x00400000 /* CONF: A-MSDU tx supported */
+#define IEEE80211_FEXT_AMSDU_RX 0x00800000 /* CONF: A-MSDU tx supported */
+#define IEEE80211_FEXT_USEHT40 0x01000000 /* CONF: 20/40 use enabled */
+#define IEEE80211_FEXT_PUREN 0x02000000 /* CONF: 11n w/o legacy sta's */
+#define IEEE80211_FEXT_SHORTGI20 0x04000000 /* CONF: short GI in HT20 */
+#define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */
+#define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */
/* ic_caps */
#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
@@ -289,27 +388,44 @@
#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */
+/*
+ * ic_htcaps: HT-specific device/driver capabilities
+ *
+ * NB: the low 16-bits are the 802.11 definitions, the upper
+ * 16-bits are used to define s/w/driver capabilities.
+ */
+#define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */
+#define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */
+/* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */
+#define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */
+
void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
+const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
+ const struct ieee80211_channel *);
void ieee80211_announce(struct ieee80211com *);
+void ieee80211_announce_channels(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]);
+struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
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);
-u_int ieee80211_mhz2ieee(u_int, u_int);
-u_int ieee80211_chan2ieee(struct ieee80211com *, struct ieee80211_channel *);
+int ieee80211_mhz2ieee(u_int, u_int);
+int ieee80211_chan2ieee(struct ieee80211com *,
+ const struct ieee80211_channel *);
u_int ieee80211_ieee2mhz(u_int, u_int);
+struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *,
+ int freq, int flags);
+struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,
+ int ieee, int flags);
int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
-enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
- struct ieee80211_channel *);
+enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
/*
* Key update synchronization methods. XXX should not be visible.
@@ -339,7 +455,7 @@
{
int size = ieee80211_hdrsize(data);
if (ic->ic_flags & IEEE80211_F_DATAPAD)
- size = roundup(size, sizeof(u_int32_t));
+ size = roundup(size, sizeof(uint32_t));
return size;
}
@@ -351,10 +467,31 @@
{
int size = ieee80211_anyhdrsize(data);
if (ic->ic_flags & IEEE80211_F_DATAPAD)
- size = roundup(size, sizeof(u_int32_t));
+ size = roundup(size, sizeof(uint32_t));
return size;
}
+/*
+ * Notify a driver that beacon state has been updated.
+ */
+static __inline void
+ieee80211_beacon_notify(struct ieee80211com *ic, int what)
+{
+ if (ic->ic_state == IEEE80211_S_RUN)
+ ic->ic_update_beacon(ic, what);
+}
+
+/*
+ * Debugging facilities compiled in when IEEE80211_DEBUG is defined.
+ *
+ * The intent is that any problem in the net80211 layer can be
+ * diagnosed by inspecting the statistics (dumped by the wlanstats
+ * program) and/or the msgs generated by net80211. Messages are
+ * broken into functional classes and can be controlled with the
+ * wlandebug program. Certain of these msg groups are for facilities
+ * that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1X).
+ */
+#define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */
#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */
#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */
#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */
@@ -380,6 +517,10 @@
#define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */
#define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */
#define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */
+#define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */
+#define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */
+#define IEEE80211_MSG_WDS 0x00000008 /* WDS handling */
+#define IEEE80211_MSG_IOCTL 0x00000004 /* ioctl handling */
#define IEEE80211_MSG_ANY 0xffffffff /* anything */
@@ -403,7 +544,7 @@
} while (0)
void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...);
void ieee80211_note_mac(struct ieee80211com *ic,
- const u_int8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
+ const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
void ieee80211_note_frame(struct ieee80211com *ic,
const struct ieee80211_frame *wh, const char *fmt, ...);
#define ieee80211_msg_debug(_ic) \
@@ -422,12 +563,44 @@
((_ic)->ic_debug & IEEE80211_MSG_SCAN)
#define ieee80211_msg_assoc(_ic) \
((_ic)->ic_debug & IEEE80211_MSG_ASSOC)
+
+/*
+ * 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)
+
+void ieee80211_discard_frame(struct ieee80211com *,
+ const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+void ieee80211_discard_ie(struct ieee80211com *,
+ const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+void ieee80211_discard_mac(struct ieee80211com *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], const char *type,
+ const char *fmt, ...);
#else
#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
+#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...)
#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...)
#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...)
#define ieee80211_msg_dumppkts(_ic) 0
#define ieee80211_msg(_ic, _m) 0
+
+#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
#endif /* _NET80211_IEEE80211_VAR_H_ */
Index: ieee80211_node.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_node.c -L sys/net80211/ieee80211_node.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.48.2.10 2006/03/13 03:05:47 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.88.2.3 2007/11/28 06:15:03 sam Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -59,26 +53,30 @@
#define IEEE80211_AID_ISSET(b, w) \
((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+#ifdef IEEE80211_DEBUG_REFCNT
+#define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line
+#else
+#define REFCNT_LOC "%s %p<%s> refcnt %d\n", __func__
+#endif
+
+static int ieee80211_sta_join1(struct ieee80211_node *);
+
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 int8_t node_getrssi(const struct ieee80211_node *);
+static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *);
static void ieee80211_setup_node(struct ieee80211_node_table *,
- struct ieee80211_node *, const u_int8_t *);
+ struct ieee80211_node *, const uint8_t *);
static void _ieee80211_free_node(struct ieee80211_node *);
-static void ieee80211_free_allnodes(struct ieee80211_node_table *);
-
-static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *);
-static void ieee80211_timeout_stations(struct ieee80211_node_table *);
-
-static void ieee80211_set_tim(struct ieee80211_node *, int set);
static void ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt, const char *name,
- int inact, int keyixmax,
- void (*timeout)(struct ieee80211_node_table *));
+ int inact, int keymaxix);
+static void ieee80211_node_table_reset(struct ieee80211_node_table *);
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
+static void ieee80211_erp_timeout(struct ieee80211com *);
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
@@ -90,6 +88,7 @@
ic->ic_node_free = node_free;
ic->ic_node_cleanup = node_cleanup;
ic->ic_node_getrssi = node_getrssi;
+ ic->ic_node_getsignal = node_getsignal;
/* default station inactivity timer setings */
ic->ic_inact_init = IEEE80211_INACT_INIT;
@@ -97,9 +96,12 @@
ic->ic_inact_run = IEEE80211_INACT_RUN;
ic->ic_inact_probe = IEEE80211_INACT_PROBE;
+ callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
+
/* NB: driver should override */
ic->ic_max_aid = IEEE80211_AID_DEF;
- ic->ic_set_tim = ieee80211_set_tim;
+
+ ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */
}
void
@@ -109,30 +111,17 @@
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);
+ MALLOC(ic->ic_aid_bitmap, uint32_t *,
+ howmany(ic->ic_max_aid, 32) * sizeof(uint32_t),
+ M_80211_NODE, 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__);
- }
-
ieee80211_node_table_init(ic, &ic->ic_sta, "station",
- IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix,
- ieee80211_timeout_stations);
- ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
- IEEE80211_INACT_SCAN, 0,
- ieee80211_timeout_scan_candidates);
+ IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix);
ieee80211_reset_bss(ic);
/*
@@ -182,16 +171,11 @@
ieee80211_free_node(ic->ic_bss);
ic->ic_bss = NULL;
}
- ieee80211_node_table_cleanup(&ic->ic_scan);
ieee80211_node_table_cleanup(&ic->ic_sta);
if (ic->ic_aid_bitmap != NULL) {
- FREE(ic->ic_aid_bitmap, M_DEVBUF);
+ FREE(ic->ic_aid_bitmap, M_80211_NODE);
ic->ic_aid_bitmap = NULL;
}
- if (ic->ic_tim_bitmap != NULL) {
- FREE(ic->ic_tim_bitmap, M_DEVBUF);
- ic->ic_tim_bitmap = NULL;
- }
}
/*
@@ -205,12 +189,18 @@
ni->ni_flags |= IEEE80211_NODE_AUTH;
ni->ni_inact_reload = ic->ic_inact_run;
+ ni->ni_inact = ni->ni_inact_reload;
}
void
ieee80211_node_unauthorize(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+
ni->ni_flags &= ~IEEE80211_NODE_AUTH;
+ ni->ni_inact_reload = ic->ic_inact_auth;
+ if (ni->ni_inact > ni->ni_inact_reload)
+ ni->ni_inact = ni->ni_inact_reload;
}
/*
@@ -218,134 +208,28 @@
* to insure a consistent view by drivers.
*/
static void
-ieee80211_set_chan(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct ieee80211_channel *chan)
+ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
{
+ struct ieee80211_channel *chan = ic->ic_bsschan;
+
+#if 0
+ KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup"));
+#else
if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */
chan = ic->ic_curchan;
+#endif
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 channel set to scan based on the
- * of available channels and the current PHY mode.
- */
-static void
-ieee80211_reset_scan(struct ieee80211com *ic)
-{
-
- /* 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));
-#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_curchan));
+ if (IEEE80211_IS_CHAN_HT(chan)) {
+ /*
+ * XXX Gotta be careful here; the rate set returned by
+ * ieee80211_get_suprates is actually any HT rate
+ * set so blindly copying it will be bad. We must
+ * install the legacy rate est in ni_rates and the
+ * HT rate set in ni_htrates.
+ */
+ ni->ni_htrates = *ieee80211_get_suphtrates(ic, chan);
}
-#endif /* IEEE80211_DEBUG */
-}
-
-/*
- * Begin an active scan.
- */
-void
-ieee80211_begin_scan(struct ieee80211com *ic, int reset)
-{
-
- ic->ic_scan.nt_scangen++;
- /*
- * In all but hostap mode scanning starts off in
- * an active mode before switching to passive.
- */
- if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
- ic->ic_flags |= IEEE80211_F_ASCAN;
- ic->ic_stats.is_scan_active++;
- } else
- ic->ic_stats.is_scan_passive++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "begin %s scan in %s mode, scangen %u\n",
- (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive",
- ieee80211_phymode_name[ic->ic_curmode], ic->ic_scan.nt_scangen);
- /*
- * Clear scan state and flush any previously seen AP's.
- */
- 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(ic);
-}
-
-/*
- * Switch to the next channel marked for scanning.
- */
-int
-ieee80211_next_scan(struct ieee80211com *ic)
-{
- 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;
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
-
- chan = ic->ic_curchan;
- 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_curchan),
- ieee80211_chan2ieee(ic, chan));
- ic->ic_curchan = chan;
- /*
- * XXX drivers should do this as needed,
- * XXX for now maintain compatibility
- */
- ic->ic_bss->ni_rates =
- ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- return 1;
- }
- } while (chan != ic->ic_curchan);
- ieee80211_end_scan(ic);
- return 0;
+ ni->ni_rates = *ieee80211_get_suprates(ic, chan);
}
/*
@@ -367,7 +251,7 @@
ieee80211_send_probereq(ic->ic_bss,
ic->ic_myaddr, ifp->if_broadcastaddr,
ifp->if_broadcastaddr,
- ic->ic_des_essid, ic->ic_des_esslen,
+ ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len,
ic->ic_opt_ie, ic->ic_opt_ie_len);
} else
ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
@@ -416,9 +300,10 @@
return;
}
IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
- ni->ni_esslen = ic->ic_des_esslen;
- memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
- copy_bss(ni, ic->ic_bss);
+ ni->ni_esslen = ic->ic_des_ssid[0].len;
+ memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen);
+ if (ic->ic_bss != NULL)
+ copy_bss(ni, ic->ic_bss);
ni->ni_intval = ic->ic_bintval;
if (ic->ic_flags & IEEE80211_F_PRIVACY)
ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
@@ -431,8 +316,11 @@
ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */
if (ic->ic_flags & IEEE80211_F_DESBSSID)
IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
- else
- ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */
+ else {
+ get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN);
+ /* clear group bit, add local bit */
+ ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02;
+ }
} else if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
if (ic->ic_flags & IEEE80211_F_DESBSSID)
IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
@@ -442,36 +330,47 @@
/*
* Fix the channel and related attributes.
*/
- ieee80211_set_chan(ic, ni, chan);
- ic->ic_curchan = chan;
- ic->ic_curmode = ieee80211_chan2mode(ic, chan);
+ ic->ic_bsschan = chan;
+ ieee80211_node_set_chan(ic, ni);
+ ic->ic_curmode = ieee80211_chan2mode(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);
+ if (IEEE80211_IS_CHAN_FULL(chan)) {
+ if (IEEE80211_IS_CHAN_ANYG(chan)) {
+ /*
+ * Use a mixed 11b/11g rate set.
+ */
+ ieee80211_set11gbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11G);
+ } else if (IEEE80211_IS_CHAN_B(chan)) {
+ /*
+ * Force pure 11b rate set.
+ */
+ ieee80211_set11gbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11B);
+ }
}
- (void) ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+ (void) ieee80211_sta_join1(ieee80211_ref_node(ni));
}
+/*
+ * Reset bss state on transition to the INIT state.
+ * Clear any stations from the table (they have been
+ * deauth'd) and reset the bss node (clears key, rate
+ * etc. state).
+ */
void
ieee80211_reset_bss(struct ieee80211com *ic)
{
struct ieee80211_node *ni, *obss;
- ieee80211_node_table_reset(&ic->ic_scan);
+ callout_drain(&ic->ic_inact);
ieee80211_node_table_reset(&ic->ic_sta);
+ ieee80211_reset_erp(ic);
- ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
KASSERT(ni != NULL, ("unable to setup inital BSS node"));
obss = ic->ic_bss;
ic->ic_bss = ieee80211_ref_node(ni);
@@ -482,21 +381,71 @@
}
}
-/* XXX tunable */
-#define STA_FAILS_MAX 2 /* assoc failures before ignored */
+static int
+match_ssid(const struct ieee80211_node *ni,
+ int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ int i;
+ for (i = 0; i < nssid; i++) {
+ if (ni->ni_esslen == ssids[i].len &&
+ memcmp(ni->ni_essid, ssids[i].ssid, ni->ni_esslen) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Test a node for suitability/compatibility.
+ */
static int
-ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ uint8_t rate;
+
+ if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
+ return 0;
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ return 0;
+ } else {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+ return 0;
+ }
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+ return 0;
+ } else {
+ /* XXX does this mean privacy is supported or required? */
+ if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+ return 0;
+ }
+ rate = ieee80211_fix_rate(ni, &ni->ni_rates,
+ IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
+ if (rate & IEEE80211_RATE_BASIC)
+ return 0;
+ if (ic->ic_des_nssid != 0 &&
+ !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ return 0;
+ if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ return 0;
+ return 1;
+}
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Display node suitability/compatibility.
+ */
+static void
+check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
{
- u_int8_t rate;
+ uint8_t rate;
int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
fail |= 0x01;
- if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
- ni->ni_chan != ic->ic_des_chan)
- fail |= 0x01;
if (ic->ic_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= 0x02;
@@ -512,241 +461,36 @@
if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= 0x04;
}
- rate = ieee80211_fix_rate(ni, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
+ rate = ieee80211_fix_rate(ni, &ni->ni_rates,
+ IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
fail |= 0x08;
- if (ic->ic_des_esslen != 0 &&
- (ni->ni_esslen != ic->ic_des_esslen ||
- memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0))
+ if (ic->ic_des_nssid != 0 &&
+ !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
fail |= 0x10;
if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
!IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
fail |= 0x20;
- if (ni->ni_fails >= STA_FAILS_MAX)
- fail |= 0x40;
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- printf(" %c %s",
- fail & 0x40 ? '=' : fail & 0x80 ? '^' : fail ? '-' : '+',
- ether_sprintf(ni->ni_macaddr));
- printf(" %s%c", ether_sprintf(ni->ni_bssid),
- fail & 0x20 ? '!' : ' ');
- printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
- fail & 0x01 ? '!' : ' ');
- printf(" %+4d", ni->ni_rssi);
- printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
- fail & 0x08 ? '!' : ' ');
- printf(" %4s%c",
- (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
- (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
- "????",
- fail & 0x02 ? '!' : ' ');
- printf(" %3s%c ",
- (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
- "wep" : "no",
- fail & 0x04 ? '!' : ' ');
- ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
- printf("%s\n", fail & 0x10 ? "!" : "");
- }
-#endif
- 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;
- int weight;
-
- /* 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;
-
- /* compare count of previous failures */
- weight = b->ni_fails - a->ni_fails;
- if (abs(weight) > 1)
- return weight;
-
- rssia = ic->ic_node_getrssi(a);
- rssib = ic->ic_node_getrssi(b);
- if (abs(rssib - rssia) < 5) {
- /* best/max rate preferred if signal level close enough XXX */
- maxa = maxrate(a);
- maxb = maxrate(b);
- if (maxa != maxb)
- 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;
-}
-
-/*
- * Mark an ongoing scan stopped.
- */
-void
-ieee80211_cancel_scan(struct ieee80211com *ic)
-{
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s: end %s scan\n",
- __func__,
- (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive");
-
- ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN);
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
-}
-/*
- * Complete a scan of potential channels.
- */
-void
-ieee80211_end_scan(struct ieee80211com *ic)
-{
- struct ieee80211_node_table *nt = &ic->ic_scan;
- struct ieee80211_node *ni, *selbs;
-
- ieee80211_cancel_scan(ic);
- ieee80211_notify_scan_done(ic);
-
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- 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 unoccupied one. If that fails, pick a channel that
- * looks to be quietest.
- */
- memset(maxrssi, 0, sizeof(maxrssi));
- IEEE80211_NODE_LOCK(nt);
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
- rssi = ic->ic_node_getrssi(ni);
- i = ieee80211_chan2ieee(ic, ni->ni_chan);
- if (rssi > maxrssi[i])
- maxrssi[i] = rssi;
- }
- IEEE80211_NODE_UNLOCK(nt);
- /* XXX select channel more intelligently */
- bestchan = -1;
- for (i = 0; i < IEEE80211_CHAN_MAX; i++)
- 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 (bestchan == -1 ||
- maxrssi[i] < maxrssi[bestchan])
- bestchan = i;
- }
- if (bestchan != -1) {
- ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]);
- 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.
- */
- /* NB: unlocked read should be ok */
- if (TAILQ_FIRST(&nt->nt_node) == NULL) {
- 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) &&
- ic->ic_des_esslen != 0) {
- ieee80211_create_ibss(ic, ic->ic_ibss_chan);
- return;
- }
- /*
- * Decrement the failure counts so entries will be
- * reconsidered the next time around. We really want
- * to do this only for sta's where we've previously
- * had some success.
- */
- IEEE80211_NODE_LOCK(nt);
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
- if (ni->ni_fails)
- ni->ni_fails--;
- IEEE80211_NODE_UNLOCK(nt);
- /*
- * Reset the list of channels to scan and start again.
- */
- ieee80211_reset_scan(ic);
- ic->ic_flags |= IEEE80211_F_SCAN;
- ieee80211_next_scan(ic);
- return;
- }
- selbs = NULL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n",
- "macaddr bssid chan rssi rate flag wep essid");
- IEEE80211_NODE_LOCK(nt);
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
- if (ieee80211_match_bss(ic, ni) == 0) {
- if (selbs == NULL)
- selbs = ni;
- else if (ieee80211_node_compare(ic, ni, selbs) > 0)
- selbs = ni;
- }
- }
- if (selbs != NULL) /* NB: grab ref while dropping lock */
- (void) ieee80211_ref_node(selbs);
- IEEE80211_NODE_UNLOCK(nt);
- if (selbs == NULL)
- goto notfound;
- if (!ieee80211_sta_join(ic, selbs)) {
- ieee80211_free_node(selbs);
- goto notfound;
- }
+ printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr));
+ printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' ');
+ printf(" %3d%c",
+ ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' ');
+ printf(" %+4d", ni->ni_rssi);
+ printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
+ fail & 0x08 ? '!' : ' ');
+ printf(" %4s%c",
+ (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
+ (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
+ "????",
+ fail & 0x02 ? '!' : ' ');
+ printf(" %3s%c ",
+ (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no",
+ fail & 0x04 ? '!' : ' ');
+ ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
+ printf("%s\n", fail & 0x10 ? "!" : "");
}
+#endif /* IEEE80211_DEBUG */
/*
* Handle 802.11 ad hoc network merge. The
@@ -770,9 +514,14 @@
/* unchanged, nothing to do */
return 0;
}
- if (ieee80211_match_bss(ic, ni) != 0) { /* capabilities mismatch */
+ if (!check_bss(ic, ni)) {
+ /* capabilities mismatch */
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
"%s: merge failed, capabilities mismatch\n", __func__);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_assoc(ic))
+ check_bss_debug(ic, ni);
+#endif
ic->ic_stats.is_ibss_capmismatch++;
return 0;
}
@@ -783,26 +532,35 @@
ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
);
- return ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+ return ieee80211_sta_join1(ieee80211_ref_node(ni));
+}
+
+/*
+ * Change the bss channel.
+ */
+void
+ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+ ic->ic_bsschan = c;
+ ic->ic_curchan = ic->ic_bsschan;
+ ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+ ic->ic_set_channel(ic);
}
/*
* 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)
+static int
+ieee80211_sta_join1(struct ieee80211_node *selbs)
{
+ struct ieee80211com *ic = selbs->ni_ic;
struct ieee80211_node *obss;
+ int canreassoc;
if (ic->ic_opmode == IEEE80211_M_IBSS) {
struct ieee80211_node_table *nt;
/*
- * Delete unusable rates; we've already checked
- * that the negotiated rate set is acceptable.
- */
- ieee80211_fix_rate(selbs, IEEE80211_F_DODEL);
- /*
* Fillin the neighbor table; it will already
* exist if we are simply switching mastership.
* XXX ic_sta always setup so this is unnecessary?
@@ -818,28 +576,106 @@
* Committed to selbs, setup state.
*/
obss = ic->ic_bss;
+ /*
+ * Check if old+new node have the same address in which
+ * case we can reassociate when operating in sta mode.
+ */
+ canreassoc = (obss != NULL &&
+ ic->ic_state == IEEE80211_S_RUN &&
+ IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr));
ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */
if (obss != NULL) {
copy_bss(selbs, obss);
ieee80211_free_node(obss);
}
+
+ /*
+ * Delete unusable rates; we've already checked
+ * that the negotiated rate set is acceptable.
+ */
+ ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates,
+ IEEE80211_F_DODEL | IEEE80211_F_JOIN);
+
+ ieee80211_setbsschan(ic, selbs->ni_chan);
/*
* 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);
- ic->ic_curchan = selbs->ni_chan;
ieee80211_reset_erp(ic);
ieee80211_wme_initparams(ic);
- if (ic->ic_opmode == IEEE80211_M_STA)
- ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
- else
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (canreassoc) {
+ /* Reassociate */
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+ } else {
+ /*
+ * Act as if we received a DEAUTH frame in case we
+ * are invoked from the RUN state. This will cause
+ * us to try to re-authenticate if we are operating
+ * as a station.
+ */
+ ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ IEEE80211_FC0_SUBTYPE_DEAUTH);
+ }
+ } else
ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
return 1;
}
+int
+ieee80211_sta_join(struct ieee80211com *ic,
+ const struct ieee80211_scan_entry *se)
+{
+ struct ieee80211_node *ni;
+
+ ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr);
+ if (ni == NULL) {
+ /* XXX msg */
+ return 0;
+ }
+ /*
+ * Expand scan state into node's format.
+ * XXX may not need all this stuff
+ */
+ IEEE80211_ADDR_COPY(ni->ni_bssid, se->se_bssid);
+ ni->ni_esslen = se->se_ssid[1];
+ memcpy(ni->ni_essid, se->se_ssid+2, ni->ni_esslen);
+ ni->ni_rstamp = se->se_rstamp;
+ ni->ni_tstamp.tsf = se->se_tstamp.tsf;
+ ni->ni_intval = se->se_intval;
+ ni->ni_capinfo = se->se_capinfo;
+ ni->ni_chan = se->se_chan;
+ ni->ni_timoff = se->se_timoff;
+ ni->ni_fhdwell = se->se_fhdwell;
+ ni->ni_fhindex = se->se_fhindex;
+ ni->ni_erp = se->se_erp;
+ ni->ni_rssi = se->se_rssi;
+ ni->ni_noise = se->se_noise;
+ if (se->se_htcap_ie != NULL) {
+ ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie);
+ ieee80211_parse_htcap(ni, ni->ni_htcap_ie);
+ }
+ if (se->se_wpa_ie != NULL)
+ ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie);
+ if (se->se_rsn_ie != NULL)
+ ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie);
+ if (se->se_wme_ie != NULL)
+ ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie);
+ if (se->se_ath_ie != NULL)
+ ieee80211_saveath(ni, se->se_ath_ie);
+
+ ic->ic_dtim_period = se->se_dtimperiod;
+ ic->ic_dtim_count = 0;
+
+ /* NB: must be after ni_chan is setup */
+ ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
+ IEEE80211_F_DOSORT);
+
+ return ieee80211_sta_join1(ieee80211_ref_node(ni));
+}
+
/*
* Leave the specified IBSS/BSS network. The node is assumed to
* be passed in with a held reference.
@@ -872,17 +708,23 @@
{
#define N(a) (sizeof(a)/sizeof(a[0]))
struct ieee80211com *ic = ni->ni_ic;
- int i, qlen;
+ int i;
/* NB: preserve ni_table */
if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
- ic->ic_ps_sta--;
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ 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);
}
/*
+ * Cleanup any HT-related state.
+ */
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_ht_node_cleanup(ni);
+ /*
* Clear AREF flag that marks the authorization refcnt bump
* has happened. This is probably not needed as the node
* should always be removed from the table so not found but
@@ -893,13 +735,12 @@
/*
* Drain power save queue and, if needed, clear TIM.
*/
- IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen);
- if (qlen != 0 && ic->ic_set_tim != NULL)
+ if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL)
ic->ic_set_tim(ni, 0);
ni->ni_associd = 0;
if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_DEVBUF);
+ FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
/*
@@ -933,22 +774,33 @@
ic->ic_node_cleanup(ni);
if (ni->ni_wpa_ie != NULL)
- FREE(ni->ni_wpa_ie, M_DEVBUF);
+ FREE(ni->ni_wpa_ie, M_80211_NODE);
+ if (ni->ni_rsn_ie != NULL)
+ FREE(ni->ni_rsn_ie, M_80211_NODE);
if (ni->ni_wme_ie != NULL)
- FREE(ni->ni_wme_ie, M_DEVBUF);
+ FREE(ni->ni_wme_ie, M_80211_NODE);
+ if (ni->ni_ath_ie != NULL)
+ FREE(ni->ni_ath_ie, M_80211_NODE);
IEEE80211_NODE_SAVEQ_DESTROY(ni);
FREE(ni, M_80211_NODE);
}
-static u_int8_t
+static int8_t
node_getrssi(const struct ieee80211_node *ni)
{
return ni->ni_rssi;
}
static void
+node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
+{
+ *rssi = ni->ni_rssi;
+ *noise = ni->ni_noise;
+}
+
+static void
ieee80211_setup_node(struct ieee80211_node_table *nt,
- struct ieee80211_node *ni, const u_int8_t *macaddr)
+ struct ieee80211_node *ni, const uint8_t *macaddr)
{
struct ieee80211com *ic = nt->nt_ic;
int hash;
@@ -966,6 +818,7 @@
ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
ni->ni_inact_reload = nt->nt_inact_init;
ni->ni_inact = ni->ni_inact_reload;
+ ni->ni_ath_defkeyix = 0x7fff;
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
IEEE80211_NODE_LOCK(nt);
@@ -977,7 +830,7 @@
}
struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
{
struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
@@ -997,7 +850,7 @@
* once the send completes.
*/
struct ieee80211_node *
-ieee80211_tmp_node(struct ieee80211com *ic, const u_int8_t *macaddr)
+ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr)
{
struct ieee80211_node *ni;
@@ -1011,7 +864,7 @@
ieee80211_node_initref(ni); /* mark referenced */
ni->ni_txpower = ic->ic_bss->ni_txpower;
/* NB: required by ieee80211_fix_rate */
- ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+ ieee80211_node_set_chan(ic, ni);
ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey,
IEEE80211_KEYIX_NONE);
/* XXX optimize away */
@@ -1027,7 +880,7 @@
}
struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr)
{
struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
@@ -1042,7 +895,7 @@
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);
- ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+ ieee80211_node_set_chan(ic, ni);
ni->ni_rsn = ic->ic_bss->ni_rsn;
} else
ic->ic_stats.is_rx_nodealloc++;
@@ -1052,10 +905,10 @@
static struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const u_int8_t *macaddr, const char *func, int line)
+ const uint8_t *macaddr, const char *func, int line)
#else
_ieee80211_find_node(struct ieee80211_node_table *nt,
- const u_int8_t *macaddr)
+ const uint8_t *macaddr)
#endif
{
struct ieee80211_node *ni;
@@ -1087,9 +940,9 @@
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const u_int8_t *macaddr, const char *func, int line)
+ const uint8_t *macaddr, const char *func, int line)
#else
-ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
#endif
{
struct ieee80211_node *ni;
@@ -1108,7 +961,7 @@
*/
struct ieee80211_node *
ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
- const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
@@ -1121,158 +974,30 @@
ni->ni_rates = ic->ic_bss->ni_rates;
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, 1);
- /* XXX not right for 802.1x/WPA */
- ieee80211_node_authorize(ni);
if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
/*
- * Blindly propagate capabilities based on the
- * local configuration. In particular this permits
- * us to use QoS to disable ACK's.
+ * In adhoc demo mode there are no management
+ * frames to use to discover neighbor capabilities,
+ * so blindly propagate the local configuration
+ * so we can do interesting things (e.g. use
+ * WME to disable ACK's).
*/
if (ic->ic_flags & IEEE80211_F_WME)
ni->ni_flags |= IEEE80211_NODE_QOS;
+ if (ic->ic_flags & IEEE80211_F_FF)
+ ni->ni_flags |= IEEE80211_NODE_FF;
}
+ /* XXX not right for 802.1x/WPA */
+ ieee80211_node_authorize(ni);
}
return ni;
}
-#ifdef IEEE80211_DEBUG
-static void
-dump_probe_beacon(u_int8_t subtype, int isnew,
- const u_int8_t mac[IEEE80211_ADDR_LEN],
- const struct ieee80211_scanparams *sp)
-{
-
- printf("[%s] %s%s on chan %u (bss chan %u) ",
- ether_sprintf(mac), isnew ? "new " : "",
- ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
- sp->chan, sp->bchan);
- ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
- printf("\n");
-
- if (isnew) {
- printf("[%s] caps 0x%x bintval %u erp 0x%x",
- ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
- if (sp->country != NULL) {
-#ifdef __FreeBSD__
- printf(" country info %*D",
- sp->country[1], sp->country+2, " ");
-#else
- int i;
- printf(" country info");
- for (i = 0; i < sp->country[1]; i++)
- printf(" %02x", sp->country[i+2]);
-#endif
- }
- printf("\n");
- }
-}
-#endif /* IEEE80211_DEBUG */
-
-static void
-saveie(u_int8_t **iep, const u_int8_t *ie)
-{
-
- if (ie == NULL)
- *iep = NULL;
- else
- ieee80211_saveie(iep, ie);
-}
-
-/*
- * Process a beacon or probe response frame.
- */
-void
-ieee80211_add_scan(struct ieee80211com *ic,
- const struct ieee80211_scanparams *sp,
- const struct ieee80211_frame *wh,
- int subtype, int rssi, int rstamp)
-{
-#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
- struct ieee80211_node_table *nt = &ic->ic_scan;
- struct ieee80211_node *ni;
- int newnode = 0;
-
- ni = ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL) {
- /*
- * Create a new entry.
- */
- ni = ic->ic_node_alloc(nt);
- if (ni == NULL) {
- ic->ic_stats.is_rx_nodealloc++;
- return;
- }
- ieee80211_setup_node(nt, ni, wh->i_addr2);
- /*
- * XXX 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_set_chan(ic, ni, ic->ic_curchan);
- ni->ni_rsn = ic->ic_bss->ni_rsn;
- newnode = 1;
- }
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
- dump_probe_beacon(subtype, newnode, wh->i_addr2, sp);
-#endif
- /* XXX ap beaconing multiple ssid w/ same bssid */
- if (sp->ssid[1] != 0 &&
- (ISPROBE(subtype) || ni->ni_esslen == 0)) {
- ni->ni_esslen = sp->ssid[1];
- memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
- memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
- }
- 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.data, sp->tstamp, sizeof(ni->ni_tstamp));
- ni->ni_intval = sp->bintval;
- ni->ni_capinfo = sp->capinfo;
- ni->ni_chan = &ic->ic_channels[sp->chan];
- ni->ni_fhdwell = sp->fhdwell;
- ni->ni_fhindex = sp->fhindex;
- ni->ni_erp = sp->erp;
- if (sp->tim != NULL) {
- struct ieee80211_tim_ie *ie =
- (struct ieee80211_tim_ie *) sp->tim;
-
- ni->ni_dtim_count = ie->tim_count;
- ni->ni_dtim_period = ie->tim_period;
- }
- /*
- * 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.
- */
- ni->ni_timoff = sp->timoff;
- /*
- * Record optional information elements that might be
- * used by applications or drivers.
- */
- saveie(&ni->ni_wme_ie, sp->wme);
- saveie(&ni->ni_wpa_ie, sp->wpa);
-
- /* NB: must be after ni_chan is setup */
- ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT);
-
- if (!newnode)
- ieee80211_free_node(ni);
-#undef ISPROBE
-}
-
void
ieee80211_init_neighbor(struct ieee80211_node *ni,
const struct ieee80211_frame *wh,
const struct ieee80211_scanparams *sp)
{
-
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
- "%s: %p<%s>\n", __func__, ni, ether_sprintf(ni->ni_macaddr));
ni->ni_esslen = sp->ssid[1];
memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
@@ -1288,9 +1013,15 @@
ieee80211_saveie(&ni->ni_wme_ie, sp->wme);
if (sp->wpa != NULL)
ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa);
+ if (sp->rsn != NULL)
+ ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn);
+ if (sp->ath != NULL)
+ ieee80211_saveath(ni, sp->ath);
/* NB: must be after ni_chan is setup */
- ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT);
+ ieee80211_setup_rates(ni, sp->rates, sp->xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
}
/*
@@ -1323,6 +1054,9 @@
((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)
+#define IS_BAR(wh) \
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_BAR)
+
/*
* Locate the node for sender, track state, and then pass the
* (referenced) node up to the 802.11 layer for its use. We
@@ -1343,17 +1077,11 @@
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? */
+ nt = &ic->ic_sta;
IEEE80211_NODE_LOCK(nt);
- if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+ if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
ni = _ieee80211_find_node(nt, wh->i_addr1);
else
ni = _ieee80211_find_node(nt, wh->i_addr2);
@@ -1385,19 +1113,14 @@
struct ieee80211_node_table *nt;
struct ieee80211_node *ni;
- 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;
+ nt = &ic->ic_sta;
IEEE80211_NODE_LOCK(nt);
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax)
ni = nt->nt_keyixmap[keyix];
else
ni = NULL;
if (ni == NULL) {
- if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+ if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
ni = _ieee80211_find_node(nt, wh->i_addr1);
else
ni = _ieee80211_find_node(nt, wh->i_addr2);
@@ -1419,13 +1142,13 @@
nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni);
}
}
- } else {
+ } else
ieee80211_ref_node(ni);
- }
IEEE80211_NODE_UNLOCK(nt);
return ni;
}
+#undef IS_BAR
#undef IS_PSPOLL
#undef IS_CTL
@@ -1435,10 +1158,10 @@
*/
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr,
+ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr,
const char *func, int line)
#else
-ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr)
+ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
#endif
{
struct ieee80211_node_table *nt = &ic->ic_sta;
@@ -1454,8 +1177,19 @@
IEEE80211_NODE_LOCK(nt);
if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
ni = ieee80211_ref_node(ic->ic_bss);
- else
+ else {
ni = _ieee80211_find_node(nt, macaddr);
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ (ni != NULL && ni->ni_associd == 0)) {
+ /*
+ * Station is not associated; don't permit the
+ * data frame to be sent by returning NULL. This
+ * is kinda a kludge but the least intrusive way
+ * to add this check into all drivers.
+ */
+ ieee80211_unref_node(&ni); /* NB: null's ni */
+ }
+ }
IEEE80211_NODE_UNLOCK(nt);
if (ni == NULL) {
@@ -1480,60 +1214,21 @@
}
/*
- * Like find but search based on the channel too.
- */
-struct ieee80211_node *
-#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(nt);
- LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
- if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
- ni->ni_chan == chan) {
- ieee80211_ref_node(ni); /* mark referenced */
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
-#ifdef IEEE80211_DEBUG_REFCNT
- "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
- func, line,
-#else
- "%s %p<%s> refcnt %d\n", __func__,
-#endif
- ni, ether_sprintf(ni->ni_macaddr),
- ieee80211_node_refcnt(ni));
- break;
- }
- }
- 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 uint8_t *macaddr, u_int ssidlen, const uint8_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)
+ const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid)
#endif
{
#define MATCH_SSID(ni, ssid, ssidlen) \
(ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0)
- static const u_int8_t zeromac[IEEE80211_ADDR_LEN];
- struct ieee80211com *ic = nt->nt_ic;
+ static const uint8_t zeromac[IEEE80211_ADDR_LEN];
struct ieee80211_node *ni;
int hash;
@@ -1557,14 +1252,8 @@
}
if (ni != NULL) {
ieee80211_ref_node(ni); /* mark referenced */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
-#ifdef IEEE80211_DEBUG_REFCNT
- "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
- func, line,
-#else
- "%s %p<%s> refcnt %d\n", __func__,
-#endif
- ni, ether_sprintf(ni->ni_macaddr),
+ IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
}
IEEE80211_NODE_UNLOCK(nt);
@@ -1748,46 +1437,6 @@
}
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));
- node_reclaim(nt, ni);
- }
- }
- IEEE80211_NODE_UNLOCK(nt);
-
- nt->nt_inact_timer = IEEE80211_INACT_WAIT;
}
/*
@@ -1812,8 +1461,6 @@
ic->ic_opmode == IEEE80211_M_AHDEMO);
IEEE80211_SCAN_LOCK(nt);
gen = ++nt->nt_scangen;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s: %s scangen %u\n", __func__, nt->nt_name, gen);
restart:
IEEE80211_NODE_LOCK(nt);
TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
@@ -1826,7 +1473,8 @@
* will be reclaimed when the last reference to them
* goes away (when frame xmits complete).
*/
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_STA) &&
(ni->ni_flags & IEEE80211_NODE_AREF) == 0)
continue;
/*
@@ -1839,59 +1487,38 @@
m_freem(ni->ni_rxfrag[0]);
ni->ni_rxfrag[0] = NULL;
}
+ if (ni->ni_inact > 0)
+ ni->ni_inact--;
/*
* Special case ourself; we may be idle for extended periods
* of time and regardless reclaiming our state is wrong.
*/
if (ni == ic->ic_bss)
continue;
- ni->ni_inact--;
if (ni->ni_associd != 0 || isadhoc) {
/*
- * 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.
+ * Age frames on the power save queue.
*/
- 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(ni, 0);
- }
- }
+ if (ieee80211_node_saveq_age(ni) != 0 &&
+ IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
+ ic->ic_set_tim != NULL)
+ ic->ic_set_tim(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...).
+ *
+ * XXX don't probe the station unless we've
+ * received a frame from them (and have
+ * some idea of the rates they are capable
+ * of); this will get fixed more properly
+ * soon with better handling of the rate set.
*/
- if (0 < ni->ni_inact &&
- ni->ni_inact <= ic->ic_inact_probe) {
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ (0 < ni->ni_inact &&
+ ni->ni_inact <= ic->ic_inact_probe) &&
+ ni->ni_rates.rs_nrates != 0) {
IEEE80211_NOTE(ic,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE,
ni, "%s",
@@ -1910,7 +1537,8 @@
goto restart;
}
}
- if (ni->ni_inact <= 0) {
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ ni->ni_inact <= 0) {
IEEE80211_NOTE(ic,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni,
"station timed out due to inactivity "
@@ -1927,9 +1555,10 @@
* completed or by ieee80211_node_leave.
*
* 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.
+ * in case the driver takes a lock, as this can result
+ * in a LOR between the node lock and the driver lock.
*/
+ ieee80211_ref_node(ni);
IEEE80211_NODE_UNLOCK(nt);
if (ni->ni_associd != 0) {
IEEE80211_SEND_MGMT(ic, ni,
@@ -1937,6 +1566,7 @@
IEEE80211_REASON_AUTH_EXPIRE);
}
ieee80211_node_leave(ic, ni);
+ ieee80211_free_node(ni);
ic->ic_stats.is_node_timeout++;
goto restart;
}
@@ -1944,8 +1574,23 @@
IEEE80211_NODE_UNLOCK(nt);
IEEE80211_SCAN_UNLOCK(nt);
+}
- nt->nt_inact_timer = IEEE80211_INACT_WAIT;
+void
+ieee80211_node_timeout(void *arg)
+{
+ struct ieee80211com *ic = arg;
+
+ ieee80211_scan_timeout(ic);
+ ieee80211_timeout_stations(&ic->ic_sta);
+
+ IEEE80211_LOCK(ic);
+ ieee80211_erp_timeout(ic);
+ ieee80211_ht_timeout(ic);
+ IEEE80211_UNLOCK(ic);
+
+ callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+ ieee80211_node_timeout, ic);
}
void
@@ -1983,18 +1628,24 @@
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_txseqs[IEEE80211_NONQOS_TID],
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] & 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("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n",
+ ni->ni_rstamp, ni->ni_rssi, ni->ni_noise,
+ 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);
+ printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n",
+ ni->ni_htcap, ni->ni_htparam,
+ ni->ni_htctlchan, ni->ni_ht2ndchan);
+ printf("\thtopmode %x htstbc %x chw %u\n",
+ ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw);
}
void
@@ -2004,6 +1655,13 @@
(ieee80211_iter_func *) ieee80211_dump_node, nt);
}
+void
+ieee80211_notify_erp(struct ieee80211com *ic)
+{
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ ieee80211_beacon_notify(ic, IEEE80211_BEACON_ERP);
+}
+
/*
* Handle a station joining an 11g network.
*/
@@ -2011,6 +1669,8 @@
ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
{
+ IEEE80211_LOCK_ASSERT(ic);
+
/*
* Station isn't capable of short slot time. Bump
* the count of long slot time stations and disable
@@ -2020,11 +1680,18 @@
*/
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);
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ "station needs long slot time, count %d",
+ ic->ic_longslotsta);
/* XXX vap's w/ conflicting needs won't work */
- ieee80211_set_shortslottime(ic, 0);
+ if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) {
+ /*
+ * Don't force slot time when switched to turbo
+ * mode as non-ERP stations won't be present; this
+ * need only be done when on the normal G channel.
+ */
+ ieee80211_set_shortslottime(ic, 0);
+ }
}
/*
* If the new station is not an ERP station
@@ -2033,30 +1700,30 @@
*/
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;
- }
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ "station is !ERP, %d non-ERP stations associated",
+ ic->ic_nonerpsta);
/*
* 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));
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ "%s", "station needs long preamble");
ic->ic_flags |= IEEE80211_F_USEBARKER;
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
}
- if (ic->ic_nonerpsta == 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
+ /*
+ * If protection is configured, enable it.
+ */
+ if (ic->ic_protmode != IEEE80211_PROT_NONE &&
+ ic->ic_nonerpsta == 1 &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: enable use of protection\n", __func__);
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ ieee80211_notify_erp(ic);
+ }
} else
ni->ni_flags |= IEEE80211_NODE_ERP;
}
@@ -2067,8 +1734,9 @@
int newassoc;
if (ni->ni_associd == 0) {
- u_int16_t aid;
+ uint16_t aid;
+ IEEE80211_LOCK(ic);
/*
* It would be good to search the bitmap
* more efficiently, but this will do for now.
@@ -2079,40 +1747,70 @@
break;
}
if (aid >= ic->ic_max_aid) {
+ IEEE80211_UNLOCK(ic);
IEEE80211_SEND_MGMT(ic, ni, resp,
IEEE80211_REASON_ASSOC_TOOMANY);
ieee80211_node_leave(ic, ni);
return;
}
ni->ni_associd = aid | 0xc000;
+ ni->ni_jointime = time_uptime;
IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
ic->ic_sta_assoc++;
- newassoc = 1;
- if (ic->ic_curmode == IEEE80211_MODE_11G)
+
+ if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ ieee80211_ht_node_join(ni);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+ IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
ieee80211_node_join_11g(ic, ni);
+ IEEE80211_UNLOCK(ic);
+
+ newassoc = 1;
} else
newassoc = 0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
- "[%s] station %sassociated at aid %d: %s preamble, %s slot time%s%s\n",
- ether_sprintf(ni->ni_macaddr), newassoc ? "" : "re",
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
IEEE80211_NODE_AID(ni),
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" : ""
+ ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+ ni->ni_flags & IEEE80211_NODE_HT ?
+ (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+ ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+ IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+ ", fast-frames" : "",
+ IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+ ", turbo" : ""
);
/* give driver a chance to setup state like ni_txrate */
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, newassoc);
- ni->ni_inact_reload = ic->ic_inact_auth;
- ni->ni_inact = ni->ni_inact_reload;
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);
+ ieee80211_notify_node_join(ic, ni,
+ resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+}
+
+static void
+disable_protection(struct ieee80211com *ic)
+{
+ KASSERT(ic->ic_nonerpsta == 0 &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0,
+ ("%d non ERP stations, flags 0x%x", ic->ic_nonerpsta,
+ ic->ic_flags_ext));
+
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ /* XXX verify mode? */
+ if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ }
+ ieee80211_notify_erp(ic);
}
/*
@@ -2122,9 +1820,11 @@
ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
{
- KASSERT(ic->ic_curmode == IEEE80211_MODE_11G,
- ("not in 11g, bss %u:0x%x, curmode %u", ni->ni_chan->ic_freq,
- ni->ni_chan->ic_flags, ic->ic_curmode));
+ IEEE80211_LOCK_ASSERT(ic);
+
+ KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan),
+ ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq,
+ ic->ic_bsschan->ic_flags, ic->ic_curmode));
/*
* If a long slot station do the slot time bookkeeping.
@@ -2133,9 +1833,9 @@
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);
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ "long slot time station leaves, count now %d",
+ ic->ic_longslotsta);
if (ic->ic_longslotsta == 0) {
/*
* Re-enable use of short slot time if supported
@@ -2157,27 +1857,44 @@
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_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ "non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
+ (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
+ " (non-ERP sta present)" : "");
+ if (ic->ic_nonerpsta == 0 &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 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;
- }
- ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
+ disable_protection(ic);
}
}
}
/*
+ * Time out presence of an overlapping bss with non-ERP
+ * stations. When operating in hostap mode we listen for
+ * beacons from other stations and if we identify a non-ERP
+ * station is present we enable protection. To identify
+ * when all non-ERP stations are gone we time out this
+ * condition.
+ */
+static void
+ieee80211_erp_timeout(struct ieee80211com *ic)
+{
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
+ time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s\n", "age out non-ERP sta present on channel");
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+ if (ic->ic_nonerpsta == 0)
+ disable_protection(ic);
+ }
+}
+
+/*
* Handle bookkeeping for station deauthentication/disassociation
* when operating as an ap.
*/
@@ -2186,13 +1903,10 @@
{
struct ieee80211_node_table *nt = ni->ni_table;
- 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,
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ "station with aid %d leaves", IEEE80211_NODE_AID(ni));
+
+ KASSERT(ic->ic_opmode != IEEE80211_M_STA,
("unexpected operating mode %u", ic->ic_opmode));
/*
* If node wasn't previously associated all
@@ -2209,12 +1923,18 @@
*/
if (ic->ic_auth->ia_node_leave != NULL)
ic->ic_auth->ia_node_leave(ic, ni);
+
+ IEEE80211_LOCK(ic);
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)
+ if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ ieee80211_ht_node_leave(ni);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+ IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
ieee80211_node_leave_11g(ic, ni);
+ IEEE80211_UNLOCK(ic);
/*
* Cleanup station state. In particular clear various
* state that might otherwise be reused if the node
@@ -2237,38 +1957,30 @@
ieee80211_free_node(ni);
}
-u_int8_t
+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;
+ int rssi_samples;
+ int32_t 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 */
- /* XXX locking */
- TAILQ_FOREACH(ni, &nt->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 */
- /* XXX locking */
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
- rssi_samples++;
- rssi_total += ic->ic_node_getrssi(ni);
- }
- break;
case IEEE80211_M_HOSTAP: /* average of all associated stations */
/* XXX locking */
TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
- if (IEEE80211_AID(ni->ni_associd) != 0) {
- rssi_samples++;
- rssi_total += ic->ic_node_getrssi(ni);
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) {
+ int8_t rssi = ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ rssi_samples++;
+ rssi_total += rssi;
+ }
}
break;
case IEEE80211_M_MONITOR: /* XXX */
@@ -2283,35 +1995,16 @@
#undef NZ
}
-/*
- * Indicate whether there are frames queued for a station in power-save mode.
- */
-static void
-ieee80211_set_tim(struct ieee80211_node *ni, int set)
+void
+ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise)
{
- struct ieee80211com *ic = ni->ni_ic;
- 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);
+ if (ic->ic_bss == NULL) /* NB: shouldn't happen */
+ return;
+ ic->ic_node_getsignal(ic->ic_bss, rssi, noise);
+ /* for non-station mode return avg'd rssi accounting */
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ *rssi = ieee80211_getrssi(ic);
}
/*
@@ -2321,8 +2014,7 @@
static void
ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt,
- const char *name, int inact, int keyixmax,
- void (*timeout)(struct ieee80211_node_table *))
+ const char *name, int inact, int keyixmax)
{
IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
@@ -2336,7 +2028,6 @@
nt->nt_name = name;
nt->nt_scangen = 1;
nt->nt_inact_init = inact;
- nt->nt_timeout = timeout;
nt->nt_keyixmax = keyixmax;
if (nt->nt_keyixmax > 0) {
MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
@@ -2350,7 +2041,7 @@
nt->nt_keyixmap = NULL;
}
-void
+static void
ieee80211_node_table_reset(struct ieee80211_node_table *nt)
{
@@ -2358,7 +2049,6 @@
"%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);
}
--- /dev/null
+++ sys/net80211/ieee80211_power.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2002-2007 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.
+ *
+ * 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: src/sys/net80211/ieee80211_power.h,v 1.1 2007/06/11 03:36:54 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_POWER_H_
+#define _NET80211_IEEE80211_POWER_H_
+
+struct ieee80211com;
+
+void ieee80211_power_attach(struct ieee80211com *);
+void ieee80211_power_lateattach(struct ieee80211com *);
+void ieee80211_power_detach(struct ieee80211com *);
+
+int ieee80211_node_saveq_drain(struct ieee80211_node *);
+int ieee80211_node_saveq_age(struct ieee80211_node *);
+void ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
+void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
+void ieee80211_sta_pwrsave(struct ieee80211com *, int enable);
+
+void ieee80211_power_poll(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_POWER_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_power.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 2002-2007 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.
+ *
+ * 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>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_power.c,v 1.2 2007/09/17 19:07:22 sam Exp $");
+
+/*
+ * IEEE 802.11 power save support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+static void ieee80211_set_tim(struct ieee80211_node *ni, int set);
+
+void
+ieee80211_power_attach(struct ieee80211com *ic)
+{
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_IBSS) {
+ /* NB: driver should override */
+ ic->ic_set_tim = ieee80211_set_tim;
+ }
+}
+
+void
+ieee80211_power_lateattach(struct ieee80211com *ic)
+{
+ /*
+ * Allocate these only if needed. Beware that we
+ * know adhoc mode doesn't support ATIM yet...
+ */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
+ MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ic->ic_tim_bitmap == NULL) {
+ printf("%s: no memory for TIM bitmap!\n", __func__);
+ /* XXX good enough to keep from crashing? */
+ ic->ic_tim_len = 0;
+ }
+ }
+}
+
+void
+ieee80211_power_detach(struct ieee80211com *ic)
+{
+ if (ic->ic_tim_bitmap != NULL) {
+ FREE(ic->ic_tim_bitmap, M_DEVBUF);
+ ic->ic_tim_bitmap = NULL;
+ }
+}
+
+/*
+ * Clear any frames queued on a node's power save queue.
+ * The number of frames that were present is returned.
+ */
+int
+ieee80211_node_saveq_drain(struct ieee80211_node *ni)
+{
+ int qlen;
+
+ IEEE80211_NODE_SAVEQ_LOCK(ni);
+ qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
+ _IF_DRAIN(&ni->ni_savedq);
+ IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+ return qlen;
+}
+
+/*
+ * 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
+ * so we can check and/or adjust only the head of the list.
+ * If a frame's age exceeds the threshold then discard it.
+ * The number of frames discarded is returned so the caller
+ * can check if it needs to adjust the tim.
+ */
+int
+ieee80211_node_saveq_age(struct ieee80211_node *ni)
+{
+ int discard = 0;
+
+ if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+ struct mbuf *m;
+
+ IEEE80211_NODE_SAVEQ_LOCK(ni);
+ while (IF_POLL(&ni->ni_savedq, m) != NULL &&
+ M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+IEEE80211_DPRINTF(ni->ni_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);
+
+ IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni,
+ "discard %u frames for age", discard);
+ IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard);
+ }
+ return discard;
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static void
+ieee80211_set_tim(struct ieee80211_node *ni, int set)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_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--;
+ }
+ /* NB: we know ic is in RUN state so no need to check */
+ ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM);
+ }
+ IEEE80211_BEACON_UNLOCK(ic);
+}
+
+/*
+ * 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 ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ 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(ic, 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.
+ */
+ /* TU -> secs. XXX handle overflow? */
+ age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000;
+ _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
+ IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ "[%s] save frame with age %d, %u now queued\n",
+ ether_sprintf(ni->ni_macaddr), age, qlen);
+
+ if (qlen == 1 && ic->ic_set_tim != NULL)
+ ic->ic_set_tim(ni, 1);
+}
+
+/*
+ * Handle station power-save state change.
+ */
+void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m, *mhead, *mtail;
+ int mcount;
+
+ if (enable) {
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+ ic->ic_ps_sta++;
+ ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ "power save mode on, %u sta's in ps mode", 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_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", 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) {
+ if (ic->ic_set_tim != NULL)
+ ic->ic_set_tim(ni, 0); /* just in case */
+ return;
+ }
+ IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni));
+ /*
+ * Unload the frames from the ps q but don't send them
+ * to the driver yet. We do this in two stages to minimize
+ * locking but also because there's no easy way to preserve
+ * ordering given the existing ifnet access mechanisms.
+ * XXX could be optimized
+ */
+ IEEE80211_NODE_SAVEQ_LOCK(ni);
+ mcount = IEEE80211_NODE_SAVEQ_QLEN(ni);
+ mhead = mtail = NULL;
+ for (;;) {
+ _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+ if (m == NULL)
+ break;
+ if (mhead == NULL) {
+ mhead = m;
+ m->m_nextpkt = NULL;
+ } else
+ mtail->m_nextpkt = m;
+ mtail = m;
+ }
+ IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+ if (mhead != NULL) {
+ /* XXX need different driver interface */
+ /* XXX bypasses q max */
+ IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount);
+ }
+ if (ic->ic_set_tim != NULL)
+ ic->ic_set_tim(ni, 0);
+}
+
+/*
+ * Handle power-save state change in station mode.
+ */
+void
+ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
+{
+ struct ieee80211_node *ni = ic->ic_bss;
+ int qlen;
+
+ if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0)))
+ return;
+
+ IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ "sta power save mode %s", enable ? "on" : "off");
+ if (!enable) {
+ ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+ ieee80211_send_nulldata(ieee80211_ref_node(ni));
+ /*
+ * Flush any queued frames; we can do this immediately
+ * because we know they'll be queued behind the null
+ * data frame we send the ap.
+ * XXX can we use a data frame to take us out of ps?
+ */
+ qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
+ if (qlen != 0) {
+ IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ "flush ps queue, %u packets queued", qlen);
+ for (;;) {
+ struct mbuf *m;
+
+ IEEE80211_NODE_SAVEQ_LOCK(ni);
+ _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+ IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+ if (m == NULL)
+ break;
+ /* XXX need different driver interface */
+ /* XXX bypasses q max */
+ IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+ }
+ }
+ } else {
+ ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+ ieee80211_send_nulldata(ieee80211_ref_node(ni));
+ }
+}
--- /dev/null
+++ sys/net80211/ieee80211_ht.c
@@ -0,0 +1,2002 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * 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>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ht.c,v 1.4.2.3.2.1 2008/02/05 18:19:12 sam Exp $");
+#endif
+
+/*
+ * IEEE 802.11n protocol support.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+/* define here, used throughout file */
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+#define SM(_v, _f) (((_v) << _f##_S) & _f)
+
+/* XXX need max array size */
+/* NB: these are for HT20 w/ long GI */
+const int ieee80211_htrates[16] = {
+ 13, /* IFM_IEEE80211_MCS0 */
+ 26, /* IFM_IEEE80211_MCS1 */
+ 39, /* IFM_IEEE80211_MCS2 */
+ 52, /* IFM_IEEE80211_MCS3 */
+ 78, /* IFM_IEEE80211_MCS4 */
+ 104, /* IFM_IEEE80211_MCS5 */
+ 117, /* IFM_IEEE80211_MCS6 */
+ 130, /* IFM_IEEE80211_MCS7 */
+ 26, /* IFM_IEEE80211_MCS8 */
+ 52, /* IFM_IEEE80211_MCS9 */
+ 78, /* IFM_IEEE80211_MCS10 */
+ 104, /* IFM_IEEE80211_MCS11 */
+ 156, /* IFM_IEEE80211_MCS12 */
+ 208, /* IFM_IEEE80211_MCS13 */
+ 234, /* IFM_IEEE80211_MCS14 */
+ 260, /* IFM_IEEE80211_MCS15 */
+};
+
+static const struct ieee80211_htrateset ieee80211_rateset_11n =
+ { 16, {
+ /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ /* 39 52 78 104 117, 130 */
+ 10, 11, 12, 13, 14, 15 }
+ };
+
+#ifdef IEEE80211_AMPDU_AGE
+/* XXX public for sysctl hookup */
+int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */
+#endif
+int ieee80211_recv_bar_ena = 1;
+
+#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250)
+#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000)
+#define IEEE80211_AGGR_MAXTRIES 3
+
+static int ieee80211_addba_request(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap,
+ int dialogtoken, int baparamset, int batimeout);
+static int ieee80211_addba_response(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap,
+ int code, int baparamset, int batimeout);
+static void ieee80211_addba_stop(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap);
+static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
+ const uint8_t *frm, const uint8_t *efrm);
+
+void
+ieee80211_ht_attach(struct ieee80211com *ic)
+{
+#ifdef IEEE80211_AMPDU_AGE
+ if (ieee80211_ampdu_age == -1)
+ ieee80211_ampdu_age = msecs_to_ticks(500);
+#endif
+
+ /* setup default aggregation policy */
+ ic->ic_recv_action = ieee80211_aggr_recv_action;
+ ic->ic_send_action = ieee80211_send_action;
+ ic->ic_addba_request = ieee80211_addba_request;
+ ic->ic_addba_response = ieee80211_addba_response;
+ ic->ic_addba_stop = ieee80211_addba_stop;
+
+ ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
+ ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+
+ /* XXX get from driver */
+ ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
+ ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
+ ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
+ ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
+
+ if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ /*
+ * Device is HT capable; enable all HT-related
+ * facilities by default.
+ * XXX these choices may be too aggressive.
+ */
+ ic->ic_flags_ext |= IEEE80211_FEXT_HT
+ | IEEE80211_FEXT_HTCOMPAT
+ ;
+ if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
+ ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ /* XXX infer from channel list? */
+ if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
+ if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
+ ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ }
+ /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ }
+}
+
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+ht_announce(struct ieee80211com *ic, int mode,
+ const struct ieee80211_htrateset *rs)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int i, rate, mword;
+
+ if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]);
+ for (i = 0; i < rs->rs_nrates; i++) {
+ mword = ieee80211_rate2media(ic,
+ rs->rs_rates[i] | IEEE80211_RATE_MCS, mode);
+ if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
+ continue;
+ rate = ieee80211_htrates[rs->rs_rates[i]];
+ printf("%s%d%sMbps", (i != 0 ? " " : ""),
+ rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
+ }
+ printf("\n");
+}
+
+void
+ieee80211_ht_announce(struct ieee80211com *ic)
+{
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
+ ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
+ ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
+}
+
+const struct ieee80211_htrateset *
+ieee80211_get_suphtrates(struct ieee80211com *ic,
+ const struct ieee80211_channel *c)
+{
+ return &ieee80211_rateset_11n;
+}
+
+/*
+ * Receive processing.
+ */
+
+/*
+ * Decap the encapsulated A-MSDU frames and dispatch all but
+ * the last for delivery. The last frame is returned for
+ * delivery via the normal path.
+ */
+struct mbuf *
+ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ int framelen;
+ struct mbuf *n;
+
+ /* discard 802.3 header inserted by ieee80211_decap */
+ m_adj(m, sizeof(struct ether_header));
+
+ ic->ic_stats.is_amsdu_decap++;
+
+ for (;;) {
+ /*
+ * Decap the first frame, bust it apart from the
+ * remainder and deliver. We leave the last frame
+ * delivery to the caller (for consistency with other
+ * code paths, could also do it here).
+ */
+ m = ieee80211_decap1(m, &framelen);
+ if (m == NULL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "a-msdu", "%s", "decap failed");
+ ic->ic_stats.is_amsdu_tooshort++;
+ return NULL;
+ }
+ if (m->m_pkthdr.len == framelen)
+ break;
+ n = m_split(m, framelen, M_NOWAIT);
+ if (n == NULL) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "a-msdu",
+ "%s", "unable to split encapsulated frames");
+ ic->ic_stats.is_amsdu_split++;
+ m_freem(m); /* NB: must reclaim */
+ return NULL;
+ }
+ ieee80211_deliver_data(ic, ni, m);
+
+ /*
+ * Remove frame contents; each intermediate frame
+ * is required to be aligned to a 4-byte boundary.
+ */
+ m = n;
+ m_adj(m, roundup2(framelen, 4) - framelen); /* padding */
+ }
+ return m; /* last delivered by caller */
+}
+
+/*
+ * Start A-MPDU rx/re-order processing for the specified TID.
+ */
+static void
+ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
+{
+ memset(rap, 0, sizeof(*rap));
+ rap->rxa_wnd = (bufsiz == 0) ?
+ IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+ rap->rxa_start = start;
+ rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
+}
+
+/*
+ * Purge all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
+{
+ struct mbuf *m;
+ int i;
+
+ for (i = 0; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m != NULL) {
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= m->m_pkthdr.len;
+ m_freem(m);
+ if (--rap->rxa_qframes == 0)
+ break;
+ }
+ }
+ KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0,
+ ("lost %u data, %u frames on ampdu rx q",
+ rap->rxa_qbytes, rap->rxa_qframes));
+}
+
+/*
+ * Stop A-MPDU rx processing for the specified TID.
+ */
+static void
+ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
+{
+ rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+ ampdu_rx_purge(rap);
+}
+
+/*
+ * Dispatch a frame from the A-MPDU reorder queue. The
+ * frame is fed back into ieee80211_input marked with an
+ * M_AMPDU flag so it doesn't come back to us (it also
+ * permits ieee80211_input to optimize re-processing).
+ */
+static __inline void
+ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
+{
+ m->m_flags |= M_AMPDU; /* bypass normal processing */
+ /* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
+ (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0);
+}
+
+/*
+ * Dispatch as many frames as possible from the re-order queue.
+ * Frames will always be "at the front"; we process all frames
+ * up to the first empty slot in the window. On completion we
+ * cleanup state if there are still pending frames in the current
+ * BA window. We assume the frame at slot 0 is already handled
+ * by the caller; we always start at slot 1.
+ */
+static void
+ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m;
+ int i;
+
+ /* flush run of frames */
+ for (i = 1; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m == NULL)
+ break;
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= m->m_pkthdr.len;
+ rap->rxa_qframes--;
+
+ ampdu_dispatch(ni, m);
+ }
+ /*
+ * If frames remain, copy the mbuf pointers down so
+ * they correspond to the offsets in the new window.
+ */
+ if (rap->rxa_qframes != 0) {
+ int n = rap->rxa_qframes, j;
+ for (j = i+1; j < rap->rxa_wnd; j++) {
+ if (rap->rxa_m[j] != NULL) {
+ rap->rxa_m[j-i] = rap->rxa_m[j];
+ rap->rxa_m[j] = NULL;
+ if (--n == 0)
+ break;
+ }
+ }
+ KASSERT(n == 0, ("lost %d frames", n));
+ ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ }
+ /*
+ * Adjust the start of the BA window to
+ * reflect the frames just dispatched.
+ */
+ rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
+ ic->ic_stats.is_ampdu_rx_oor += i;
+}
+
+#ifdef IEEE80211_AMPDU_AGE
+/*
+ * Dispatch all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m;
+ int i;
+
+ for (i = 0; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m == NULL)
+ continue;
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= m->m_pkthdr.len;
+ rap->rxa_qframes--;
+ ic->ic_stats.is_ampdu_rx_oor++;
+
+ ampdu_dispatch(ni, m);
+ if (rap->rxa_qframes == 0)
+ break;
+ }
+}
+#endif /* IEEE80211_AMPDU_AGE */
+
+/*
+ * Dispatch all frames in the A-MPDU re-order queue
+ * preceding the specified sequence number. This logic
+ * handles window moves due to a received MSDU or BAR.
+ */
+static void
+ampdu_rx_flush_upto(struct ieee80211_node *ni,
+ struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m;
+ ieee80211_seq seqno;
+ int i;
+
+ /*
+ * Flush any complete MSDU's with a sequence number lower
+ * than winstart. Gaps may exist. Note that we may actually
+ * dispatch frames past winstart if a run continues; this is
+ * an optimization that avoids having to do a separate pass
+ * to dispatch frames after moving the BA window start.
+ */
+ seqno = rap->rxa_start;
+ for (i = 0; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m != NULL) {
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= m->m_pkthdr.len;
+ rap->rxa_qframes--;
+ ic->ic_stats.is_ampdu_rx_oor++;
+
+ ampdu_dispatch(ni, m);
+ } else {
+ if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart))
+ break;
+ }
+ seqno = IEEE80211_SEQ_INC(seqno);
+ }
+ /*
+ * If frames remain, copy the mbuf pointers down so
+ * they correspond to the offsets in the new window.
+ */
+ if (rap->rxa_qframes != 0) {
+ int n = rap->rxa_qframes, j;
+ for (j = i+1; j < rap->rxa_wnd; j++) {
+ if (rap->rxa_m[j] != NULL) {
+ rap->rxa_m[j-i] = rap->rxa_m[j];
+ rap->rxa_m[j] = NULL;
+ if (--n == 0)
+ break;
+ }
+ }
+ KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d "
+ "BA win <%d:%d> winstart %d",
+ __func__, n, rap->rxa_qframes, i, rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ winstart));
+ ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ }
+ /*
+ * Move the start of the BA window; we use the
+ * sequence number of the last MSDU that was
+ * passed up the stack+1 or winstart if stopped on
+ * a gap in the reorder buffer.
+ */
+ rap->rxa_start = seqno;
+}
+
+/*
+ * Process a received QoS data frame for an HT station. Handle
+ * A-MPDU reordering: if this frame is received out of order
+ * and falls within the BA window hold onto it. Otherwise if
+ * this frame completes a run, flush any pending frames. We
+ * return 1 if the frame is consumed. A 0 is returned if
+ * the frame should be processed normally by the caller.
+ */
+int
+ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
+{
+#define IEEE80211_FC0_QOSDATA \
+ (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
+#define PROCESS 0 /* caller should process frame */
+#define CONSUMED 1 /* frame consumed, caller does nothing */
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_qosframe *wh;
+ struct ieee80211_rx_ampdu *rap;
+ ieee80211_seq rxseq;
+ uint8_t tid;
+ int off;
+
+ KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
+
+ /* NB: m_len known to be sufficient */
+ wh = mtod(m, struct ieee80211_qosframe *);
+ KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data"));
+
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+ tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
+ else
+ tid = wh->i_qos[0];
+ tid &= IEEE80211_QOS_TID;
+ rap = &ni->ni_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+ /*
+ * No ADDBA request yet, don't touch.
+ */
+ return PROCESS;
+ }
+ rxseq = le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+ rap->rxa_nframes++;
+again:
+ if (rxseq == rap->rxa_start) {
+ /*
+ * First frame in window.
+ */
+ if (rap->rxa_qframes != 0) {
+ /*
+ * Dispatch as many packets as we can.
+ */
+ KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup"));
+ ampdu_dispatch(ni, m);
+ ampdu_rx_dispatch(rap, ni);
+ return CONSUMED;
+ } else {
+ /*
+ * In order; advance window and notify
+ * caller to dispatch directly.
+ */
+ rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+ return PROCESS;
+ }
+ }
+ /*
+ * Frame is out of order; store if in the BA window.
+ */
+ /* calculate offset in BA window */
+ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
+ if (off < rap->rxa_wnd) {
+ /*
+ * Common case (hopefully): in the BA window.
+ * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
+ */
+#ifdef IEEE80211_AMPDU_AGE
+ /*
+ * Check for frames sitting too long in the reorder queue.
+ * This should only ever happen if frames are not delivered
+ * without the sender otherwise notifying us (e.g. with a
+ * BAR to move the window). Typically this happens because
+ * of vendor bugs that cause the sequence number to jump.
+ * When this happens we get a gap in the reorder queue that
+ * leaves frame sitting on the queue until they get pushed
+ * out due to window moves. When the vendor does not send
+ * BAR this move only happens due to explicit packet sends
+ *
+ * NB: we only track the time of the oldest frame in the
+ * reorder q; this means that if we flush we might push
+ * frames that still "new"; if this happens then subsequent
+ * frames will result in BA window moves which cost something
+ * but is still better than a big throughput dip.
+ */
+ if (rap->rxa_qframes != 0) {
+ /* XXX honor batimeout? */
+ if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
+ /*
+ * Too long since we received the first
+ * frame; flush the reorder buffer.
+ */
+ if (rap->rxa_qframes != 0) {
+ ic->ic_stats.is_ampdu_rx_age +=
+ rap->rxa_qframes;
+ ampdu_rx_flush(ni, rap);
+ }
+ rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+ return PROCESS;
+ }
+ } else {
+ /*
+ * First frame, start aging timer.
+ */
+ rap->rxa_age = ticks;
+ }
+#endif /* IEEE80211_AMPDU_AGE */
+ /* save packet */
+ if (rap->rxa_m[off] == NULL) {
+ rap->rxa_m[off] = m;
+ rap->rxa_qframes++;
+ rap->rxa_qbytes += m->m_pkthdr.len;
+ ic->ic_stats.is_ampdu_rx_reorder++;
+ } else {
+ IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
+ ni->ni_macaddr, "a-mpdu duplicate",
+ "seqno %u tid %u BA win <%u:%u>",
+ rxseq, tid, rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
+ ic->ic_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ m_freem(m);
+ }
+ return CONSUMED;
+ }
+ if (off < IEEE80211_SEQ_BA_RANGE) {
+ /*
+ * Outside the BA window, but within range;
+ * flush the reorder q and move the window.
+ * Sec 9.10.7.6 b) (D2.04 p.118 line 60)
+ */
+ IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ "move BA win <%u:%u> (%u frames) rxseq %u tid %u",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid);
+ ic->ic_stats.is_ampdu_rx_move++;
+
+ /*
+ * The spec says to flush frames up to but not including:
+ * WinStart_B = rxseq - rap->rxa_wnd + 1
+ * Then insert the frame or notify the caller to process
+ * it immediately. We can safely do this by just starting
+ * over again because we know the frame will now be within
+ * the BA window.
+ */
+ /* NB: rxa_wnd known to be >0 */
+ ampdu_rx_flush_upto(ni, rap,
+ IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1));
+ goto again;
+ } else {
+ /*
+ * Outside the BA window and out of range; toss.
+ * Sec 9.10.7.6 c) (D2.04 p.119 line 16)
+ */
+ IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
+ "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid,
+ wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
+ ic->ic_stats.is_ampdu_rx_drop++;
+ IEEE80211_NODE_STAT(ni, rx_drop);
+ m_freem(m);
+ return CONSUMED;
+ }
+#undef CONSUMED
+#undef PROCESS
+#undef IEEE80211_FC0_QOSDATA
+}
+
+/*
+ * Process a BAR ctl frame. Dispatch all frames up to
+ * the sequence number of the frame. If this frame is
+ * out of range it's discarded.
+ */
+void
+ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame_bar *wh;
+ struct ieee80211_rx_ampdu *rap;
+ ieee80211_seq rxseq;
+ int tid, off;
+
+ if (!ieee80211_recv_bar_ena) {
+#if 0
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N,
+ ni->ni_macaddr, "BAR", "%s", "processing disabled");
+#endif
+ ic->ic_stats.is_ampdu_bar_bad++;
+ return;
+ }
+ wh = mtod(m0, struct ieee80211_frame_bar *);
+ /* XXX check basic BAR */
+ tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID);
+ rap = &ni->ni_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+ /*
+ * No ADDBA request yet, don't touch.
+ */
+ IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
+ ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
+ ic->ic_stats.is_ampdu_bar_bad++;
+ return;
+ }
+ ic->ic_stats.is_ampdu_bar_rx++;
+ rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+ if (rxseq == rap->rxa_start)
+ return;
+ /* calculate offset in BA window */
+ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
+ if (off < IEEE80211_SEQ_BA_RANGE) {
+ /*
+ * Flush the reorder q up to rxseq and move the window.
+ * Sec 9.10.7.6 a) (D2.04 p.119 line 22)
+ */
+ IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid);
+ ic->ic_stats.is_ampdu_bar_move++;
+
+ ampdu_rx_flush_upto(ni, rap, rxseq);
+ if (off >= rap->rxa_wnd) {
+ /*
+ * BAR specifies a window start to the right of BA
+ * window; we must move it explicitly since
+ * ampdu_rx_flush_upto will not.
+ */
+ rap->rxa_start = rxseq;
+ }
+ } else {
+ /*
+ * Out of range; toss.
+ * Sec 9.10.7.6 b) (D2.04 p.119 line 41)
+ */
+ IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
+ "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid,
+ wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
+ ic->ic_stats.is_ampdu_bar_oow++;
+ IEEE80211_NODE_STAT(ni, rx_drop);
+ }
+}
+
+/*
+ * Setup HT-specific state in a node. Called only
+ * when HT use is negotiated so we don't do extra
+ * work for temporary and/or legacy sta's.
+ */
+void
+ieee80211_ht_node_init(struct ieee80211_node *ni, const uint8_t *htcap)
+{
+ struct ieee80211_tx_ampdu *tap;
+ int ac;
+
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ /*
+ * Clean AMPDU state on re-associate. This handles the case
+ * where a station leaves w/o notifying us and then returns
+ * before node is reaped for inactivity.
+ */
+ ieee80211_ht_node_cleanup(ni);
+ }
+ ieee80211_parse_htcap(ni, htcap);
+ for (ac = 0; ac < WME_NUM_AC; ac++) {
+ tap = &ni->ni_tx_ampdu[ac];
+ tap->txa_ac = ac;
+ /* NB: further initialization deferred */
+ }
+ ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
+}
+
+/*
+ * Cleanup HT-specific state in a node. Called only
+ * when HT use has been marked.
+ */
+void
+ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ int i;
+
+ KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
+
+ /* XXX optimize this */
+ for (i = 0; i < WME_NUM_AC; i++) {
+ struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
+ if (tap->txa_flags & IEEE80211_AGGR_SETUP) {
+ /*
+ * Stop BA stream if setup so driver has a chance
+ * to reclaim any resources it might have allocated.
+ */
+ ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
+ IEEE80211_TAPQ_DESTROY(tap);
+ /* NB: clearing NAK means we may re-send ADDBA */
+ tap->txa_flags &=
+ ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
+ }
+ }
+ for (i = 0; i < WME_NUM_TID; i++)
+ ampdu_rx_stop(&ni->ni_rx_ampdu[i]);
+
+ ni->ni_htcap = 0;
+ ni->ni_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT |
+ IEEE80211_NODE_AMPDU);
+}
+
+static struct ieee80211_channel *
+findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
+{
+ return ieee80211_find_channel(ic, c->ic_freq,
+ (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags);
+}
+
+/*
+ * Adjust a channel to be HT/non-HT according to the vap's configuration.
+ */
+struct ieee80211_channel *
+ieee80211_ht_adjust_channel(struct ieee80211com *ic,
+ struct ieee80211_channel *chan, int flags)
+{
+ struct ieee80211_channel *c;
+
+ if (flags & IEEE80211_FEXT_HT) {
+ /* promote to HT if possible */
+ if (flags & IEEE80211_FEXT_USEHT40) {
+ if (!IEEE80211_IS_CHAN_HT40(chan)) {
+ /* NB: arbitrarily pick ht40+ over ht40- */
+ c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U);
+ if (c == NULL)
+ c = findhtchan(ic, chan,
+ IEEE80211_CHAN_HT40D);
+ if (c == NULL)
+ c = findhtchan(ic, chan,
+ IEEE80211_CHAN_HT20);
+ if (c != NULL)
+ chan = c;
+ }
+ } else if (!IEEE80211_IS_CHAN_HT20(chan)) {
+ c = findhtchan(ic, chan, IEEE80211_CHAN_HT20);
+ if (c != NULL)
+ chan = c;
+ }
+ } else if (IEEE80211_IS_CHAN_HT(chan)) {
+ /* demote to legacy, HT use is disabled */
+ c = ieee80211_find_channel(ic, chan->ic_freq,
+ chan->ic_flags &~ IEEE80211_CHAN_HT);
+ if (c != NULL)
+ chan = c;
+ }
+ return chan;
+}
+
+/*
+ * Setup HT-specific state for a legacy WDS peer.
+ */
+void
+ieee80211_ht_wds_init(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_tx_ampdu *tap;
+ int ac;
+
+ KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
+
+ /* XXX check scan cache in case peer has an ap and we have info */
+ /*
+ * If setup with a legacy channel; locate an HT channel.
+ * Otherwise if the inherited channel (from a companion
+ * AP) is suitable use it so we use the same location
+ * for the extension channel).
+ */
+ ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan,
+ ic->ic_flags_ext);
+
+ ni->ni_htcap = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
+ ni->ni_chw = 40;
+ if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
+ ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
+ else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
+ ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
+ } else {
+ ni->ni_chw = 20;
+ ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
+ }
+ ni->ni_htctlchan = ni->ni_chan->ic_ieee;
+
+ ni->ni_htopmode = 0; /* XXX need protection state */
+ ni->ni_htstbc = 0; /* XXX need info */
+
+ for (ac = 0; ac < WME_NUM_AC; ac++) {
+ tap = &ni->ni_tx_ampdu[ac];
+ tap->txa_ac = ac;
+ }
+ /* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */
+ ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
+}
+
+/*
+ * Notify hostap vaps of a change in the HTINFO ie.
+ */
+static void
+htinfo_notify(struct ieee80211com *ic)
+{
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ return;
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+ ic->ic_bss,
+ "HT bss occupancy change: %d sta, %d ht, "
+ "%d ht40%s, HT protmode now 0x%x"
+ , ic->ic_sta_assoc
+ , ic->ic_ht_sta_assoc
+ , ic->ic_ht40_sta_assoc
+ , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
+ ", non-HT sta present" : ""
+ , ic->ic_curhtprotmode);
+ ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO);
+}
+
+/*
+ * Calculate HT protection mode from current
+ * state and handle updates.
+ */
+static void
+htinfo_update(struct ieee80211com *ic)
+{
+ uint8_t protmode;
+
+ if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
+ protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+ protmode = IEEE80211_HTINFO_OPMODE_MIXED
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
+ ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
+ protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
+ } else {
+ protmode = IEEE80211_HTINFO_OPMODE_PURE;
+ }
+ if (protmode != ic->ic_curhtprotmode) {
+ ic->ic_curhtprotmode = protmode;
+ htinfo_notify(ic);
+ }
+}
+
+/*
+ * Handle an HT station joining a BSS.
+ */
+void
+ieee80211_ht_node_join(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ ic->ic_ht_sta_assoc++;
+ if (ni->ni_chw == 40)
+ ic->ic_ht40_sta_assoc++;
+ }
+ htinfo_update(ic);
+}
+
+/*
+ * Handle an HT station leaving a BSS.
+ */
+void
+ieee80211_ht_node_leave(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ ic->ic_ht_sta_assoc--;
+ if (ni->ni_chw == 40)
+ ic->ic_ht40_sta_assoc--;
+ }
+ htinfo_update(ic);
+}
+
+/*
+ * Public version of htinfo_update; used for processing
+ * beacon frames from overlapping bss in hostap_recv_mgmt.
+ */
+void
+ieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
+{
+ if (protmode != ic->ic_curhtprotmode) {
+ ic->ic_curhtprotmode = protmode;
+ htinfo_notify(ic);
+ }
+}
+
+/*
+ * Time out presence of an overlapping bss with non-HT
+ * stations. When operating in hostap mode we listen for
+ * beacons from other stations and if we identify a non-HT
+ * station is present we update the opmode field of the
+ * HTINFO ie. To identify when all non-HT stations are
+ * gone we time out this condition.
+ */
+void
+ieee80211_ht_timeout(struct ieee80211com *ic)
+{
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) &&
+ time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
+#if 0
+ IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ "%s", "time out non-HT STA present on channel");
+#endif
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
+ htinfo_update(ic);
+ }
+}
+
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+
+/*
+ * Process an 802.11n HT capabilities ie.
+ */
+void
+ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ if (ie[0] == IEEE80211_ELEMID_VENDOR) {
+ /*
+ * Station used Vendor OUI ie to associate;
+ * mark the node so when we respond we'll use
+ * the Vendor OUI's and not the standard ie's.
+ */
+ ni->ni_flags |= IEEE80211_NODE_HTCOMPAT;
+ ie += 4;
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT;
+
+ ni->ni_htcap = LE_READ_2(ie +
+ __offsetof(struct ieee80211_ie_htcap, hc_cap));
+ ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
+ /* XXX needed or will ieee80211_parse_htinfo always be called? */
+ ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
+}
+
+/*
+ * Process an 802.11n HT info ie and update the node state.
+ * Note that we handle use this information to identify the
+ * correct channel (HT20, HT40+, HT40-, legacy). The caller
+ * is responsible for insuring any required channel change is
+ * done (e.g. in sta mode when parsing the contents of a
+ * beacon frame).
+ */
+void
+ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_ie_htinfo *htinfo;
+ struct ieee80211_channel *c;
+ uint16_t w;
+ int htflags, chanflags;
+
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ htinfo = (const struct ieee80211_ie_htinfo *) ie;
+ ni->ni_htctlchan = htinfo->hi_ctrlchannel;
+ ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
+ w = LE_READ_2(&htinfo->hi_byte2);
+ ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
+ w = LE_READ_2(&htinfo->hi_byte45);
+ ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
+ /*
+ * Handle 11n channel switch. Use the received HT ie's to
+ * identify the right channel to use. If we cannot locate it
+ * in the channel table then fallback to legacy operation.
+ */
+ htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
+ IEEE80211_CHAN_HT20 : 0;
+ /* NB: honor operating mode constraint */
+ if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
+ if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
+ htflags = IEEE80211_CHAN_HT40U;
+ else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
+ htflags = IEEE80211_CHAN_HT40D;
+ }
+ chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
+ if (chanflags != ni->ni_chan->ic_flags) {
+ c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
+ if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
+ /*
+ * No HT40 channel entry in our table; fall back
+ * to HT20 operation. This should not happen.
+ */
+ c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
+ IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+ "no HT40 channel (freq %u), falling back to HT20",
+ ni->ni_chan->ic_freq);
+ /* XXX stat */
+ }
+ if (c != NULL && c != ni->ni_chan) {
+ IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+ "switch station to HT%d channel %u/0x%x",
+ IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
+ c->ic_freq, c->ic_flags);
+ ni->ni_chan = c;
+ }
+ /* NB: caller responsible for forcing any channel change */
+ }
+ /* update node's tx channel width */
+ ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20;
+}
+
+/*
+ * Install received HT rate set by parsing the HT cap ie.
+ */
+int
+ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_ie_htcap *htcap;
+ struct ieee80211_htrateset *rs;
+ int i;
+
+ rs = &ni->ni_htrates;
+ memset(rs, 0, sizeof(*rs));
+ if (ie != NULL) {
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ htcap = (const struct ieee80211_ie_htcap *) ie;
+ for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+ if (isclr(htcap->hc_mcsset, i))
+ continue;
+ if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+ "WARNING, HT rate set too large; only "
+ "using %u rates", IEEE80211_HTRATE_MAXSIZE);
+ ic->ic_stats.is_rx_rstoobig++;
+ break;
+ }
+ rs->rs_rates[rs->rs_nrates++] = i;
+ }
+ }
+ return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags);
+}
+
+/*
+ * Mark rates in a node's HT rate set as basic according
+ * to the information in the supplied HT info ie.
+ */
+void
+ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
+{
+ const struct ieee80211_ie_htinfo *htinfo;
+ struct ieee80211_htrateset *rs;
+ int i, j;
+
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ htinfo = (const struct ieee80211_ie_htinfo *) ie;
+ rs = &ni->ni_htrates;
+ if (rs->rs_nrates == 0) {
+ IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+ "%s", "WARNING, empty HT rate set");
+ return;
+ }
+ for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+ if (isclr(htinfo->hi_basicmcsset, i))
+ continue;
+ for (j = 0; j < rs->rs_nrates; j++)
+ if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
+ rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
+ }
+}
+
+static void
+addba_timeout(void *arg)
+{
+ struct ieee80211_tx_ampdu *tap = arg;
+
+ /* XXX ? */
+ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+ tap->txa_attempts++;
+}
+
+static void
+addba_start_timeout(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX use CALLOUT_PENDING instead? */
+ callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT,
+ addba_timeout, tap);
+ tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
+ tap->txa_lastrequest = ticks;
+}
+
+static void
+addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX use CALLOUT_PENDING instead? */
+ if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
+ callout_stop(&tap->txa_timer);
+ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+ }
+}
+
+/*
+ * Default method for requesting A-MPDU tx aggregation.
+ * We setup the specified state block and start a timer
+ * to wait for an ADDBA response frame.
+ */
+static int
+ieee80211_addba_request(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap,
+ int dialogtoken, int baparamset, int batimeout)
+{
+ int bufsiz;
+
+ /* XXX locking */
+ tap->txa_token = dialogtoken;
+ tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
+ tap->txa_start = tap->txa_seqstart = 0;
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ tap->txa_wnd = (bufsiz == 0) ?
+ IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+ addba_start_timeout(tap);
+ return 1;
+}
+
+/*
+ * Default method for processing an A-MPDU tx aggregation
+ * response. We shutdown any pending timer and update the
+ * state block according to the reply.
+ */
+static int
+ieee80211_addba_response(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap,
+ int status, int baparamset, int batimeout)
+{
+ int bufsiz;
+
+ /* XXX locking */
+ addba_stop_timeout(tap);
+ if (status == IEEE80211_STATUS_SUCCESS) {
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ /* XXX override our request? */
+ tap->txa_wnd = (bufsiz == 0) ?
+ IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+ tap->txa_flags |= IEEE80211_AGGR_RUNNING;
+ } else {
+ /* mark tid so we don't try again */
+ tap->txa_flags |= IEEE80211_AGGR_NAK;
+ }
+ return 1;
+}
+
+/*
+ * Default method for stopping A-MPDU tx aggregation.
+ * Any timer is cleared and we drain any pending frames.
+ */
+static void
+ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX locking */
+ addba_stop_timeout(tap);
+ if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
+ /* clear aggregation queue */
+ ieee80211_drain_ifq(&tap->txa_q);
+ tap->txa_flags &= ~IEEE80211_AGGR_RUNNING;
+ }
+ tap->txa_attempts = 0;
+}
+
+/*
+ * Process a received action frame using the default aggregation
+ * policy. We intercept ADDBA-related frames and use them to
+ * update our aggregation state. All other frames are passed up
+ * for processing by ieee80211_recv_action.
+ */
+static void
+ieee80211_aggr_recv_action(struct ieee80211_node *ni,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_action *ia;
+ struct ieee80211_rx_ampdu *rap;
+ struct ieee80211_tx_ampdu *tap;
+ uint8_t dialogtoken;
+ uint16_t baparamset, batimeout, baseqctl, code;
+ uint16_t args[4];
+ int tid, ac, bufsiz;
+
+ ia = (const struct ieee80211_action *) frm;
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ dialogtoken = frm[2];
+ baparamset = LE_READ_2(frm+3);
+ batimeout = LE_READ_2(frm+5);
+ baseqctl = LE_READ_2(frm+7);
+
+ tid = MS(baparamset, IEEE80211_BAPS_TID);
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "recv ADDBA request: dialogtoken %u "
+ "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
+ "baseqctl %d:%d",
+ dialogtoken, baparamset, tid, bufsiz, batimeout,
+ MS(baseqctl, IEEE80211_BASEQ_START),
+ MS(baseqctl, IEEE80211_BASEQ_FRAG));
+
+ rap = &ni->ni_rx_ampdu[tid];
+
+ /* Send ADDBA response */
+ args[0] = dialogtoken;
+ /*
+ * NB: We ack only if the sta associated with HT and
+ * the ap is configured to do AMPDU rx (the latter
+ * violates the 11n spec and is mostly for testing).
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
+ ampdu_rx_start(rap, bufsiz,
+ MS(baseqctl, IEEE80211_BASEQ_START));
+
+ args[1] = IEEE80211_STATUS_SUCCESS;
+ } else {
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ ni, "reject ADDBA request: %s",
+ ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
+ "administratively disabled" :
+ "not negotiated for station");
+ ic->ic_stats.is_addba_reject++;
+ args[1] = IEEE80211_STATUS_UNSPECIFIED;
+ }
+ /* XXX honor rap flags? */
+ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
+ | SM(tid, IEEE80211_BAPS_TID)
+ | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
+ ;
+ args[3] = 0;
+ ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
+ IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
+ return;
+
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ dialogtoken = frm[2];
+ code = LE_READ_2(frm+3);
+ baparamset = LE_READ_2(frm+5);
+ tid = MS(baparamset, IEEE80211_BAPS_TID);
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ batimeout = LE_READ_2(frm+7);
+
+ ac = TID_TO_WME_AC(tid);
+ tap = &ni->ni_tx_ampdu[ac];
+ if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+ IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ ni->ni_macaddr, "ADDBA response",
+ "no pending ADDBA, tid %d dialogtoken %u "
+ "code %d", tid, dialogtoken, code);
+ ic->ic_stats.is_addba_norequest++;
+ return;
+ }
+ if (dialogtoken != tap->txa_token) {
+ IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ ni->ni_macaddr, "ADDBA response",
+ "dialogtoken mismatch: waiting for %d, "
+ "received %d, tid %d code %d",
+ tap->txa_token, dialogtoken, tid, code);
+ ic->ic_stats.is_addba_badtoken++;
+ return;
+ }
+
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "recv ADDBA response: dialogtoken %u code %d "
+ "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
+ dialogtoken, code, baparamset, tid, bufsiz,
+ batimeout);
+ ic->ic_addba_response(ni, tap,
+ code, baparamset, batimeout);
+ return;
+
+ case IEEE80211_ACTION_BA_DELBA:
+ baparamset = LE_READ_2(frm+2);
+ code = LE_READ_2(frm+4);
+
+ tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
+ "code %d", baparamset, tid,
+ MS(baparamset, IEEE80211_DELBAPS_INIT), code);
+
+ if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+ ac = TID_TO_WME_AC(tid);
+ tap = &ni->ni_tx_ampdu[ac];
+ ic->ic_addba_stop(ni, tap);
+ } else {
+ rap = &ni->ni_rx_ampdu[tid];
+ ampdu_rx_stop(rap);
+ }
+ return;
+ }
+ break;
+ }
+ ieee80211_recv_action(ni, frm, efrm);
+}
+
+/*
+ * Process a received 802.11n action frame.
+ * Aggregation-related frames are assumed to be handled
+ * already; we handle any other frames we can, otherwise
+ * complain about being unsupported (with debugging).
+ */
+void
+ieee80211_recv_action(struct ieee80211_node *ni,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_action *ia;
+ int chw;
+
+ ia = (const struct ieee80211_action *) frm;
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "%s: BA action %d not implemented", __func__,
+ ia->ia_action);
+ ic->ic_stats.is_rx_mgtdiscard++;
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
+ if (chw != ni->ni_chw) {
+ ni->ni_chw = chw;
+ ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
+ }
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "%s: HT txchwidth, width %d (%s)",
+ __func__, chw,
+ ni->ni_flags & IEEE80211_NODE_CHWUPDATE ?
+ "new" : "no change");
+ break;
+ case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "%s: HT MIMO PS", __func__);
+ break;
+ default:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "%s: HT action %d not implemented", __func__,
+ ia->ia_action);
+ ic->ic_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ break;
+ default:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "%s: category %d not implemented", __func__,
+ ia->ia_category);
+ ic->ic_stats.is_rx_mgtdiscard++;
+ break;
+ }
+}
+
+/*
+ * Transmit processing.
+ */
+
+/*
+ * Request A-MPDU tx aggregation. Setup local state and
+ * issue an ADDBA request. BA use will only happen after
+ * the other end replies with ADDBA response.
+ */
+int
+ieee80211_ampdu_request(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t args[4];
+ int tid, dialogtoken;
+ static int tokens = 0; /* XXX */
+
+ /* XXX locking */
+ if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
+ /* do deferred setup of state */
+ IEEE80211_TAPQ_INIT(tap);
+ callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
+ tap->txa_flags |= IEEE80211_AGGR_SETUP;
+ }
+ if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
+ (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
+ /*
+ * Don't retry too often; IEEE80211_AGGR_MINRETRY
+ * defines the minimum interval we'll retry after
+ * IEEE80211_AGGR_MAXTRIES failed attempts to
+ * negotiate use.
+ */
+ return 0;
+ }
+ /* XXX hack for not doing proper locking */
+ tap->txa_flags &= ~IEEE80211_AGGR_NAK;
+
+ dialogtoken = (tokens+1) % 63; /* XXX */
+
+ tid = WME_AC_TO_TID(tap->txa_ac);
+ args[0] = dialogtoken;
+ args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
+ | SM(tid, IEEE80211_BAPS_TID)
+ | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ)
+ ;
+ args[2] = 0; /* batimeout */
+ args[3] = SM(0, IEEE80211_BASEQ_START)
+ | SM(0, IEEE80211_BASEQ_FRAG)
+ ;
+ /* NB: do first so there's no race against reply */
+ if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
+ /* unable to setup state, don't make request */
+ IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N,
+ ni, "%s: could not setup BA stream for AC %d",
+ __func__, tap->txa_ac);
+ /* defer next try so we don't slam the driver with requests */
+ tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
+ tap->txa_lastrequest = ticks;
+ return 0;
+ }
+ tokens = dialogtoken; /* allocate token */
+ return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
+ IEEE80211_ACTION_BA_ADDBA_REQUEST, args);
+}
+
+/*
+ * Terminate an AMPDU tx stream. State is reclaimed
+ * and the peer notified with a DelBA Action frame.
+ */
+void
+ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t args[4];
+
+ /* XXX locking */
+ if (IEEE80211_AMPDU_RUNNING(tap)) {
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac);
+ ic->ic_stats.is_ampdu_stop++;
+
+ ic->ic_addba_stop(ni, tap);
+ args[0] = WME_AC_TO_TID(tap->txa_ac);
+ args[1] = IEEE80211_DELBAPS_INIT;
+ args[2] = 1; /* XXX reason code */
+ ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
+ IEEE80211_ACTION_BA_DELBA, args);
+ } else {
+ IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ ni, "%s: BA stream for AC %d not running",
+ __func__, tap->txa_ac);
+ ic->ic_stats.is_ampdu_stop_failed++;
+ }
+}
+
+/*
+ * Transmit a BAR frame to the specified node. The
+ * BAR contents are drawn from the supplied aggregation
+ * state associated with the node.
+ */
+int
+ieee80211_send_bar(struct ieee80211_node *ni,
+ const struct ieee80211_tx_ampdu *tap)
+{
+#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_frame_min *wh;
+ struct mbuf *m;
+ uint8_t *frm;
+ uint16_t barctl, barseqctl;
+ int tid, ret;
+
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame_min),
+ sizeof(struct ieee80211_ba_request)
+ );
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nobuf);
+
+ wh = mtod(m, struct ieee80211_frame_min *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
+ wh->i_fc[1] = 0;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+
+ tid = WME_AC_TO_TID(tap->txa_ac);
+ barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
+ IEEE80211_BAPS_POLICY_IMMEDIATE :
+ IEEE80211_BAPS_POLICY_DELAYED)
+ | SM(tid, IEEE80211_BAPS_TID)
+ | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ)
+ ;
+ barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START)
+ | SM(0, IEEE80211_BASEQ_FRAG)
+ ;
+ ADDSHORT(frm, barctl);
+ ADDSHORT(frm, barseqctl);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+ IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */
+
+ IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ ni, "send bar frame (tid %u start %u) on channel %u",
+ tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+ m->m_pkthdr.rcvif = (void *)ni;
+ IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
+ if_start(ifp);
+
+ return 0;
+bad:
+ ieee80211_free_node(ni);
+ return ret;
+#undef ADDSHORT
+#undef senderr
+}
+
+/*
+ * Send an action management frame. The arguments are stuff
+ * into a frame without inspection; the caller is assumed to
+ * prepare them carefully (e.g. based on the aggregation state).
+ */
+int
+ieee80211_send_action(struct ieee80211_node *ni,
+ int category, int action, uint16_t args[4])
+{
+#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m;
+ uint8_t *frm;
+ uint16_t baparamset;
+ int ret;
+
+ KASSERT(ni != NULL, ("null node"));
+
+ /*
+ * Hold a reference on the node so it doesn't go away until after
+ * the xmit is complete all the way in the driver. On error we
+ * will remove our reference.
+ */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+ __func__, __LINE__,
+ ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ sizeof(uint16_t) /* action+category */
+ /* XXX may action payload */
+ + sizeof(struct ieee80211_action_ba_addbaresponse)
+ );
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nobuf);
+
+ *frm++ = category;
+ *frm++ = action;
+ switch (category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "send ADDBA request: dialogtoken %d "
+ "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
+ args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
+ args[2], args[3]);
+
+ *frm++ = args[0]; /* dialog token */
+ ADDSHORT(frm, args[1]); /* baparamset */
+ ADDSHORT(frm, args[2]); /* batimeout */
+ ADDSHORT(frm, args[3]); /* baseqctl */
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "send ADDBA response: dialogtoken %d status %d "
+ "baparamset 0x%x (tid %d) batimeout %d",
+ args[0], args[1], args[2],
+ MS(args[2], IEEE80211_BAPS_TID), args[3]);
+
+ *frm++ = args[0]; /* dialog token */
+ ADDSHORT(frm, args[1]); /* statuscode */
+ ADDSHORT(frm, args[2]); /* baparamset */
+ ADDSHORT(frm, args[3]); /* batimeout */
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ /* XXX */
+ baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
+ | SM(args[1], IEEE80211_DELBAPS_INIT)
+ ;
+ ADDSHORT(frm, baparamset);
+ ADDSHORT(frm, args[2]); /* reason code */
+
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "send DELBA action: tid %d, initiator %d reason %d",
+ args[0], args[1], args[2]);
+ break;
+ default:
+ goto badaction;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ ni, "send HT txchwidth: width %d",
+ IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20
+ );
+ *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ?
+ IEEE80211_A_HT_TXCHWIDTH_2040 :
+ IEEE80211_A_HT_TXCHWIDTH_20;
+ break;
+ default:
+ goto badaction;
+ }
+ break;
+ default:
+ badaction:
+ IEEE80211_NOTE(ic,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+ "%s: unsupported category %d action %d", __func__,
+ category, action);
+ senderr(EINVAL, is_tx_unknownmgt);
+ /* NOTREACHED */
+ }
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+ ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
+ if (ret != 0)
+ goto bad;
+ return 0;
+bad:
+ ieee80211_free_node(ni);
+ return ret;
+#undef ADDSHORT
+#undef senderr
+}
+
+/*
+ * Construct the MCS bit mask for inclusion
+ * in an HT information element.
+ */
+static void
+ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+{
+ int i;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */
+ /* NB: this assumes a particular implementation */
+ setbit(frm, r);
+ }
+ }
+}
+
+/*
+ * Add body of an HTCAP information element.
+ */
+static uint8_t *
+ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
+{
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t caps;
+ int rxmax, density;
+
+ /* HT capabilities */
+ caps = ic->ic_htcaps & 0xffff;
+ /*
+ * Note channel width depends on whether we are operating as
+ * a sta or not. When operating as a sta we are generating
+ * a request based on our desired configuration. Otherwise
+ * we are operational and the channel attributes identify
+ * how we've been setup (which might be different if a fixed
+ * channel is specified).
+ */
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ /* override 20/40 use based on config */
+ if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ caps |= IEEE80211_HTCAP_CHWIDTH40;
+ else
+ caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+ /* use advertised setting (XXX locally constraint) */
+ rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
+ density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+ } else {
+ /* override 20/40 use based on current channel */
+ if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ caps |= IEEE80211_HTCAP_CHWIDTH40;
+ else
+ caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+ rxmax = ic->ic_ampdu_rxmax;
+ density = ic->ic_ampdu_density;
+ }
+ /* adjust short GI based on channel and config */
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+ caps &= ~IEEE80211_HTCAP_SHORTGI20;
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
+ (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+ caps &= ~IEEE80211_HTCAP_SHORTGI40;
+ ADDSHORT(frm, caps);
+
+ /* HT parameters */
+ *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
+ | SM(density, IEEE80211_HTCAP_MPDUDENSITY)
+ ;
+ frm++;
+
+ /* pre-zero remainder of ie */
+ memset(frm, 0, sizeof(struct ieee80211_ie_htcap) -
+ __offsetof(struct ieee80211_ie_htcap, hc_mcsset));
+
+ /* supported MCS set */
+ /*
+ * XXX it would better to get the rate set from ni_htrates
+ * so we can restrict it but for sta mode ni_htrates isn't
+ * setup when we're called to form an AssocReq frame so for
+ * now we're restricted to the default HT rate set.
+ */
+ ieee80211_set_htrates(frm, &ieee80211_rateset_11n);
+
+ frm += sizeof(struct ieee80211_ie_htcap) -
+ __offsetof(struct ieee80211_ie_htcap, hc_mcsset);
+ return frm;
+#undef ADDSHORT
+}
+
+/*
+ * Add 802.11n HT capabilities information element
+ */
+uint8_t *
+ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni)
+{
+ frm[0] = IEEE80211_ELEMID_HTCAP;
+ frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
+ return ieee80211_add_htcap_body(frm + 2, ni);
+}
+
+/*
+ * Add Broadcom OUI wrapped standard HTCAP ie; this is
+ * used for compatibility w/ pre-draft implementations.
+ */
+uint8_t *
+ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni)
+{
+ frm[0] = IEEE80211_ELEMID_VENDOR;
+ frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2;
+ frm[2] = (BCM_OUI >> 0) & 0xff;
+ frm[3] = (BCM_OUI >> 8) & 0xff;
+ frm[4] = (BCM_OUI >> 16) & 0xff;
+ frm[5] = BCM_OUI_HTCAP;
+ return ieee80211_add_htcap_body(frm + 6, ni);
+}
+
+/*
+ * Construct the MCS bit mask of basic rates
+ * for inclusion in an HT information element.
+ */
+static void
+ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+{
+ int i;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
+ r < IEEE80211_HTRATE_MAXSIZE) {
+ /* NB: this assumes a particular implementation */
+ setbit(frm, r);
+ }
+ }
+}
+
+/*
+ * Update the HTINFO ie for a beacon frame.
+ */
+void
+ieee80211_ht_update_beacon(struct ieee80211com *ic,
+ struct ieee80211_beacon_offsets *bo)
+{
+#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
+ struct ieee80211_ie_htinfo *ht =
+ (struct ieee80211_ie_htinfo *) bo->bo_htinfo;
+
+ /* XXX only update on channel change */
+ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
+ if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+ else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+ else
+ ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
+ if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+ /* protection mode */
+ ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
+
+ /* XXX propagate to vendor ie's */
+#undef PROTMODE
+}
+
+/*
+ * Add body of an HTINFO information element.
+ *
+ * NB: We don't use struct ieee80211_ie_htinfo because we can
+ * be called to fillin both a standard ie and a compat ie that
+ * has a vendor OUI at the front.
+ */
+static uint8_t *
+ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ /* pre-zero remainder of ie */
+ memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
+
+ /* primary/control channel center */
+ *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+
+ frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
+ if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+ else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+ else
+ frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
+ if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+ frm[1] = ic->ic_curhtprotmode;
+
+ frm += 5;
+
+ /* basic MCS set */
+ ieee80211_set_basic_htrates(frm, &ni->ni_htrates);
+ frm += sizeof(struct ieee80211_ie_htinfo) -
+ __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
+ return frm;
+}
+
+/*
+ * Add 802.11n HT information information element.
+ */
+uint8_t *
+ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni)
+{
+ frm[0] = IEEE80211_ELEMID_HTINFO;
+ frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2;
+ return ieee80211_add_htinfo_body(frm + 2, ni);
+}
+
+/*
+ * Add Broadcom OUI wrapped standard HTINFO ie; this is
+ * used for compatibility w/ pre-draft implementations.
+ */
+uint8_t *
+ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni)
+{
+ frm[0] = IEEE80211_ELEMID_VENDOR;
+ frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2;
+ frm[2] = (BCM_OUI >> 0) & 0xff;
+ frm[3] = (BCM_OUI >> 8) & 0xff;
+ frm[4] = (BCM_OUI >> 16) & 0xff;
+ frm[5] = BCM_OUI_HTINFO;
+ return ieee80211_add_htinfo_body(frm + 6, ni);
+}
Index: ieee80211_proto.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_proto.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_proto.c -L sys/net80211/ieee80211_proto.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211_proto.c
+++ sys/net80211/ieee80211_proto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.17.2.9 2006/03/13 03:10:31 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.42.2.3 2007/11/28 06:11:18 sam Exp $");
/*
* IEEE 802.11 protocol support.
@@ -41,8 +35,8 @@
#include <sys/param.h>
#include <sys/kernel.h>
-#include <sys/systm.h>
-
+#include <sys/systm.h>
+
#include <sys/socket.h>
#include <net/if.h>
@@ -61,7 +55,7 @@
"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"
+ "deauth", "action", "reserved#14", "reserved#15"
};
const char *ieee80211_ctl_subtype_name[] = {
"reserved#0", "reserved#1", "reserved#2", "reserved#3",
@@ -69,12 +63,25 @@
"reserved#8", "reserved#9", "ps_poll", "rts",
"cts", "ack", "cf_end", "cf_end_ack"
};
+const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
+ "IBSS", /* IEEE80211_M_IBSS */
+ "STA", /* IEEE80211_M_STA */
+ "#2",
+ "AHDEMO", /* IEEE80211_M_AHDEMO */
+ "#4", "#5",
+ "HOSTAP", /* IEEE80211_M_HOSTAP */
+ "#7",
+ "MONITOR" /* IEEE80211_M_MONITOR */
+};
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 */
+ "CAC", /* IEEE80211_S_CAC */
+ "RUN", /* IEEE80211_S_RUN */
+ "CSA", /* IEEE80211_S_CSA */
+ "SLEEP", /* IEEE80211_S_SLEEP */
};
const char *ieee80211_wme_acnames[] = {
"WME_AC_BE",
@@ -86,6 +93,11 @@
static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void
+null_update_beacon(struct ieee80211com *ic, int item)
+{
+}
+
void
ieee80211_proto_attach(struct ieee80211com *ic)
{
@@ -99,6 +111,7 @@
ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
ic->ic_bmiss_max = IEEE80211_BMISS_MAX;
callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE);
+ callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE);
ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
ic->ic_roaming = IEEE80211_ROAMING_AUTO;
@@ -110,10 +123,12 @@
/* protocol state change handler */
ic->ic_newstate = ieee80211_newstate;
+ ic->ic_update_beacon = null_update_beacon;
/* initialize management frame handlers */
ic->ic_recv_mgmt = ieee80211_recv_mgmt;
ic->ic_send_mgmt = ieee80211_send_mgmt;
+ ic->ic_raw_xmit = ieee80211_raw_xmit;
}
void
@@ -128,7 +143,7 @@
if (ic->ic_auth->ia_detach)
ic->ic_auth->ia_detach(ic);
- IF_DRAIN(&ic->ic_mgtq);
+ ieee80211_drain_ifq(&ic->ic_mgtq);
mtx_destroy(&ic->ic_mgtq.ifq_mtx);
/*
@@ -232,9 +247,9 @@
}
void
-ieee80211_print_essid(const u_int8_t *essid, int len)
+ieee80211_print_essid(const uint8_t *essid, int len)
{
- const u_int8_t *p;
+ const uint8_t *p;
int i;
if (len > IEEE80211_NWID_LEN)
@@ -257,7 +272,8 @@
}
void
-ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi)
+ieee80211_dump_pkt(struct ieee80211com *ic,
+ const uint8_t *buf, int len, int rate, int rssi)
{
const struct ieee80211_frame *wh;
int i;
@@ -280,7 +296,7 @@
printf("(%s)", ether_sprintf(wh->i_addr2));
break;
case IEEE80211_FC1_DIR_DSTODS:
- printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1]));
+ printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1]));
printf("->%s", ether_sprintf(wh->i_addr3));
printf("(%s", ether_sprintf(wh->i_addr2));
printf("->%s)", ether_sprintf(wh->i_addr1));
@@ -299,12 +315,22 @@
printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
break;
}
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ const struct ieee80211_qosframe *qwh =
+ (const struct ieee80211_qosframe *)buf;
+ printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID,
+ qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : "");
+ }
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);
+ int off;
+
+ off = ieee80211_anyhdrspace(ic, wh);
+ printf(" WEP [IV %.02x %.02x %.02x",
+ buf[off+0], buf[off+1], buf[off+2]);
+ if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)
+ printf(" %.02x %.02x %.02x",
+ buf[off+4], buf[off+5], buf[off+6]);
+ printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6);
}
if (rate >= 0)
printf(" %dM", rate / 2);
@@ -321,29 +347,33 @@
}
}
+static __inline int
+findrix(const struct ieee80211_rateset *rs, int r)
+{
+ int i;
+
+ for (i = 0; i < rs->rs_nrates; i++)
+ if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r)
+ return i;
+ return -1;
+}
+
int
-ieee80211_fix_rate(struct ieee80211_node *ni, int flags)
+ieee80211_fix_rate(struct ieee80211_node *ni,
+ struct ieee80211_rateset *nrs, int flags)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
struct ieee80211com *ic = ni->ni_ic;
- int i, j, ignore, error;
+ int i, j, rix, error;
int okrate, badrate, fixedrate;
- struct ieee80211_rateset *srs, *nrs;
- u_int8_t r;
+ const struct ieee80211_rateset *srs;
+ uint8_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 == IEEE80211_FIXED_RATE_NONE)
- flags &= ~IEEE80211_F_DOFRATE;
error = 0;
- okrate = badrate = fixedrate = 0;
- srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
- nrs = &ni->ni_rates;
+ okrate = badrate = 0;
+ fixedrate = IEEE80211_FIXED_RATE_NONE;
+ srs = ieee80211_get_suprates(ic, ni->ni_chan);
for (i = 0; i < nrs->rs_nrates; ) {
- ignore = 0;
if (flags & IEEE80211_F_DOSORT) {
/*
* Sort rates.
@@ -358,63 +388,50 @@
}
r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
badrate = r;
- if (flags & IEEE80211_F_DOFRATE) {
- /*
- * Check any fixed rate is included.
- */
- if (r == RV(srs->rs_rates[ic->ic_fixed_rate]))
- fixedrate = r;
- }
+ /*
+ * Check for fixed rate.
+ */
+ if (r == ic->ic_fixed_rate)
+ fixedrate = r;
+ /*
+ * Check against supported rates.
+ */
+ rix = findrix(srs, r);
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) {
+ if (rix < 0) {
/*
* 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.
+ * are operating as a STA 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 &&
+ if ((flags & IEEE80211_F_JOIN) &&
(nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
error++;
- ignore++;
+ } else if ((flags & IEEE80211_F_JOIN) == 0) {
+ /*
+ * Overwrite with the supported rate
+ * value so any basic rate bit is set.
+ */
+ nrs->rs_rates[i] = srs->rs_rates[rix];
}
}
- if (flags & IEEE80211_F_DODEL) {
+ if ((flags & IEEE80211_F_DODEL) && rix < 0) {
/*
* 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;
- }
+ 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)
+ if (rix >= 0)
okrate = nrs->rs_rates[i];
i++;
}
if (okrate == 0 || error != 0 ||
- ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
+ ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate))
return badrate | IEEE80211_RATE_BASIC;
else
return RV(okrate);
@@ -436,14 +453,15 @@
* the driver is capable of doing it.
*/
ieee80211_set_shortslottime(ic,
- ic->ic_curmode == IEEE80211_MODE_11A ||
- (ic->ic_curmode == IEEE80211_MODE_11G &&
+ IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ IEEE80211_IS_CHAN_HT(ic->ic_curchan) ||
+ (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
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 ||
+ if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
@@ -507,14 +525,17 @@
void
ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
{
- static const struct ieee80211_rateset basic[] = {
- { 0 }, /* IEEE80211_MODE_AUTO */
+ static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
+ { .rs_nrates = 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 */
+ { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */
/* IEEE80211_MODE_PUREG (not yet) */
{ 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */
+ /* IEEE80211_MODE_11NG (mixed b/g) */
+ { 7, { 2, 4, 11, 22, 12, 24, 48 } },
};
int i, j;
@@ -532,76 +553,97 @@
* 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;
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txopLimit;
+ uint8_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 */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11B */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_FH */
+ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */
};
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 */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */
+ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */
};
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 */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */
+ { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */
+ { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */
};
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 */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */
+ { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */
+ { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
};
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 */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */
+ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */
};
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 */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */
+ { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */
+ { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */
+ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */
};
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 */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */
+ { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */
+ { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
};
void
@@ -610,29 +652,40 @@
struct ieee80211_wme_state *wme = &ic->ic_wme;
const paramType *pPhyParam, *pBssPhyParam;
struct wmeParams *wmep;
+ enum ieee80211_phymode mode;
int i;
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
return;
+ /*
+ * Select mode; we can be called early in which case we
+ * always use auto mode. We know we'll be called when
+ * entering the RUN state with bsschan setup properly
+ * so state will eventually get set correctly
+ */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
+ mode = ieee80211_chan2mode(ic->ic_bsschan);
+ else
+ mode = IEEE80211_MODE_AUTO;
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];
+ pPhyParam = &phyParamForAC_BK[mode];
+ pBssPhyParam = &phyParamForAC_BK[mode];
break;
case WME_AC_VI:
- pPhyParam = &phyParamForAC_VI[ic->ic_curmode];
- pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode];
+ pPhyParam = &phyParamForAC_VI[mode];
+ pBssPhyParam = &bssPhyParamForAC_VI[mode];
break;
case WME_AC_VO:
- pPhyParam = &phyParamForAC_VO[ic->ic_curmode];
- pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode];
+ pPhyParam = &phyParamForAC_VO[mode];
+ pBssPhyParam = &bssPhyParamForAC_VO[mode];
break;
case WME_AC_BE:
default:
- pPhyParam = &phyParamForAC_BE[ic->ic_curmode];
- pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode];
+ pPhyParam = &phyParamForAC_BE[mode];
+ pBssPhyParam = &bssPhyParamForAC_BE[mode];
break;
}
@@ -700,17 +753,21 @@
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 */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */
+ { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */
+ { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/
};
struct ieee80211_wme_state *wme = &ic->ic_wme;
const struct wmeParams *wmep;
struct wmeParams *chanp, *bssp;
+ enum ieee80211_phymode mode;
int i;
/* set up the channel access parameters for the physical device */
@@ -731,6 +788,17 @@
}
/*
+ * Select mode; we can be called early in which case we
+ * always use auto mode. We know we'll be called when
+ * entering the RUN state with bsschan setup properly
+ * so state will eventually get set correctly
+ */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
+ mode = ieee80211_chan2mode(ic->ic_bsschan);
+ else
+ mode = IEEE80211_MODE_AUTO;
+
+ /*
* 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
@@ -746,15 +814,14 @@
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_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn;
chanp->wmep_logcwmin = bssp->wmep_logcwmin =
- phyParam[ic->ic_curmode].logcwmin;
+ phyParam[mode].logcwmin;
chanp->wmep_logcwmax = bssp->wmep_logcwmax =
- phyParam[ic->ic_curmode].logcwmax;
+ phyParam[mode].logcwmax;
chanp->wmep_txopLimit = bssp->wmep_txopLimit =
(ic->ic_flags & IEEE80211_F_BURST) ?
- phyParam[ic->ic_curmode].txopLimit : 0;
+ phyParam[mode].txopLimit : 0;
IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
"%s: %s [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
@@ -769,7 +836,7 @@
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] = {
+ static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
3, /* IEEE80211_MODE_AUTO */
3, /* IEEE80211_MODE_11A */
4, /* IEEE80211_MODE_11B */
@@ -777,12 +844,14 @@
4, /* IEEE80211_MODE_FH */
3, /* IEEE80211_MODE_TURBO_A */
3, /* IEEE80211_MODE_TURBO_G */
+ 3, /* IEEE80211_MODE_STURBO_A */
+ 3, /* IEEE80211_MODE_11NA */
+ 3, /* IEEE80211_MODE_11NG */
};
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];
+ chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
"%s: %s log2(cwmin) %u\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
@@ -796,7 +865,7 @@
*/
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
- ic->ic_flags |= IEEE80211_F_WMEUPDATE;
+ ieee80211_beacon_notify(ic, IEEE80211_BEACON_WME);
}
wme->wme_update(ic);
@@ -819,6 +888,75 @@
}
}
+/*
+ * Start a device. If this is the first vap running on the
+ * underlying device then we first bring it up.
+ */
+int
+ieee80211_init(struct ieee80211com *ic, int forcescan)
+{
+
+ IEEE80211_DPRINTF(ic,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", "start running");
+
+ /*
+ * Kick the 802.11 state machine as appropriate.
+ */
+ if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ } else {
+ /*
+ * For monitor+wds modes there's nothing to do but
+ * start running. Otherwise, if this is the first
+ * vap to be brought up, start a scan which may be
+ * preempted if the station is locked to a particular
+ * channel.
+ */
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ ic->ic_opmode == IEEE80211_M_WDS) {
+ ic->ic_state = IEEE80211_S_INIT; /* XXX*/
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ } else
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Switch between turbo and non-turbo operating modes.
+ * Use the specified channel flags to locate the new
+ * channel, update 802.11 state, and then call back into
+ * the driver to effect the change.
+ */
+void
+ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
+{
+ struct ieee80211_channel *chan;
+
+ chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
+ if (chan == NULL) { /* XXX should not happen */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "%s: no channel with freq %u flags 0x%x\n",
+ __func__, ic->ic_bsschan->ic_freq, newflags);
+ return;
+ }
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ "%s: %s -> %s (freq %u flags 0x%x)\n", __func__,
+ ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)],
+ ieee80211_phymode_name[ieee80211_chan2mode(chan)],
+ chan->ic_freq, chan->ic_flags);
+
+ ic->ic_bsschan = chan;
+ ic->ic_prevchan = ic->ic_curchan;
+ ic->ic_curchan = chan;
+ ic->ic_set_channel(ic);
+ /* NB: do not need to reset ERP state 'cuz we're in sta mode */
+}
+
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
@@ -827,8 +965,7 @@
/* XXX check ic_curchan != ic_bsschan? */
return;
}
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"%s\n", "beacon miss");
/*
@@ -853,7 +990,28 @@
return;
}
ic->ic_bmiss_count = 0;
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+ /*
+ * If we receive a beacon miss interrupt when using
+ * dynamic turbo, attempt to switch modes before
+ * reassociating.
+ */
+ if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP))
+ ieee80211_dturbo_switch(ic,
+ ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
+ /*
+ * Try to reassociate before scanning for a new ap.
+ */
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+ } else {
+ /*
+ * Somebody else is controlling state changes (e.g.
+ * a user-mode app) don't do anything that would
+ * confuse them; just drop into scan mode so they'll
+ * notified of the state change and given control.
+ */
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ }
}
/*
@@ -897,6 +1055,35 @@
IEEE80211_REASON_ASSOC_LEAVE);
}
+/*
+ * Handle deauth with reason. We retry only for
+ * the cases where we might succeed. Otherwise
+ * we downgrade the ap and scan.
+ */
+static void
+sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason)
+{
+ switch (reason) {
+ case IEEE80211_STATUS_SUCCESS:
+ case IEEE80211_STATUS_TIMEOUT:
+ case IEEE80211_REASON_ASSOC_EXPIRE:
+ case IEEE80211_REASON_NOT_AUTHED:
+ case IEEE80211_REASON_NOT_ASSOCED:
+ case IEEE80211_REASON_ASSOC_LEAVE:
+ case IEEE80211_REASON_ASSOC_NOT_AUTHED:
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ default:
+ ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason);
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan(ic,
+ IEEE80211_SCAN_ACTIVE,
+ IEEE80211_SCAN_FOREVER,
+ ic->ic_des_nssid, ic->ic_des_ssid);
+ break;
+ }
+}
+
static int
ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
@@ -908,6 +1095,9 @@
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 */
+ callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(ic); /* background scan */
ni = ic->ic_bss; /* NB: no reference held */
if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)
callout_stop(&ic->ic_swbmiss);
@@ -931,7 +1121,7 @@
default:
break;
}
- goto reset;
+ break;
case IEEE80211_S_ASSOC:
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
@@ -946,67 +1136,94 @@
default:
break;
}
- goto reset;
+ break;
case IEEE80211_S_SCAN:
ieee80211_cancel_scan(ic);
- goto reset;
+ break;
case IEEE80211_S_AUTH:
- reset:
- ic->ic_mgt_timer = 0;
- IF_DRAIN(&ic->ic_mgtq);
- ieee80211_reset_bss(ic);
+ break;
+ default:
break;
}
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_drain_ifq(&ic->ic_mgtq);
+ ieee80211_reset_bss(ic);
+ ieee80211_scan_flush(ic);
+ }
if (ic->ic_auth->ia_detach != NULL)
ic->ic_auth->ia_detach(ic);
break;
case IEEE80211_S_SCAN:
switch (ostate) {
case IEEE80211_S_INIT:
+ createibss:
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;
- * bypass the scan and startup immediately.
+ * Already have a channel; bypass the
+ * scan and startup immediately. Because
+ * of this explicitly sync the scanner state.
*/
+ ieee80211_scan_update(ic);
ieee80211_create_ibss(ic, ic->ic_des_chan);
} else {
- ieee80211_begin_scan(ic, arg);
+ ieee80211_check_scan(ic,
+ IEEE80211_SCAN_ACTIVE |
+ IEEE80211_SCAN_FLUSH,
+ IEEE80211_SCAN_FOREVER,
+ ic->ic_des_nssid, ic->ic_des_ssid);
}
break;
case IEEE80211_S_SCAN:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
/*
- * Scan next. If doing an active scan probe
- * for the requested ap (if any).
+ * These can happen either because of a timeout
+ * on an assoc/auth response or because of a
+ * change in state that requires a reset. For
+ * the former we're called with a non-zero arg
+ * that is the cause for the failure; pass this
+ * to the scan code so it can update state.
+ * Otherwise trigger a new scan unless we're in
+ * manual roaming mode in which case an application
+ * must issue an explicit scan request.
*/
- if (ic->ic_flags & IEEE80211_F_ASCAN)
- ieee80211_probe_curchan(ic, 0);
+ if (arg != 0)
+ ieee80211_scan_assoc_fail(ic,
+ ic->ic_bss->ni_macaddr, arg);
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan(ic,
+ IEEE80211_SCAN_ACTIVE,
+ IEEE80211_SCAN_FOREVER,
+ ic->ic_des_nssid, ic->ic_des_ssid);
break;
- case IEEE80211_S_RUN:
- /* beacon miss */
- 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_scan,
- ic->ic_bss->ni_macaddr);
- if (ni != NULL) {
- ni->ni_fails++;
- ieee80211_unref_node(&ni);
+ case IEEE80211_S_RUN: /* beacon miss */
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ieee80211_sta_leave(ic, ni);
+ ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan(ic,
+ IEEE80211_SCAN_ACTIVE,
+ IEEE80211_SCAN_FOREVER,
+ ic->ic_des_nssid,
+ ic->ic_des_ssid);
+ } else {
+ ieee80211_iterate_nodes(&ic->ic_sta,
+ sta_disassoc, ic);
+ goto createibss;
}
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_begin_scan(ic, arg);
+ break;
+ default:
break;
}
break;
case IEEE80211_S_AUTH:
+ KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+ ("switch to %s state when operating in mode %u",
+ ieee80211_state_name[nstate], ic->ic_opmode));
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
@@ -1015,19 +1232,19 @@
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
- switch (arg) {
+ switch (arg & 0xff) {
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 */
+ sta_authretry(ic, ni, arg>>8);
break;
}
break;
case IEEE80211_S_RUN:
- switch (arg) {
+ switch (arg & 0xff) {
case IEEE80211_FC0_SUBTYPE_AUTH:
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_AUTH, 2);
@@ -1043,27 +1260,35 @@
break;
}
break;
+ default:
+ break;
}
break;
case IEEE80211_S_ASSOC:
+ KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+ ("switch to %s state when operating in mode %u",
+ ieee80211_state_name[nstate], ic->ic_opmode));
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
- case IEEE80211_S_ASSOC:
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
"%s: invalid transition\n", __func__);
break;
case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
break;
case IEEE80211_S_RUN:
ieee80211_sta_leave(ic, ni);
if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+ IEEE80211_SEND_MGMT(ic, ni, arg ?
+ IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
}
break;
+ default:
+ break;
}
break;
case IEEE80211_S_RUN:
@@ -1072,8 +1297,20 @@
}
switch (ostate) {
case IEEE80211_S_INIT:
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ ic->ic_opmode == IEEE80211_M_WDS ||
+ ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately. Because
+ * of this explicitly sync the scanner state.
+ */
+ ieee80211_scan_update(ic);
+ ieee80211_create_ibss(ic,
+ ieee80211_ht_adjust_channel(ic,
+ ic->ic_curchan, ic->ic_flags_ext));
break;
+ }
/* fall thru... */
case IEEE80211_S_AUTH:
IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
@@ -1101,12 +1338,16 @@
IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
}
#endif
- ic->ic_mgt_timer = 0;
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ieee80211_scan_assoc_success(ic,
+ ni->ni_macaddr);
ieee80211_notify_node_join(ic, ni,
arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ }
if_start(ifp); /* XXX not authorized yet */
break;
+ default:
+ break;
}
if (ostate != IEEE80211_S_RUN &&
ic->ic_opmode == IEEE80211_M_STA &&
@@ -1144,8 +1385,10 @@
* Enable inactivity processing.
* XXX
*/
- ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
- ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
+ callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+ ieee80211_node_timeout, ic);
+ break;
+ default:
break;
}
return 0;
Index: ieee80211_freebsd.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_freebsd.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_freebsd.c -L sys/net80211/ieee80211_freebsd.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_freebsd.c
+++ sys/net80211/ieee80211_freebsd.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,8 +10,6 @@
* 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
@@ -26,7 +24,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_freebsd.c,v 1.7.2.2 2005/12/22 19:22:51 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_freebsd.c,v 1.16.2.1 2007/11/11 17:44:35 sam Exp $");
/*
* IEEE 802.11 support (FreeBSD-specific code)
@@ -56,6 +54,27 @@
SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
0, "debugging printfs");
#endif
+extern int ieee80211_recv_bar_ena;
+SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
+ 0, "BAR frame processing (ena/dis)");
+
+#ifdef IEEE80211_AMPDU_AGE
+static int
+ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
+{
+ extern int ieee80211_ampdu_age;
+ int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
+ int error;
+
+ error = sysctl_handle_int(oidp, &du_age, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
+ return 0;
+}
+SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
+ ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
+#endif
static int
ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
@@ -138,6 +157,7 @@
if (ic->ic_sysctl != NULL) {
sysctl_ctx_free(ic->ic_sysctl);
+ FREE(ic->ic_sysctl, M_DEVBUF);
ic->ic_sysctl = NULL;
}
}
@@ -150,6 +170,35 @@
return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
}
+void
+ieee80211_drain_ifq(struct ifqueue *ifq)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ for (;;) {
+ IF_DEQUEUE(ifq, m);
+ if (m == NULL)
+ break;
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ KASSERT(ni != NULL, ("frame w/o node"));
+ ieee80211_free_node(ni);
+ m->m_pkthdr.rcvif = NULL;
+
+ m_freem(m);
+ }
+}
+
+/*
+ * As above, for mbufs allocated with m_gethdr/MGETHDR
+ * or initialized by M_COPY_PKTHDR.
+ */
+#define MC_ALIGN(m, len) \
+do { \
+ (m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1); \
+} while (/* CONSTCOND */ 0)
+
/*
* Allocate and setup a management frame of the specified
* size. We return the mbuf and a pointer to the start
@@ -160,7 +209,7 @@
* can use this interface too.
*/
struct mbuf *
-ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
+ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
{
struct mbuf *m;
u_int len;
@@ -169,11 +218,10 @@
* 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);
+ len = roundup2(headroom + pktlen, 4);
KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
if (len < MINCLSIZE) {
- m = m_gethdr(M_NOWAIT, MT_HEADER);
+ m = m_gethdr(M_NOWAIT, MT_DATA);
/*
* Align the data in case additional headers are added.
* This should only happen when a WEP header is added
@@ -182,27 +230,63 @@
*/
if (m != NULL)
MH_ALIGN(m, len);
- } else
- m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR);
+ } else {
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m != NULL)
+ MC_ALIGN(m, len);
+ }
if (m != NULL) {
- m->m_data += sizeof(struct ieee80211_frame);
+ m->m_data += headroom;
*frm = m->m_data;
}
return m;
}
+int
+ieee80211_add_callback(struct mbuf *m,
+ void (*func)(struct ieee80211_node *, void *, int), void *arg)
+{
+ struct m_tag *mtag;
+ struct ieee80211_cb *cb;
+
+ mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK,
+ sizeof(struct ieee80211_cb), M_NOWAIT);
+ if (mtag == NULL)
+ return 0;
+
+ cb = (struct ieee80211_cb *)(mtag+1);
+ cb->func = func;
+ cb->arg = arg;
+ m_tag_prepend(m, mtag);
+ m->m_flags |= M_TXCB;
+ return 1;
+}
+
+void
+ieee80211_process_callback(struct ieee80211_node *ni,
+ struct mbuf *m, int status)
+{
+ struct m_tag *mtag;
+
+ mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL);
+ if (mtag != NULL) {
+ struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1);
+ cb->func(ni, cb->arg, status);
+ }
+}
+
#include <sys/libkern.h>
void
get_random_bytes(void *p, size_t n)
{
- u_int8_t *dp = p;
+ uint8_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;
+ uint32_t v = arc4random();
+ size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n;
+ bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n);
+ dp += sizeof(uint32_t), n -= nb;
}
}
@@ -249,8 +333,7 @@
{
struct ifnet *ifp = ic->ic_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: notify scan done\n", ic->ic_ifp->if_xname);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
/* dispatch wireless event indicating scan completed */
rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
@@ -310,14 +393,9 @@
void
ieee80211_load_module(const char *modname)
{
-#ifdef notyet
- 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);
- }
+#ifdef notyet
+ (void)kern_kldload(curthread, modname, NULL);
#else
printf("%s: load the %s module by hand for now.\n", __func__, modname);
#endif
Index: ieee80211_crypto_wep.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_wep.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_wep.c -L sys/net80211/ieee80211_crypto_wep.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_crypto_wep.c
+++ sys/net80211/ieee80211_crypto_wep.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
* 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
@@ -30,7 +24,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_wep.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_wep.c,v 1.10 2007/06/11 03:36:54 sam Exp $");
/*
* IEEE 802.11 WEP crypto support.
@@ -54,7 +48,7 @@
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_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid);
static int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
static int wep_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -79,7 +73,7 @@
struct wep_ctx {
struct ieee80211com *wc_ic; /* for diagnostics */
- u_int32_t wc_iv; /* initial vector for crypto */
+ uint32_t wc_iv; /* initial vector for crypto */
};
/* number of references from net80211 layer */
@@ -123,12 +117,12 @@
* Add privacy headers appropriate for the specified key.
*/
static int
-wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+wep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct wep_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->wc_ic;
- u_int32_t iv;
- u_int8_t *ivp;
+ uint32_t iv;
+ uint8_t *ivp;
int hdrlen;
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -139,7 +133,7 @@
M_PREPEND(m, wep.ic_header, M_NOWAIT);
if (m == NULL)
return 0;
- ivp = mtod(m, u_int8_t *);
+ ivp = mtod(m, uint8_t *);
ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
ivp += hdrlen;
@@ -235,7 +229,7 @@
/*
* Copy up 802.11 header and strip crypto bits.
*/
- ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
+ ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen);
m_adj(m, wep.ic_header);
m_adj(m, -wep.ic_trailer);
@@ -312,7 +306,7 @@
#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 rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
uint32_t i, j, k, crc;
size_t buflen, data_len;
@@ -323,7 +317,7 @@
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, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
/* Setup RC4 state */
@@ -394,7 +388,7 @@
#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 rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
uint32_t i, j, k, crc;
size_t buflen, data_len;
@@ -405,7 +399,7 @@
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, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
/* Setup RC4 state */
@@ -479,32 +473,4 @@
/*
* 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:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_wep: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
- }
- if (type == 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);
+IEEE80211_CRYPTO_MODULE(wep, 1);
Index: ieee80211_node.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211_node.h -L sys/net80211/ieee80211_node.h -u -r1.2 -r1.3
--- sys/net80211/ieee80211_node.h
+++ sys/net80211/ieee80211_node.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,12 +23,13 @@
* (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: src/sys/net80211/ieee80211_node.h,v 1.17.2.5 2006/03/13 03:05:47 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.29.2.1 2007/11/11 17:44:36 sam Exp $
*/
#ifndef _NET80211_IEEE80211_NODE_H_
#define _NET80211_IEEE80211_NODE_H_
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_nodestats */
+#include <net80211/ieee80211_ht.h> /* for aggregation state */
/*
* Each ieee80211com instance has a single timer that fires once a
@@ -58,23 +53,26 @@
#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_TRANS_WAIT 2 /* mgt frame tx timer (secs) */
+
+/* threshold for aging overlapping non-ERP bss */
+#define IEEE80211_NONERP_PRESENT_AGE msecs_to_ticks(60*1000)
#define IEEE80211_NODE_HASHSIZE 32
/* simple hash is enough for variation of macaddr */
#define IEEE80211_NODE_HASH(addr) \
- (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
+ (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
IEEE80211_NODE_HASHSIZE)
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 */
+ uint8_t rsn_mcastcipher; /* mcast/group cipher */
+ uint8_t rsn_mcastkeylen; /* mcast key length */
+ uint8_t rsn_ucastcipherset; /* unicast cipher set */
+ uint8_t rsn_ucastcipher; /* selected unicast cipher */
+ uint8_t rsn_ucastkeylen; /* unicast key length */
+ uint8_t rsn_keymgmtset; /* key mangement algorithms */
+ uint8_t rsn_keymgmt; /* selected key mgmt algo */
+ uint16_t rsn_caps; /* capabilities */
};
struct ieee80211_node_table;
@@ -93,52 +91,87 @@
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 */
+ uint8_t ni_authmode; /* authentication algorithm */
+ uint8_t ni_ath_flags; /* Atheros feature flags */
+ /* NB: These must have the same values as IEEE80211_ATHC_* */
+#define IEEE80211_NODE_TURBOP 0x0001 /* Turbo prime enable */
+#define IEEE80211_NODE_COMP 0x0002 /* Compresssion enable */
+#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */
+#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */
+#define IEEE80211_NODE_AR 0x0010 /* AR capable */
+#define IEEE80211_NODE_BOOST 0x0080
+#define IEEE80211_NODE_PSUPDATE 0x0200 /* power save state changed */
+#define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */
+ uint16_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 */
#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */
- 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 */
+#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
+#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
+#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
+#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
+ uint16_t ni_ath_defkeyix;/* Atheros def key index */
+ uint16_t ni_associd; /* assoc response */
+ uint16_t ni_txpower; /* current transmit power */
+ uint16_t ni_vlan; /* vlan tag */
+ uint32_t ni_jointime; /* time of join (secs) */
+ uint32_t *ni_challenge; /* shared-key challenge */
+ uint8_t *ni_wpa_ie; /* captured WPA ie */
+ uint8_t *ni_rsn_ie; /* captured RSN ie */
+ uint8_t *ni_wme_ie; /* captured WME ie */
+ uint8_t *ni_ath_ie; /* captured Atheros ie */
+ /* tx seq per-tid */
+ uint16_t ni_txseqs[IEEE80211_TID_SIZE];
+ /* rx seq previous per-tid*/
+ uint16_t ni_rxseqs[IEEE80211_TID_SIZE];
+ uint32_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 */
- u_int8_t ni_rssi; /* recv ssi */
+ uint32_t ni_rstamp; /* recv timestamp */
+ int8_t ni_rssi; /* recv ssi */
+ int8_t ni_noise; /* noise floor */
/* header */
- u_int8_t ni_macaddr[IEEE80211_ADDR_LEN];
- u_int8_t ni_bssid[IEEE80211_ADDR_LEN];
+ uint8_t ni_macaddr[IEEE80211_ADDR_LEN];
+ uint8_t ni_bssid[IEEE80211_ADDR_LEN];
/* beacon, probe response */
union {
- u_int8_t data[8];
- u_int64_t tsf;
+ uint8_t data[8];
+ uint64_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];
+ uint16_t ni_intval; /* beacon interval */
+ uint16_t ni_capinfo; /* capabilities */
+ uint8_t ni_esslen;
+ uint8_t ni_essid[IEEE80211_NWID_LEN];
struct ieee80211_rateset ni_rates; /* negotiated rate set */
- struct ieee80211_channel *ni_chan; /* XXX multiple uses */
- u_int16_t ni_fhdwell; /* FH only */
- u_int8_t ni_fhindex; /* FH only */
- u_int8_t ni_erp; /* ERP from beacon/probe resp */
- u_int16_t ni_timoff; /* byte offset to TIM ie */
- u_int8_t ni_dtim_period; /* DTIM period */
- u_int8_t ni_dtim_count; /* DTIM count for last bcn */
+ struct ieee80211_channel *ni_chan;
+ uint16_t ni_fhdwell; /* FH only */
+ uint8_t ni_fhindex; /* FH only */
+ uint8_t ni_erp; /* ERP from beacon/probe resp */
+ uint16_t ni_timoff; /* byte offset to TIM ie */
+ uint8_t ni_dtim_period; /* DTIM period */
+ uint8_t ni_dtim_count; /* DTIM count for last bcn */
+
+ /* 11n state */
+ uint8_t *ni_htcap_ie; /* captured HTCAP ie */
+ uint16_t ni_htcap; /* HT capabilities */
+ uint8_t ni_htparam; /* HT params */
+ uint8_t ni_htctlchan; /* HT control channel */
+ uint8_t ni_ht2ndchan; /* HT 2nd channel */
+ uint8_t ni_htopmode; /* HT operating mode */
+ uint8_t ni_htstbc; /* HT */
+ uint8_t ni_reqcw; /* requested tx channel width */
+ uint8_t ni_chw; /* negotiated channel width */
+ struct ieee80211_htrateset ni_htrates; /* negotiated ht rate set */
+ struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_AC];
+ struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
/* others */
int ni_fails; /* failure count to associate */
@@ -147,10 +180,13 @@
int ni_txrate; /* index to ni_rates[] */
struct ifqueue ni_savedq; /* ps-poll queue */
struct ieee80211_nodestats ni_stats; /* per-node statistics */
- u_int32_t ni_pad[8]; /* future expansion */
};
MALLOC_DECLARE(M_80211_NODE);
+#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
+#define IEEE80211_NODE_AMPDU \
+ (IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX)
+
#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd)
#define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++)
@@ -186,15 +222,14 @@
void ieee80211_node_authorize(struct ieee80211_node *);
void ieee80211_node_unauthorize(struct ieee80211_node *);
-void ieee80211_begin_scan(struct ieee80211com *, int);
-int ieee80211_next_scan(struct ieee80211com *);
void ieee80211_probe_curchan(struct ieee80211com *, int);
void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
void ieee80211_reset_bss(struct ieee80211com *);
-void ieee80211_cancel_scan(struct ieee80211com *);
-void ieee80211_end_scan(struct ieee80211com *);
+void ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
-int ieee80211_sta_join(struct ieee80211com *, struct ieee80211_node *);
+struct ieee80211_scan_entry;
+int ieee80211_sta_join(struct ieee80211com *,
+ const struct ieee80211_scan_entry *);
void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
/*
@@ -208,46 +243,43 @@
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];
+ struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
+ int nt_keyixmax; /* keyixmap size */
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 */
- struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
- int nt_keyixmax; /* keyixmap size */
-
- void (*nt_timeout)(struct ieee80211_node_table *);
};
-void ieee80211_node_table_reset(struct ieee80211_node_table *);
struct ieee80211_node *ieee80211_alloc_node(
- struct ieee80211_node_table *, const u_int8_t *);
+ struct ieee80211_node_table *, const uint8_t *);
struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *,
- const u_int8_t *macaddr);
+ const uint8_t *macaddr);
struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
- const u_int8_t *);
+ const uint8_t *);
#ifdef IEEE80211_DEBUG_REFCNT
void ieee80211_free_node_debug(struct ieee80211_node *,
const char *func, int line);
-struct ieee80211_node *ieee80211_find_node_debug(
- struct ieee80211_node_table *, const u_int8_t *,
+struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
+ const uint8_t *,
+ const char *func, int line);
+struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *,
+ const struct ieee80211_frame_min *,
const char *func, int line);
-struct ieee80211_node * ieee80211_find_rxnode_debug(
- struct ieee80211com *, const struct ieee80211_frame_min *,
+struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
+ struct ieee80211com *,
+ const struct ieee80211_frame_min *, uint16_t keyix,
const char *func, int line);
struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
struct ieee80211com *,
- const struct ieee80211_frame_min *, u_int16_t keyix,
+ const struct ieee80211_frame_min *, uint16_t keyix,
const char *func, int line);
-struct ieee80211_node *ieee80211_find_txnode_debug(
- struct ieee80211com *, const u_int8_t *,
+struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211com *,
+ const uint8_t *,
const char *func, int line);
-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);
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,
+ struct ieee80211_node_table *, const uint8_t *macaddr,
+ u_int ssidlen, const uint8_t *ssid,
const char *func, int line);
#define ieee80211_free_node(ni) \
ieee80211_free_node_debug(ni, __func__, __LINE__)
@@ -259,28 +291,24 @@
ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __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
void ieee80211_free_node(struct ieee80211_node *);
-struct ieee80211_node *ieee80211_find_node(
- struct ieee80211_node_table *, const u_int8_t *);
-struct ieee80211_node * ieee80211_find_rxnode(
- struct ieee80211com *, const struct ieee80211_frame_min *);
+struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *,
+ const uint8_t *);
+struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *,
+ const struct ieee80211_frame_min *);
struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
- const struct ieee80211_frame_min *, u_int16_t keyix);
-struct ieee80211_node *ieee80211_find_txnode(
- struct ieee80211com *, const u_int8_t *);
-struct ieee80211_node *ieee80211_find_node_with_channel(
- struct ieee80211_node_table *, const u_int8_t *macaddr,
- struct ieee80211_channel *);
+ const struct ieee80211_frame_min *, uint16_t keyix);
+struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
+ const uint8_t *);
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);
+ struct ieee80211_node_table *, const uint8_t *macaddr,
+ u_int ssidlen, const uint8_t *ssid);
#endif
int ieee80211_node_delucastkey(struct ieee80211_node *);
+void ieee80211_node_timeout(void *arg);
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
@@ -290,46 +318,19 @@
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);
-struct ieee80211_node *ieee80211_fakeup_adhoc_node(
- struct ieee80211_node_table *, const u_int8_t macaddr[]);
-void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
-void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
-u_int8_t ieee80211_getrssi(struct ieee80211com *ic);
+void ieee80211_notify_erp(struct ieee80211com *);
-/*
- * Parameters supplied when adding/updating an entry in a
- * scan cache. Pointer variables should be set to NULL
- * if no data is available. Pointer references can be to
- * local data; any information that is saved will be copied.
- * All multi-byte values must be in host byte order.
- */
-struct ieee80211_scanparams {
- u_int16_t capinfo; /* 802.11 capabilities */
- u_int16_t fhdwell; /* FHSS dwell interval */
- u_int8_t chan; /* */
- u_int8_t bchan;
- u_int8_t fhindex;
- u_int8_t erp;
- u_int16_t bintval;
- u_int8_t timoff;
- u_int8_t *tim;
- u_int8_t *tstamp;
- u_int8_t *country;
- u_int8_t *ssid;
- u_int8_t *rates;
- u_int8_t *xrates;
- u_int8_t *wpa;
- u_int8_t *wme;
-};
-
-void ieee80211_add_scan(struct ieee80211com *,
- const struct ieee80211_scanparams *,
- const struct ieee80211_frame *,
- int subtype, int rssi, int rstamp);
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(
+ struct ieee80211_node_table *, const uint8_t macaddr[]);
+struct ieee80211_scanparams;
void ieee80211_init_neighbor(struct ieee80211_node *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
+void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
+void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
+int8_t ieee80211_getrssi(struct ieee80211com *);
+void ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *);
#endif /* _NET80211_IEEE80211_NODE_H_ */
Index: ieee80211_crypto.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto.c -L sys/net80211/ieee80211_crypto.c -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_crypto.c
+++ sys/net80211/ieee80211_crypto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.10.2.2 2005/09/03 22:40:02 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.16 2007/06/11 03:36:54 sam Exp $");
/*
* IEEE 802.11 generic crypto support.
@@ -90,7 +84,7 @@
}
static int
null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
- const u_int8_t mac[IEEE80211_ADDR_LEN])
+ const uint8_t mac[IEEE80211_ADDR_LEN])
{
return 1;
}
@@ -131,7 +125,7 @@
static __inline int
dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
- const u_int8_t mac[IEEE80211_ADDR_LEN])
+ const uint8_t mac[IEEE80211_ADDR_LEN])
{
return ic->ic_crypto.cs_key_set(ic, key, mac);
}
@@ -467,7 +461,7 @@
*/
int
ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
- const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
const struct ieee80211_cipher *cip = key->wk_cipher;
@@ -511,7 +505,7 @@
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
- u_int8_t keyid;
+ uint8_t keyid;
/*
* Multicast traffic always uses the multicast key.
@@ -521,7 +515,7 @@
*/
wh = mtod(m, struct ieee80211_frame *);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
- ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+ IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
"[%s] no default transmit key (%s) deftxkey %u\n",
@@ -555,8 +549,8 @@
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
- const u_int8_t *ivp;
- u_int8_t keyid;
+ const uint8_t *ivp;
+ uint8_t keyid;
/* NB: this minimum size data frame could be bigger */
if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
@@ -574,10 +568,10 @@
* the key id in the header is meaningless (typically 0).
*/
wh = mtod(m, struct ieee80211_frame *);
- ivp = mtod(m, const u_int8_t *) + hdrlen; /* XXX contig */
+ ivp = mtod(m, const uint8_t *) + hdrlen; /* XXX contig */
keyid = ivp[IEEE80211_WEP_IVLEN];
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
- ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
+ IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey))
k = &ic->ic_nw_keys[keyid >> 6];
else
k = &ni->ni_ucastkey;
@@ -592,7 +586,7 @@
"[%s] unable to pullup %s header\n",
ether_sprintf(wh->i_addr2), cip->ic_name);
ic->ic_stats.is_rx_wepfail++; /* XXX */
- return 0;
+ return NULL;
}
return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
Index: ieee80211.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/net80211/ieee80211.c -L sys/net80211/ieee80211.c -u -r1.2 -r1.3
--- sys/net80211/ieee80211.c
+++ sys/net80211/ieee80211.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -31,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.19.2.7 2006/03/11 19:25:23 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.43.2.2 2007/12/07 05:46:08 kmacy Exp $");
/*
* IEEE 802.11 generic handler
@@ -59,13 +53,36 @@
"FH", /* IEEE80211_MODE_FH */
"turboA", /* IEEE80211_MODE_TURBO_A */
"turboG", /* IEEE80211_MODE_TURBO_G */
+ "sturboA", /* IEEE80211_MODE_STURBO_A */
+ "11na", /* IEEE80211_MODE_11NA */
+ "11ng", /* IEEE80211_MODE_11NG */
};
+/*
+ * Default supported rates for 802.11 operation (in IEEE .5Mb units).
+ */
+#define B(r) ((r) | IEEE80211_RATE_BASIC)
+static const struct ieee80211_rateset ieee80211_rateset_11a =
+ { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } };
+static const struct ieee80211_rateset ieee80211_rateset_half =
+ { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } };
+static const struct ieee80211_rateset ieee80211_rateset_quarter =
+ { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } };
+static const struct ieee80211_rateset ieee80211_rateset_11b =
+ { 4, { B(2), B(4), B(11), B(22) } };
+/* NB: OFDM rates are handled specially based on mode */
+static const struct ieee80211_rateset ieee80211_rateset_11g =
+ { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
+#undef B
+
+static int media_status(enum ieee80211_opmode ,
+ const struct ieee80211_channel *);
+
/* 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 uint8_t ieee80211_vapmap[32]; /* enough for 256 */
static struct mtx ieee80211_vap_mtx;
MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
@@ -74,7 +91,7 @@
{
#define N(a) (sizeof(a)/sizeof(a[0]))
int i;
- u_int8_t b;
+ uint8_t b;
mtx_lock(&ieee80211_vap_mtx);
ic->ic_vap = 0;
@@ -118,86 +135,134 @@
return ENETRESET;
}
+/*
+ * Fill in 802.11 available channel set, mark
+ * all available channels as active, and pick
+ * a default channel if not already specified.
+ */
+static void
+ieee80211_chan_init(struct ieee80211com *ic)
+{
+#define DEFAULTRATES(m, def) do { \
+ if (isset(ic->ic_modecaps, m) && ic->ic_sup_rates[m].rs_nrates == 0) \
+ ic->ic_sup_rates[m] = def; \
+} while (0)
+ struct ieee80211_channel *c;
+ int i;
+
+ KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
+ ("invalid number of channels specified: %u", ic->ic_nchans));
+ memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+ setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ KASSERT(c->ic_flags != 0, ("channel with no flags"));
+ KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX,
+ ("channel with bogus ieee number %u", c->ic_ieee));
+ setbit(ic->ic_chan_avail, c->ic_ieee);
+ /*
+ * Identify mode capabilities.
+ */
+ if (IEEE80211_IS_CHAN_A(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11A);
+ if (IEEE80211_IS_CHAN_B(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11B);
+ if (IEEE80211_IS_CHAN_ANYG(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11G);
+ if (IEEE80211_IS_CHAN_FHSS(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_FH);
+ if (IEEE80211_IS_CHAN_108A(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A);
+ if (IEEE80211_IS_CHAN_108G(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G);
+ if (IEEE80211_IS_CHAN_ST(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A);
+ if (IEEE80211_IS_CHAN_HTA(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11NA);
+ if (IEEE80211_IS_CHAN_HTG(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_11NG);
+ }
+ /* initialize candidate channels to all available */
+ memcpy(ic->ic_chan_active, ic->ic_chan_avail,
+ sizeof(ic->ic_chan_avail));
+
+ ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ ic->ic_bsschan = IEEE80211_CHAN_ANYC;
+ ic->ic_prevchan = NULL;
+ /* arbitrarily pick the first channel */
+ ic->ic_curchan = &ic->ic_channels[0];
+
+ /* fillin well-known rate sets if driver has not specified */
+ DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b);
+ DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g);
+ DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a);
+ DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a);
+ DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g);
+
+ /*
+ * Set auto mode to reset active channel state and any desired channel.
+ */
+ (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO);
+#undef DEFAULTRATES
+}
+
void
ieee80211_ifattach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_channel *c;
- int i;
ether_ifattach(ifp, ic->ic_myaddr);
+ ifp->if_output = ieee80211_output;
+
bpfattach2(ifp, DLT_IEEE802_11,
sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
- ieee80211_crypto_attach(ic);
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_headroom
+ + sizeof(struct ieee80211_qosframe_addr4)
+ + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ + IEEE80211_WEP_EXTIVLEN;
+ /* XXX no way to recalculate on ifdetach */
+ if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
+ /* XXX sanity check... */
+ max_linkhdr = ALIGN(ifp->if_hdrlen);
+ max_hdr = max_linkhdr + max_protohdr;
+ max_datalen = MHLEN - max_hdr;
+ }
/*
- * Fill in 802.11 available channel set, mark
- * all available channels as active, and pick
- * a default channel if not already specified.
+ * Fill in 802.11 available channel set, mark all
+ * available channels as active, and pick a default
+ * channel if not already specified.
*/
- memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
- ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO;
- for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
- c = &ic->ic_channels[i];
- if (c->ic_flags) {
- /*
- * Verify driver passed us valid data.
- */
- if (i != ieee80211_chan2ieee(ic, c)) {
- if_printf(ifp, "bad channel ignored; "
- "freq %u flags %x number %u\n",
- c->ic_freq, c->ic_flags, i);
- c->ic_flags = 0; /* NB: remove */
- continue;
- }
- setbit(ic->ic_chan_avail, i);
- /*
- * Identify mode capabilities.
- */
- if (IEEE80211_IS_CHAN_A(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_11A;
- if (IEEE80211_IS_CHAN_B(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_11B;
- if (IEEE80211_IS_CHAN_PUREG(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
- if (IEEE80211_IS_CHAN_FHSS(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_FH;
- if (IEEE80211_IS_CHAN_T(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A;
- if (IEEE80211_IS_CHAN_108G(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G;
- if (ic->ic_curchan == NULL) {
- /* arbitrarily pick the first channel */
- ic->ic_curchan = &ic->ic_channels[i];
- }
- }
- }
- /* validate ic->ic_curmode */
- if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0)
- ic->ic_curmode = IEEE80211_MODE_AUTO;
- ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ ieee80211_chan_init(ic);
+
+ if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */
+ ic->ic_flags |= IEEE80211_F_BGSCAN;
#if 0
- /*
- * Enable WME by default if we're capable.
- */
- if (ic->ic_caps & IEEE80211_C_WME)
+ /* XXX not until WME+WPA issues resolved */
+ if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */
ic->ic_flags |= IEEE80211_F_WME;
#endif
if (ic->ic_caps & IEEE80211_C_BURST)
ic->ic_flags |= IEEE80211_F_BURST;
- (void) ieee80211_setmode(ic, ic->ic_curmode);
+ ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
+ IEEE80211_LOCK_INIT(ic, "ieee80211com");
IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
ic->ic_lintval = ic->ic_bintval;
ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
+ ieee80211_crypto_attach(ic);
ieee80211_node_attach(ic);
+ ieee80211_power_attach(ic);
ieee80211_proto_attach(ic);
+ ieee80211_ht_attach(ic);
+ ieee80211_scan_attach(ic);
ieee80211_add_vap(ic);
@@ -209,6 +274,9 @@
*/
if (ic->ic_reset == NULL)
ic->ic_reset = ieee80211_default_reset;
+
+ KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed"));
+ ifp->if_llsoftc = ic;
}
void
@@ -219,61 +287,98 @@
ieee80211_remove_vap(ic);
ieee80211_sysctl_detach(ic);
+ ieee80211_scan_detach(ic);
+ ieee80211_ht_detach(ic);
+ /* NB: must be called before ieee80211_node_detach */
ieee80211_proto_detach(ic);
ieee80211_crypto_detach(ic);
+ ieee80211_power_detach(ic);
ieee80211_node_detach(ic);
ifmedia_removeall(&ic->ic_media);
+ IEEE80211_LOCK_DESTROY(ic);
IEEE80211_BEACON_LOCK_DESTROY(ic);
bpfdetach(ifp);
ether_ifdetach(ifp);
}
+static __inline int
+mapgsm(u_int freq, u_int flags)
+{
+ freq *= 10;
+ if (flags & IEEE80211_CHAN_QUARTER)
+ freq += 5;
+ else if (flags & IEEE80211_CHAN_HALF)
+ freq += 10;
+ else
+ freq += 20;
+ /* NB: there is no 907/20 wide but leave room */
+ return (freq - 906*10) / 5;
+}
+
+static __inline int
+mappsb(u_int freq, u_int flags)
+{
+ return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5;
+}
+
/*
* Convert MHz frequency to IEEE channel number.
*/
-u_int
+int
ieee80211_mhz2ieee(u_int freq, u_int flags)
{
+#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990)
+ if (flags & IEEE80211_CHAN_GSM)
+ return mapgsm(freq, flags);
if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
if (freq == 2484)
return 14;
if (freq < 2484)
- return (freq - 2407) / 5;
+ return ((int) freq - 2407) / 5;
else
return 15 + ((freq - 2512) / 20);
} else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */
- return (freq - 5000) / 5;
+ if (freq <= 5000) {
+ /* XXX check regdomain? */
+ if (IS_FREQ_IN_PSB(freq))
+ return mappsb(freq, flags);
+ return (freq - 4000) / 5;
+ } else
+ return (freq - 5000) / 5;
} else { /* either, guess */
if (freq == 2484)
return 14;
- if (freq < 2484)
- return (freq - 2407) / 5;
- if (freq < 5000)
- return 15 + ((freq - 2512) / 20);
+ if (freq < 2484) {
+ if (907 <= freq && freq <= 922)
+ return mapgsm(freq, flags);
+ return ((int) freq - 2407) / 5;
+ }
+ if (freq < 5000) {
+ if (IS_FREQ_IN_PSB(freq))
+ return mappsb(freq, flags);
+ else if (freq > 4900)
+ return (freq - 4000) / 5;
+ else
+ return 15 + ((freq - 2512) / 20);
+ }
return (freq - 5000) / 5;
}
+#undef IS_FREQ_IN_PSB
}
/*
* Convert channel to IEEE channel number.
*/
-u_int
-ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c)
+int
+ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c)
{
- if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX])
- return c - ic->ic_channels;
- else if (c == IEEE80211_CHAN_ANYC)
- return IEEE80211_CHAN_ANY;
- else if (c != NULL) {
- if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n",
- c->ic_freq, c->ic_flags);
- return 0; /* XXX */
- } else {
+ if (c == NULL) {
if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
return 0; /* XXX */
}
+ return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee);
}
/*
@@ -282,6 +387,8 @@
u_int
ieee80211_ieee2mhz(u_int chan, u_int flags)
{
+ if (flags & IEEE80211_CHAN_GSM)
+ return 907 + 5 * (chan / 10);
if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
if (chan == 14)
return 2484;
@@ -290,8 +397,13 @@
else
return 2512 + ((chan-15)*20);
} else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
+ if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) {
+ chan -= 37;
+ return 4940 + chan*5 + (chan % 5 ? 2 : 0);
+ }
return 5000 + (chan*5);
} else { /* either, guess */
+ /* XXX can't distinguish PSB+GSM channels */
if (chan == 14)
return 2484;
if (chan < 14) /* 0-13 */
@@ -303,6 +415,97 @@
}
/*
+ * Locate a channel given a frequency+flags. We cache
+ * the previous lookup to optimize swithing between two
+ * channels--as happens with dynamic turbo.
+ */
+struct ieee80211_channel *
+ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags)
+{
+ struct ieee80211_channel *c;
+ int i;
+
+ flags &= IEEE80211_CHAN_ALLTURBO;
+ c = ic->ic_prevchan;
+ if (c != NULL && c->ic_freq == freq &&
+ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return c;
+ /* brute force search */
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (c->ic_freq == freq &&
+ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return c;
+ }
+ return NULL;
+}
+
+/*
+ * Locate a channel given a channel number+flags. We cache
+ * the previous lookup to optimize switching between two
+ * channels--as happens with dynamic turbo.
+ */
+struct ieee80211_channel *
+ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
+{
+ struct ieee80211_channel *c;
+ int i;
+
+ flags &= IEEE80211_CHAN_ALLTURBO;
+ c = ic->ic_prevchan;
+ if (c != NULL && c->ic_ieee == ieee &&
+ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return c;
+ /* brute force search */
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (c->ic_ieee == ieee &&
+ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return c;
+ }
+ return NULL;
+}
+
+static void
+addmedia(struct ieee80211com *ic, int mode, int mword)
+{
+#define TURBO(m) ((m) | IFM_IEEE80211_TURBO)
+#define ADD(_ic, _s, _o) \
+ ifmedia_add(&(_ic)->ic_media, \
+ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
+ static const u_int mopts[IEEE80211_MODE_MAX] = {
+ IFM_AUTO, /* IEEE80211_MODE_AUTO */
+ IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */
+ IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */
+ IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */
+ IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */
+ TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */
+ TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */
+ TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */
+ IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */
+ IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */
+ };
+ u_int mopt;
+
+ KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
+ mopt = mopts[mode];
+ KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO,
+ ("no media mapping for mode %u", mode));
+
+ ADD(ic, mword, mopt); /* e.g. 11a auto */
+ if (ic->ic_caps & IEEE80211_C_IBSS)
+ ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
+ if (ic->ic_caps & IEEE80211_C_HOSTAP)
+ ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
+ if (ic->ic_caps & IEEE80211_C_AHDEMO)
+ ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+ if (ic->ic_caps & IEEE80211_C_MONITOR)
+ ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+#undef ADD
+#undef TURBO
+}
+
+/*
* Setup the media data structures according to the channel and
* rate tables. This must be called by the driver after
* ieee80211_attach and before most anything else.
@@ -311,49 +514,42 @@
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 ifnet *ifp = ic->ic_ifp;
- struct ifmediareq imr;
- int i, j, mode, rate, maxrate, mword, mopt, r;
- struct ieee80211_rateset *rs;
+ int i, j, mode, rate, maxrate, mword, r;
+ const struct ieee80211_rateset *rs;
struct ieee80211_rateset allrates;
- /*
- * Do late attach work that must wait for any subclass
- * (i.e. driver) work such as overriding methods.
- */
- ieee80211_node_lateattach(ic);
+ /* NB: this works because the structure is initialized to zero */
+ if (LIST_EMPTY(&ic->ic_media.ifm_list)) {
+ /*
+ * Do late attach work that must wait for any subclass
+ * (i.e. driver) work such as overriding methods.
+ */
+ ieee80211_node_lateattach(ic);
+ } else {
+ /*
+ * We are re-initializing the channel list; clear
+ * the existing media state as the media routines
+ * don't suppress duplicates.
+ */
+ ifmedia_removeall(&ic->ic_media);
+ ieee80211_chan_init(ic);
+ }
+ ieee80211_power_lateattach(ic);
/*
* Fill in media characteristics.
*/
ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
maxrate = 0;
+ /*
+ * Add media for legacy operating modes.
+ */
memset(&allrates, 0, sizeof(allrates));
- for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
- static const u_int mopts[] = {
- IFM_AUTO,
- IFM_IEEE80211_11A,
- IFM_IEEE80211_11B,
- IFM_IEEE80211_11G,
- IFM_IEEE80211_FH,
- IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
- IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
- };
- if ((ic->ic_modecaps & (1<<mode)) == 0)
+ for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
+ if (isclr(ic->ic_modecaps, mode))
continue;
- mopt = mopts[mode];
- ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */
- if (ic->ic_caps & IEEE80211_C_IBSS)
- ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC);
- if (ic->ic_caps & IEEE80211_C_HOSTAP)
- ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
- if (ic->ic_caps & IEEE80211_C_AHDEMO)
- ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_caps & IEEE80211_C_MONITOR)
- ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
+ addmedia(ic, mode, IFM_AUTO);
if (mode == IEEE80211_MODE_AUTO)
continue;
rs = &ic->ic_sup_rates[mode];
@@ -362,17 +558,9 @@
mword = ieee80211_rate2media(ic, rate, mode);
if (mword == 0)
continue;
- ADD(ic, mword, mopt);
- if (ic->ic_caps & IEEE80211_C_IBSS)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
- if (ic->ic_caps & IEEE80211_C_HOSTAP)
- ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
- if (ic->ic_caps & IEEE80211_C_AHDEMO)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_caps & IEEE80211_C_MONITOR)
- ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+ addmedia(ic, mode, mword);
/*
- * Add rate to the collection of all rates.
+ * Add legacy rate to the collection of all rates.
*/
r = rate & IEEE80211_RATE_VAL;
for (j = 0; j < allrates.rs_nrates; j++)
@@ -393,23 +581,52 @@
IEEE80211_MODE_AUTO);
if (mword == 0)
continue;
- mword = IFM_SUBTYPE(mword); /* remove media options */
- ADD(ic, mword, 0);
- if (ic->ic_caps & IEEE80211_C_IBSS)
- ADD(ic, mword, IFM_IEEE80211_ADHOC);
- if (ic->ic_caps & IEEE80211_C_HOSTAP)
- ADD(ic, mword, IFM_IEEE80211_HOSTAP);
- if (ic->ic_caps & IEEE80211_C_AHDEMO)
- ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_caps & IEEE80211_C_MONITOR)
- ADD(ic, mword, IFM_IEEE80211_MONITOR);
+ /* NB: remove media options from mword */
+ addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
+ }
+ /*
+ * Add HT/11n media. Note that we do not have enough
+ * bits in the media subtype to express the MCS so we
+ * use a "placeholder" media subtype and any fixed MCS
+ * must be specified with a different mechanism.
+ */
+ for (; mode < IEEE80211_MODE_MAX; mode++) {
+ if (isclr(ic->ic_modecaps, mode))
+ continue;
+ addmedia(ic, mode, IFM_AUTO);
+ addmedia(ic, mode, IFM_IEEE80211_MCS);
}
- ieee80211_media_status(ifp, &imr);
- ifmedia_set(&ic->ic_media, imr.ifm_active);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
+ isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
+ addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
+ /* XXX could walk htrates */
+ /* XXX known array size */
+ if (ieee80211_htrates[15] > maxrate)
+ maxrate = ieee80211_htrates[15];
+ }
+
+ /* NB: strip explicit mode; we're actually in autoselect */
+ ifmedia_set(&ic->ic_media,
+ media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
if (maxrate)
ifp->if_baudrate = IF_Mbps(maxrate);
-#undef ADD
+}
+
+const struct ieee80211_rateset *
+ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c)
+{
+ if (IEEE80211_IS_CHAN_HALF(c))
+ return &ieee80211_rateset_half;
+ if (IEEE80211_IS_CHAN_QUARTER(c))
+ return &ieee80211_rateset_quarter;
+ if (IEEE80211_IS_CHAN_HTA(c))
+ return &ic->ic_sup_rates[IEEE80211_MODE_11A];
+ if (IEEE80211_IS_CHAN_HTG(c)) {
+ /* XXX does this work for basic rates? */
+ return &ic->ic_sup_rates[IEEE80211_MODE_11G];
+ }
+ return &ic->ic_sup_rates[ieee80211_chan2mode(c)];
}
void
@@ -417,44 +634,78 @@
{
struct ifnet *ifp = ic->ic_ifp;
int i, mode, rate, mword;
- struct ieee80211_rateset *rs;
+ const struct ieee80211_rateset *rs;
- for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
- if ((ic->ic_modecaps & (1<<mode)) == 0)
+ /* NB: skip AUTO since it has no rates */
+ for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) {
+ if (isclr(ic->ic_modecaps, mode))
continue;
if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
rs = &ic->ic_sup_rates[mode];
for (i = 0; i < rs->rs_nrates; i++) {
- rate = rs->rs_rates[i];
- mword = ieee80211_rate2media(ic, rate, mode);
+ mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode);
if (mword == 0)
continue;
+ rate = ieee80211_media2rate(mword);
printf("%s%d%sMbps", (i != 0 ? " " : ""),
- (rate & IEEE80211_RATE_VAL) / 2,
- ((rate & 0x1) != 0 ? ".5" : ""));
+ rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
}
printf("\n");
}
+ ieee80211_ht_announce(ic);
}
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+void
+ieee80211_announce_channels(struct ieee80211com *ic)
{
-#define IEEERATE(_ic,_m,_i) \
- ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
- int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
- for (i = 0; i < nrates; i++)
- if (IEEERATE(ic, mode, i) == rate)
- return i;
- return -1;
-#undef IEEERATE
+ const struct ieee80211_channel *c;
+ char type;
+ int i, cw;
+
+ printf("Chan Freq CW RegPwr MinPwr MaxPwr\n");
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (IEEE80211_IS_CHAN_ST(c))
+ type = 'S';
+ else if (IEEE80211_IS_CHAN_108A(c))
+ type = 'T';
+ else if (IEEE80211_IS_CHAN_108G(c))
+ type = 'G';
+ else if (IEEE80211_IS_CHAN_HT(c))
+ type = 'n';
+ else if (IEEE80211_IS_CHAN_A(c))
+ type = 'a';
+ else if (IEEE80211_IS_CHAN_ANYG(c))
+ type = 'g';
+ else if (IEEE80211_IS_CHAN_B(c))
+ type = 'b';
+ else
+ type = 'f';
+ if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c))
+ cw = 40;
+ else if (IEEE80211_IS_CHAN_HALF(c))
+ cw = 10;
+ else if (IEEE80211_IS_CHAN_QUARTER(c))
+ cw = 5;
+ else
+ cw = 20;
+ printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n"
+ , c->ic_ieee, c->ic_freq, type
+ , cw
+ , IEEE80211_IS_CHAN_HT40U(c) ? '+' :
+ IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' '
+ , c->ic_maxregpower
+ , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0
+ , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0
+ );
+ }
}
/*
* Find an instance by it's mac address.
*/
struct ieee80211com *
-ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN])
+ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct ieee80211com *ic;
@@ -478,6 +729,50 @@
return NULL;
}
+static int
+findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+{
+#define IEEERATE(_ic,_m,_i) \
+ ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
+ int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
+ for (i = 0; i < nrates; i++)
+ if (IEEERATE(ic, mode, i) == rate)
+ return i;
+ return -1;
+#undef IEEERATE
+}
+
+/*
+ * Convert a media specification to a rate index and possibly a mode
+ * (if the rate is fixed and the mode is specified as ``auto'' then
+ * we need to lock down the mode so the index is meanginful).
+ */
+static int
+checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+{
+
+ /*
+ * Check the rate table for the specified/current phy.
+ */
+ if (mode == IEEE80211_MODE_AUTO) {
+ int i;
+ /*
+ * In autoselect mode search for the rate.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
+ if (isset(ic->ic_modecaps, i) &&
+ findrate(ic, i, rate) != -1)
+ return 1;
+ }
+ return 0;
+ } else {
+ /*
+ * Mode is fixed, check for rate.
+ */
+ return (findrate(ic, mode, rate) != -1);
+ }
+}
+
/*
* Handle a media change request.
*/
@@ -488,7 +783,7 @@
struct ifmedia_entry *ime;
enum ieee80211_opmode newopmode;
enum ieee80211_phymode newphymode;
- int i, j, newrate, error = 0;
+ int newrate, error = 0;
ic = ieee80211_find_instance(ifp);
if (!ic) {
@@ -512,6 +807,12 @@
case IFM_IEEE80211_FH:
newphymode = IEEE80211_MODE_FH;
break;
+ case IFM_IEEE80211_11NA:
+ newphymode = IEEE80211_MODE_11NA;
+ break;
+ case IFM_IEEE80211_11NG:
+ newphymode = IEEE80211_MODE_11NG;
+ break;
case IFM_AUTO:
newphymode = IEEE80211_MODE_AUTO;
break;
@@ -523,55 +824,30 @@
* XXX does not apply to AUTO
*/
if (ime->ifm_media & IFM_IEEE80211_TURBO) {
- if (newphymode == IEEE80211_MODE_11A)
- newphymode = IEEE80211_MODE_TURBO_A;
- else if (newphymode == IEEE80211_MODE_11G)
+ if (newphymode == IEEE80211_MODE_11A) {
+ if (ic->ic_flags & IEEE80211_F_TURBOP)
+ newphymode = IEEE80211_MODE_TURBO_A;
+ else
+ newphymode = IEEE80211_MODE_STURBO_A;
+ } else if (newphymode == IEEE80211_MODE_11G)
newphymode = IEEE80211_MODE_TURBO_G;
else
return EINVAL;
}
- /*
- * Validate requested mode is available.
- */
- if ((ic->ic_modecaps & (1<<newphymode)) == 0)
- return EINVAL;
-
+ /* XXX HT40 +/- */
/*
* Next, the fixed/variable rate.
*/
- i = -1;
+ newrate = ic->ic_fixed_rate;
if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
/*
* Convert media subtype to rate.
*/
newrate = ieee80211_media2rate(ime->ifm_media);
- if (newrate == 0)
+ if (newrate == 0 || !checkrate(ic, newphymode, newrate))
return EINVAL;
- /*
- * Check the rate table for the specified/current phy.
- */
- if (newphymode == IEEE80211_MODE_AUTO) {
- /*
- * In autoselect mode search for the rate.
- */
- for (j = IEEE80211_MODE_11A;
- j < IEEE80211_MODE_MAX; j++) {
- if ((ic->ic_modecaps & (1<<j)) == 0)
- continue;
- i = findrate(ic, j, newrate);
- if (i != -1) {
- /* lock mode too */
- newphymode = j;
- break;
- }
- }
- } else {
- i = findrate(ic, newphymode, newrate);
- }
- if (i == -1) /* mode/rate mismatch */
- return EINVAL;
- }
- /* NB: defer rate setting to later */
+ } else
+ newrate = IEEE80211_FIXED_RATE_NONE;
/*
* Deduce new operating mode but don't install it just yet.
@@ -589,35 +865,18 @@
newopmode = IEEE80211_M_STA;
/*
- * Autoselect doesn't make sense when operating as an AP.
- * If no phy mode has been selected, pick one and lock it
- * down so rate tables can be used in forming beacon frames
- * and the like.
- */
- if (newopmode == IEEE80211_M_HOSTAP &&
- newphymode == IEEE80211_MODE_AUTO) {
- for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
- if (ic->ic_modecaps & (1<<j)) {
- newphymode = j;
- break;
- }
- }
-
- /*
* Handle phy mode change.
*/
- if (ic->ic_curmode != newphymode) { /* change phy mode */
- error = ieee80211_setmode(ic, newphymode);
- if (error != 0)
- return error;
+ if (ic->ic_des_mode != newphymode) { /* change phy mode */
+ ic->ic_des_mode = newphymode;
error = ENETRESET;
}
/*
* Committed to changes, install the rate setting.
*/
- if (ic->ic_fixed_rate != i) {
- ic->ic_fixed_rate = i; /* set fixed tx rate */
+ if (ic->ic_fixed_rate != newrate) {
+ ic->ic_fixed_rate = newrate; /* set fixed tx rate */
error = ENETRESET;
}
@@ -631,6 +890,7 @@
case IEEE80211_M_HOSTAP:
case IEEE80211_M_STA:
case IEEE80211_M_MONITOR:
+ case IEEE80211_M_WDS:
ic->ic_flags &= ~IEEE80211_F_IBSSON;
break;
case IEEE80211_M_IBSS:
@@ -653,11 +913,62 @@
return error;
}
+/*
+ * Common code to calculate the media status word
+ * from the operating mode and channel state.
+ */
+static int
+media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
+{
+ int status;
+
+ status = IFM_IEEE80211;
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ break;
+ case IEEE80211_M_IBSS:
+ status |= IFM_IEEE80211_ADHOC;
+ break;
+ case IEEE80211_M_HOSTAP:
+ status |= IFM_IEEE80211_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ status |= IFM_IEEE80211_MONITOR;
+ break;
+ case IEEE80211_M_AHDEMO:
+ status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
+ break;
+ case IEEE80211_M_WDS:
+ /* should not come here */
+ break;
+ }
+ if (IEEE80211_IS_CHAN_HTA(chan)) {
+ status |= IFM_IEEE80211_11NA;
+ } else if (IEEE80211_IS_CHAN_HTG(chan)) {
+ status |= IFM_IEEE80211_11NG;
+ } else if (IEEE80211_IS_CHAN_A(chan)) {
+ status |= IFM_IEEE80211_11A;
+ } else if (IEEE80211_IS_CHAN_B(chan)) {
+ status |= IFM_IEEE80211_11B;
+ } else if (IEEE80211_IS_CHAN_ANYG(chan)) {
+ status |= IFM_IEEE80211_11G;
+ } else if (IEEE80211_IS_CHAN_FHSS(chan)) {
+ status |= IFM_IEEE80211_FH;
+ }
+ /* XXX else complain? */
+
+ if (IEEE80211_IS_CHAN_TURBO(chan))
+ status |= IFM_IEEE80211_TURBO;
+
+ return status;
+}
+
void
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
struct ieee80211com *ic;
- struct ieee80211_rateset *rs;
+ enum ieee80211_phymode mode;
+ const struct ieee80211_rateset *rs;
ic = ieee80211_find_instance(ifp);
if (!ic) {
@@ -665,9 +976,17 @@
return;
}
imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
+ /*
+ * NB: use the current channel's mode to lock down a xmit
+ * rate only when running; otherwise we may have a mismatch
+ * in which case the rate will not be convertible.
+ */
+ if (ic->ic_state == IEEE80211_S_RUN) {
imr->ifm_status |= IFM_ACTIVE;
+ mode = ieee80211_chan2mode(ic->ic_curchan);
+ } else
+ mode = IEEE80211_MODE_AUTO;
+ imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
/*
* Calculate a current rate if possible.
*/
@@ -675,82 +994,18 @@
/*
* 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);
+ ic->ic_fixed_rate, mode);
} else if (ic->ic_opmode == IEEE80211_M_STA) {
/*
* In station mode report the current transmit rate.
+ * XXX HT rate
*/
rs = &ic->ic_bss->ni_rates;
imr->ifm_active |= ieee80211_rate2media(ic,
- rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode);
+ rs->rs_rates[ic->ic_bss->ni_txrate], mode);
} else
imr->ifm_active |= IFM_AUTO;
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- break;
- case IEEE80211_M_IBSS:
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- break;
- case IEEE80211_M_AHDEMO:
- /* should not come here */
- break;
- case IEEE80211_M_HOSTAP:
- imr->ifm_active |= IFM_IEEE80211_HOSTAP;
- break;
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
- }
- switch (ic->ic_curmode) {
- case IEEE80211_MODE_11A:
- imr->ifm_active |= IFM_IEEE80211_11A;
- break;
- case IEEE80211_MODE_11B:
- imr->ifm_active |= IFM_IEEE80211_11B;
- break;
- case IEEE80211_MODE_11G:
- imr->ifm_active |= IFM_IEEE80211_11G;
- break;
- case IEEE80211_MODE_FH:
- imr->ifm_active |= IFM_IEEE80211_FH;
- break;
- 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;
- }
-}
-
-void
-ieee80211_watchdog(struct ieee80211com *ic)
-{
- 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->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;
}
/*
@@ -762,170 +1017,80 @@
int
ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- static const u_int chanflags[] = {
- 0, /* IEEE80211_MODE_AUTO */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
- IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
- IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
- IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
- IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */
- IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
- };
- struct ieee80211_channel *c;
- u_int modeflags;
- int i;
-
- /* validate new mode */
- if ((ic->ic_modecaps & (1<<mode)) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: mode %u not supported (caps 0x%x)\n",
- __func__, mode, ic->ic_modecaps);
- return EINVAL;
- }
-
/*
- * Verify at least one channel is present in the available
- * channel list before committing to the new mode.
+ * Adjust basic rates in 11b/11g supported rate set.
+ * Note that if operating on a hal/quarter rate channel
+ * this is a noop as those rates sets are different
+ * and used instead.
*/
- KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
- modeflags = chanflags[mode];
- for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
- c = &ic->ic_channels[i];
- if (c->ic_flags == 0)
- continue;
- if (mode == IEEE80211_MODE_AUTO) {
- /* ignore static turbo channels for autoselect */
- if (!IEEE80211_IS_CHAN_T(c))
- break;
- } else {
- if ((c->ic_flags & modeflags) == modeflags)
- break;
- }
- }
- if (i > IEEE80211_CHAN_MAX) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: no channels found for mode %u\n", __func__, mode);
- return EINVAL;
- }
-
- /*
- * Calculate the active channel set.
- */
- memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active));
- for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
- c = &ic->ic_channels[i];
- if (c->ic_flags == 0)
- continue;
- if (mode == IEEE80211_MODE_AUTO) {
- /* take anything but static turbo channels */
- if (!IEEE80211_IS_CHAN_T(c))
- setbit(ic->ic_chan_active, i);
- } else {
- if ((c->ic_flags & modeflags) == modeflags)
- setbit(ic->ic_chan_active, i);
- }
- }
- /*
- * If no current/default channel is setup or the current
- * channel is wrong for the mode then pick the first
- * available channel from the active list. This is likely
- * not the right one.
- */
- if (ic->ic_ibss_chan == NULL ||
- isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
- for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
- if (isset(ic->ic_chan_active, i)) {
- 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;
-
- /*
- * Do mode-specific rate setup.
- */
- if (mode == IEEE80211_MODE_11G) {
- /*
- * Use a mixed 11b/11g rate set.
- */
- ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
- IEEE80211_MODE_11G);
- } 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];
+ if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
+ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], 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 for channels
- * where multiple operating modes are possible (e.g. 11g+11b).
- * In those cases we defer to the current operating mode when set.
+ * Return the phy mode for with the specified channel.
*/
enum ieee80211_phymode
-ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
+ieee80211_chan2mode(const struct ieee80211_channel *chan)
{
- if (IEEE80211_IS_CHAN_T(chan)) {
+
+ if (IEEE80211_IS_CHAN_HTA(chan))
+ return IEEE80211_MODE_11NA;
+ else if (IEEE80211_IS_CHAN_HTG(chan))
+ return IEEE80211_MODE_11NG;
+ else if (IEEE80211_IS_CHAN_108G(chan))
+ return IEEE80211_MODE_TURBO_G;
+ else if (IEEE80211_IS_CHAN_ST(chan))
+ return IEEE80211_MODE_STURBO_A;
+ else if (IEEE80211_IS_CHAN_TURBO(chan))
return IEEE80211_MODE_TURBO_A;
- } else if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+ else if (IEEE80211_IS_CHAN_A(chan))
return IEEE80211_MODE_11A;
- } else if (IEEE80211_IS_CHAN_FHSS(chan))
- return IEEE80211_MODE_FH;
- 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;
+ else if (IEEE80211_IS_CHAN_ANYG(chan))
return IEEE80211_MODE_11G;
- } else
+ else if (IEEE80211_IS_CHAN_B(chan))
return IEEE80211_MODE_11B;
+ else if (IEEE80211_IS_CHAN_FHSS(chan))
+ return IEEE80211_MODE_FH;
+
+ /* NB: should not get here */
+ printf("%s: cannot map channel to mode; freq %u flags 0x%x\n",
+ __func__, chan->ic_freq, chan->ic_flags);
+ return IEEE80211_MODE_11B;
+}
+
+struct ratemedia {
+ u_int match; /* rate + mode */
+ u_int media; /* if_media rate */
+};
+
+static int
+findmedia(const struct ratemedia rates[], int n, u_int match)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (rates[i].match == match)
+ return rates[i].media;
+ return IFM_AUTO;
}
/*
- * convert IEEE80211 rate value to ifmedia subtype.
- * ieee80211 rate is in unit of 0.5Mbps.
+ * Convert IEEE80211 rate value to ifmedia subtype.
+ * Rate is either a legacy rate in units of 0.5Mbps
+ * or an MCS index.
*/
int
ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
- static const struct {
- u_int m; /* rate + mode */
- u_int r; /* if_media rate */
- } rates[] = {
+ static const struct ratemedia rates[] = {
{ 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
{ 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
{ 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
@@ -953,38 +1118,73 @@
{ 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
{ 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
+ { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 },
+ { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 },
+ { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 },
/* NB: OFDM72 doesn't realy exist so we don't handle it */
};
- u_int mask, i;
+ static const struct ratemedia htrates[] = {
+ { 0, IFM_IEEE80211_MCS },
+ { 1, IFM_IEEE80211_MCS },
+ { 2, IFM_IEEE80211_MCS },
+ { 3, IFM_IEEE80211_MCS },
+ { 4, IFM_IEEE80211_MCS },
+ { 5, IFM_IEEE80211_MCS },
+ { 6, IFM_IEEE80211_MCS },
+ { 7, IFM_IEEE80211_MCS },
+ { 8, IFM_IEEE80211_MCS },
+ { 9, IFM_IEEE80211_MCS },
+ { 10, IFM_IEEE80211_MCS },
+ { 11, IFM_IEEE80211_MCS },
+ { 12, IFM_IEEE80211_MCS },
+ { 13, IFM_IEEE80211_MCS },
+ { 14, IFM_IEEE80211_MCS },
+ { 15, IFM_IEEE80211_MCS },
+ };
+ int m;
- mask = rate & IEEE80211_RATE_VAL;
+ /*
+ * Check 11n rates first for match as an MCS.
+ */
+ if (mode == IEEE80211_MODE_11NA) {
+ if (rate & IEEE80211_RATE_MCS) {
+ rate &= ~IEEE80211_RATE_MCS;
+ m = findmedia(htrates, N(htrates), rate);
+ if (m != IFM_AUTO)
+ return m | IFM_IEEE80211_11NA;
+ }
+ } else if (mode == IEEE80211_MODE_11NG) {
+ /* NB: 12 is ambiguous, it will be treated as an MCS */
+ if (rate & IEEE80211_RATE_MCS) {
+ rate &= ~IEEE80211_RATE_MCS;
+ m = findmedia(htrates, N(htrates), rate);
+ if (m != IFM_AUTO)
+ return m | IFM_IEEE80211_11NG;
+ }
+ }
+ rate &= IEEE80211_RATE_VAL;
switch (mode) {
case IEEE80211_MODE_11A:
+ case IEEE80211_MODE_11NA:
case IEEE80211_MODE_TURBO_A:
- mask |= IFM_IEEE80211_11A;
- break;
+ case IEEE80211_MODE_STURBO_A:
+ return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A);
case IEEE80211_MODE_11B:
- mask |= IFM_IEEE80211_11B;
- break;
+ return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B);
case IEEE80211_MODE_FH:
- mask |= IFM_IEEE80211_FH;
- break;
+ return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH);
case IEEE80211_MODE_AUTO:
/* NB: ic may be NULL for some drivers */
- if (ic && ic->ic_phytype == IEEE80211_T_FH) {
- mask |= IFM_IEEE80211_FH;
- break;
- }
+ if (ic && ic->ic_phytype == IEEE80211_T_FH)
+ return findmedia(rates, N(rates),
+ rate | IFM_IEEE80211_FH);
/* NB: hack, 11g matches both 11b+11a rates */
/* fall thru... */
case IEEE80211_MODE_11G:
+ case IEEE80211_MODE_11NG:
case IEEE80211_MODE_TURBO_G:
- mask |= IFM_IEEE80211_11G;
- break;
+ return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G);
}
- for (i = 0; i < N(rates); i++)
- if (rates[i].m == mask)
- return rates[i].r;
return IFM_AUTO;
#undef N
}
@@ -1013,6 +1213,12 @@
96, /* IFM_IEEE80211_OFDM48 */
108, /* IFM_IEEE80211_OFDM54 */
144, /* IFM_IEEE80211_OFDM72 */
+ 0, /* IFM_IEEE80211_DS354k */
+ 0, /* IFM_IEEE80211_DS512k */
+ 6, /* IFM_IEEE80211_OFDM3 */
+ 9, /* IFM_IEEE80211_OFDM4 */
+ 54, /* IFM_IEEE80211_OFDM27 */
+ -1, /* IFM_IEEE80211_MCS */
};
return IFM_SUBTYPE(mword) < N(ieeerates) ?
ieeerates[IFM_SUBTYPE(mword)] : 0;
Index: ieee80211_crypto_tkip.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_tkip.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_tkip.c -L sys/net80211/ieee80211_crypto_tkip.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_crypto_tkip.c
+++ sys/net80211/ieee80211_crypto_tkip.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
* 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
@@ -30,7 +24,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.9.2.2 2005/12/22 19:02:08 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.13 2007/06/11 03:36:54 sam Exp $");
/*
* IEEE 802.11i TKIP crypto support.
@@ -58,7 +52,7 @@
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_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid);
static int tkip_enmic(struct ieee80211_key *, struct mbuf *, int);
static int tkip_decap(struct ieee80211_key *, struct mbuf *, int);
static int tkip_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -156,11 +150,11 @@
* 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)
+tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->tc_ic;
- u_int8_t *ivp;
+ uint8_t *ivp;
int hdrlen;
/*
@@ -185,7 +179,7 @@
M_PREPEND(m, tkip.ic_header, M_NOWAIT);
if (m == NULL)
return 0;
- ivp = mtod(m, u_int8_t *);
+ ivp = mtod(m, uint8_t *);
memmove(ivp, ivp + tkip.ic_header, hdrlen);
ivp += hdrlen;
@@ -976,32 +970,4 @@
/*
* 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:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_tkip: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
- }
- if (type == 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_tkip, wlan, 1, 1, 1);
+IEEE80211_CRYPTO_MODULE(tkip, 1);
--- /dev/null
+++ sys/net80211/ieee80211_regdomain.c
@@ -0,0 +1,339 @@
+/*-
+ * Copyright (c) 2005-2007 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.
+ *
+ * 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>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_regdomain.c,v 1.3 2007/09/17 03:48:32 sam Exp $");
+
+/*
+ * IEEE 802.11 regdomain support.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+
+void
+ieee80211_regdomain_attach(struct ieee80211com *ic)
+{
+ ic->ic_regdomain = 0; /* XXX */
+ ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
+ ic->ic_location = 1+2; /* both */
+}
+
+void
+ieee80211_regdomain_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+addchan(struct ieee80211com *ic, int ieee, int flags)
+{
+ struct ieee80211_channel *c;
+
+ c = &ic->ic_channels[ic->ic_nchans++];
+ c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
+ c->ic_ieee = ieee;
+ c->ic_flags = flags;
+}
+
+/*
+ * Setup the channel list for the specified regulatory domain,
+ * country code, and operating modes. This interface is used
+ * when a driver does not obtain the channel list from another
+ * source (such as firmware).
+ */
+void
+ieee80211_init_channels(struct ieee80211com *ic,
+ int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
+{
+ int i;
+
+ /* XXX just do something for now */
+ ic->ic_nchans = 0;
+ if (isset(&bands, IEEE80211_MODE_11B) ||
+ isset(&bands, IEEE80211_MODE_11G)) {
+ for (i = 1; i <= (ecm ? 14 : 11); i++) {
+ if (isset(&bands, IEEE80211_MODE_11B))
+ addchan(ic, i, IEEE80211_CHAN_B);
+ if (isset(&bands, IEEE80211_MODE_11G))
+ addchan(ic, i, IEEE80211_CHAN_G);
+ }
+ }
+ if (isset(&bands, IEEE80211_MODE_11A)) {
+ for (i = 36; i <= 64; i += 4)
+ addchan(ic, i, IEEE80211_CHAN_A);
+ for (i = 100; i <= 140; i += 4)
+ addchan(ic, i, IEEE80211_CHAN_A);
+ for (i = 149; i <= 161; i += 4)
+ addchan(ic, i, IEEE80211_CHAN_A);
+ }
+ ic->ic_regdomain = rd;
+ ic->ic_countrycode = cc;
+ ic->ic_location = outdoor;
+}
+
+/*
+ * Add Country Information IE.
+ */
+uint8_t *
+ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
+ enum ISOCountryCode cc, int location)
+{
+#define CHAN_UNINTERESTING \
+ (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
+ IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
+ /* XXX what about auto? */
+ /* flag set of channels to be excluded */
+ static const int skipflags[IEEE80211_MODE_MAX] = {
+ CHAN_UNINTERESTING, /* MODE_AUTO */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11A */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11B */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11G */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_OFDM | /* MODE_FH */
+ IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN,
+ CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_TURBO_A */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_TURBO_G */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_STURBO_A */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */
+ CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */
+ };
+ struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
+ const char *iso_name;
+ uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
+ int i, skip;
+
+ ie->ie = IEEE80211_ELEMID_COUNTRY;
+ iso_name = ieee80211_cctoiso(cc);
+ if (iso_name == NULL) {
+ if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
+ iso_name = " ";
+ }
+ ie->cc[0] = iso_name[0];
+ ie->cc[1] = iso_name[1];
+ /*
+ * Indoor/Outdoor portion of country string.
+ * NB: this is not quite right, since we should have one of:
+ * 'I' indoor only
+ * 'O' outdoor only
+ * ' ' all enviroments
+ */
+ ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
+
+ /*
+ * Run-length encoded channel+max tx power info.
+ */
+ frm = (uint8_t *)&ie->band[0];
+ nextchan = 0; /* NB: impossible channel # */
+ memset(chans, 0, sizeof(chans));
+ skip = skipflags[ic->ic_curmode];
+ for (i = 0; i < ic->ic_nchans; i++) {
+ const struct ieee80211_channel *c = &ic->ic_channels[i];
+
+ if (isset(chans, c->ic_ieee)) /* suppress dup's */
+ continue;
+ if (c->ic_flags & skip) /* skip band, etc. */
+ continue;
+ setbit(chans, c->ic_ieee);
+ if (c->ic_ieee != nextchan ||
+ c->ic_maxregpower != frm[-1]) { /* new run */
+ /* XXX max of 83 runs */
+ frm[0] = c->ic_ieee; /* starting channel # */
+ frm[1] = 1; /* # channels in run */
+ frm[2] = c->ic_maxregpower; /* tx power cap */
+ frm += 3;
+ nextchan = c->ic_ieee + 1; /* overflow? */
+ } else { /* extend run */
+ frm[-2]++;
+ nextchan++;
+ }
+ }
+ ie->len = frm - ie->cc;
+ if (ie->len & 1) { /* Zero pad to multiple of 2 */
+ ie->len++;
+ *frm++ = 0;
+ }
+ return frm;
+#undef CHAN_UNINTERESTING
+}
+
+/*
+ * Country Code Table for code-to-string conversion.
+ */
+static const struct {
+ enum ISOCountryCode iso_code;
+ const char* iso_name;
+} country_strings[] = {
+ { CTRY_DEBUG, "DB" }, /* NB: nonstandard */
+ { CTRY_DEFAULT, "NA" }, /* NB: nonstandard */
+ { CTRY_ALBANIA, "AL" },
+ { CTRY_ALGERIA, "DZ" },
+ { CTRY_ARGENTINA, "AR" },
+ { CTRY_ARMENIA, "AM" },
+ { CTRY_AUSTRALIA, "AU" },
+ { CTRY_AUSTRIA, "AT" },
+ { CTRY_AZERBAIJAN, "AZ" },
+ { CTRY_BAHRAIN, "BH" },
+ { CTRY_BELARUS, "BY" },
+ { CTRY_BELGIUM, "BE" },
+ { CTRY_BELIZE, "BZ" },
+ { CTRY_BOLIVIA, "BO" },
+ { CTRY_BRAZIL, "BR" },
+ { CTRY_BRUNEI_DARUSSALAM, "BN" },
+ { CTRY_BULGARIA, "BG" },
+ { CTRY_CANADA, "CA" },
+ { CTRY_CHILE, "CL" },
+ { CTRY_CHINA, "CN" },
+ { CTRY_COLOMBIA, "CO" },
+ { CTRY_COSTA_RICA, "CR" },
+ { CTRY_CROATIA, "HR" },
+ { CTRY_CYPRUS, "CY" },
+ { CTRY_CZECH, "CZ" },
+ { CTRY_DENMARK, "DK" },
+ { CTRY_DOMINICAN_REPUBLIC, "DO" },
+ { CTRY_ECUADOR, "EC" },
+ { CTRY_EGYPT, "EG" },
+ { CTRY_EL_SALVADOR, "SV" },
+ { CTRY_ESTONIA, "EE" },
+ { CTRY_FINLAND, "FI" },
+ { CTRY_FRANCE, "FR" },
+ { CTRY_FRANCE2, "F2" },
+ { CTRY_GEORGIA, "GE" },
+ { CTRY_GERMANY, "DE" },
+ { CTRY_GREECE, "GR" },
+ { CTRY_GUATEMALA, "GT" },
+ { CTRY_HONDURAS, "HN" },
+ { CTRY_HONG_KONG, "HK" },
+ { CTRY_HUNGARY, "HU" },
+ { CTRY_ICELAND, "IS" },
+ { CTRY_INDIA, "IN" },
+ { CTRY_INDONESIA, "ID" },
+ { CTRY_IRAN, "IR" },
+ { CTRY_IRELAND, "IE" },
+ { CTRY_ISRAEL, "IL" },
+ { CTRY_ITALY, "IT" },
+ { CTRY_JAMAICA, "JM" },
+ { CTRY_JAPAN, "JP" },
+ { CTRY_JAPAN1, "J1" },
+ { CTRY_JAPAN2, "J2" },
+ { CTRY_JAPAN3, "J3" },
+ { CTRY_JAPAN4, "J4" },
+ { CTRY_JAPAN5, "J5" },
+ { CTRY_JORDAN, "JO" },
+ { CTRY_KAZAKHSTAN, "KZ" },
+ { CTRY_KOREA_NORTH, "KP" },
+ { CTRY_KOREA_ROC, "KR" },
+ { CTRY_KOREA_ROC2, "K2" },
+ { CTRY_KUWAIT, "KW" },
+ { CTRY_LATVIA, "LV" },
+ { CTRY_LEBANON, "LB" },
+ { CTRY_LIECHTENSTEIN, "LI" },
+ { CTRY_LITHUANIA, "LT" },
+ { CTRY_LUXEMBOURG, "LU" },
+ { CTRY_MACAU, "MO" },
+ { CTRY_MACEDONIA, "MK" },
+ { CTRY_MALAYSIA, "MY" },
+ { CTRY_MEXICO, "MX" },
+ { CTRY_MONACO, "MC" },
+ { CTRY_MOROCCO, "MA" },
+ { CTRY_NETHERLANDS, "NL" },
+ { CTRY_NEW_ZEALAND, "NZ" },
+ { CTRY_NORWAY, "NO" },
+ { CTRY_OMAN, "OM" },
+ { CTRY_PAKISTAN, "PK" },
+ { CTRY_PANAMA, "PA" },
+ { CTRY_PERU, "PE" },
+ { CTRY_PHILIPPINES, "PH" },
+ { CTRY_POLAND, "PL" },
+ { CTRY_PORTUGAL, "PT" },
+ { CTRY_PUERTO_RICO, "PR" },
+ { CTRY_QATAR, "QA" },
+ { CTRY_ROMANIA, "RO" },
+ { CTRY_RUSSIA, "RU" },
+ { CTRY_SAUDI_ARABIA, "SA" },
+ { CTRY_SINGAPORE, "SG" },
+ { CTRY_SLOVAKIA, "SK" },
+ { CTRY_SLOVENIA, "SI" },
+ { CTRY_SOUTH_AFRICA, "ZA" },
+ { CTRY_SPAIN, "ES" },
+ { CTRY_SWEDEN, "SE" },
+ { CTRY_SWITZERLAND, "CH" },
+ { CTRY_SYRIA, "SY" },
+ { CTRY_TAIWAN, "TW" },
+ { CTRY_THAILAND, "TH" },
+ { CTRY_TRINIDAD_Y_TOBAGO, "TT" },
+ { CTRY_TUNISIA, "TN" },
+ { CTRY_TURKEY, "TR" },
+ { CTRY_UKRAINE, "UA" },
+ { CTRY_UAE, "AE" },
+ { CTRY_UNITED_KINGDOM, "GB" },
+ { CTRY_UNITED_STATES, "US" },
+ { CTRY_URUGUAY, "UY" },
+ { CTRY_UZBEKISTAN, "UZ" },
+ { CTRY_VENEZUELA, "VE" },
+ { CTRY_VIET_NAM, "VN" },
+ { CTRY_YEMEN, "YE" },
+ { CTRY_ZIMBABWE, "ZW" }
+};
+
+const char *
+ieee80211_cctoiso(enum ISOCountryCode cc)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(country_strings); i++) {
+ if (country_strings[i].iso_code == cc)
+ return country_strings[i].iso_name;
+ }
+ return NULL;
+#undef N
+}
+
+int
+ieee80211_isotocc(const char iso[2])
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(country_strings); i++) {
+ if (country_strings[i].iso_name[0] == iso[0] &&
+ country_strings[i].iso_name[1] == iso[1])
+ return country_strings[i].iso_code;
+ }
+ return -1;
+#undef N
+}
--- /dev/null
+++ sys/net80211/ieee80211_ht.h
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * 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: src/sys/net80211/ieee80211_ht.h,v 1.2.2.1 2007/11/11 17:44:35 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_HT_H_
+#define _NET80211_IEEE80211_HT_H_
+
+/*
+ * 802.11n protocol implementation definitions.
+ */
+
+#define IEEE80211_AGGR_BAWMAX 64 /* max block ack window size */
+/* threshold for aging overlapping non-HT bss */
+#define IEEE80211_NONHT_PRESENT_AGE msecs_to_ticks(60*1000)
+
+typedef uint16_t ieee80211_seq;
+
+struct ieee80211_tx_ampdu {
+ u_short txa_flags;
+#define IEEE80211_AGGR_IMMEDIATE 0x0001 /* BA policy */
+#define IEEE80211_AGGR_XCHGPEND 0x0002 /* ADDBA response pending */
+#define IEEE80211_AGGR_RUNNING 0x0004 /* ADDBA response received */
+#define IEEE80211_AGGR_SETUP 0x0008 /* deferred state setup */
+#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
+ uint8_t txa_ac;
+ uint8_t txa_token; /* dialog token */
+ int txa_qbytes; /* data queued (bytes) */
+ short txa_qframes; /* data queued (frames) */
+ ieee80211_seq txa_seqstart;
+ ieee80211_seq txa_start;
+ uint16_t txa_wnd; /* BA window size */
+ uint8_t txa_attempts; /* # setup attempts */
+ int txa_lastrequest;/* time of last ADDBA request */
+ struct ifqueue txa_q; /* packet queue */
+ struct callout txa_timer;
+};
+
+/* return non-zero if AMPDU tx for the TID is running */
+#define IEEE80211_AMPDU_RUNNING(tap) \
+ (((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0)
+
+/* return non-zero if AMPDU tx for the TID is running or started */
+#define IEEE80211_AMPDU_REQUESTED(tap) \
+ (((tap)->txa_flags & \
+ (IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
+
+struct ieee80211_rx_ampdu {
+ int rxa_flags;
+ int rxa_qbytes; /* data queued (bytes) */
+ short rxa_qframes; /* data queued (frames) */
+ ieee80211_seq rxa_seqstart;
+ ieee80211_seq rxa_start; /* start of current BA window */
+ uint16_t rxa_wnd; /* BA window size */
+ int rxa_age; /* age of oldest frame in window */
+ int rxa_nframes; /* frames since ADDBA */
+ struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX];
+};
+
+void ieee80211_ht_attach(struct ieee80211com *);
+void ieee80211_ht_detach(struct ieee80211com *);
+
+void ieee80211_ht_announce(struct ieee80211com *);
+
+extern const int ieee80211_htrates[16];
+const struct ieee80211_htrateset *ieee80211_get_suphtrates(
+ struct ieee80211com *, const struct ieee80211_channel *);
+
+struct ieee80211_node;
+int ieee80211_setup_htrates(struct ieee80211_node *,
+ const uint8_t *htcap, int flags);
+void ieee80211_setup_basic_htrates(struct ieee80211_node *,
+ const uint8_t *htinfo);
+struct mbuf *ieee80211_decap_amsdu(struct ieee80211_node *, struct mbuf *);
+int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
+void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
+void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
+void ieee80211_ht_node_cleanup(struct ieee80211_node *);
+struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
+ struct ieee80211_channel *, int);
+void ieee80211_ht_wds_init(struct ieee80211_node *);
+void ieee80211_ht_node_join(struct ieee80211_node *);
+void ieee80211_ht_node_leave(struct ieee80211_node *);
+void ieee80211_htinfo_update(struct ieee80211com *, int protmode);
+void ieee80211_ht_timeout(struct ieee80211com *);
+void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
+void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
+void ieee80211_recv_action(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+int ieee80211_ampdu_request(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
+void ieee80211_ampdu_stop(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
+int ieee80211_send_bar(struct ieee80211_node *,
+ const struct ieee80211_tx_ampdu *);
+int ieee80211_send_action(struct ieee80211_node *,
+ int, int, uint16_t [4]);
+uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *);
+uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *);
+uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *);
+uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *);
+struct ieee80211_beacon_offsets;
+void ieee80211_ht_update_beacon(struct ieee80211com *,
+ struct ieee80211_beacon_offsets *);
+#endif /* _NET80211_IEEE80211_HT_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_amrr.h
@@ -0,0 +1,64 @@
+/* $FreeBSD: src/sys/net80211/ieee80211_amrr.h,v 1.1 2006/11/26 19:55:26 sam Exp $ */
+/* $OpenBSD: ieee80211_amrr.h,v 1.3 2006/06/17 19:34:31 damien Exp $ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini at free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _NET80211_IEEE80211_AMRR_H_
+#define _NET80211_IEEE80211_AMRR_H_
+
+/*-
+ * Naive implementation of the Adaptive Multi Rate Retry algorithm:
+ *
+ * "IEEE 802.11 Rate Adaptation: A Practical Approach"
+ * Mathieu Lacage, Hossein Manshaei, Thierry Turletti
+ * INRIA Sophia - Projet Planete
+ * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
+ */
+
+/*
+ * Rate control settings.
+ */
+struct ieee80211com;
+
+struct ieee80211_amrr {
+ u_int amrr_min_success_threshold;
+ u_int amrr_max_success_threshold;
+ struct ieee80211com *amrr_ic;
+};
+
+#define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD 1
+#define IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD 15
+
+/*
+ * Rate control state for a given node.
+ */
+struct ieee80211_amrr_node {
+ u_int amn_success;
+ u_int amn_recovery;
+ u_int amn_success_threshold;
+ u_int amn_txcnt;
+ u_int amn_retrycnt;
+};
+
+void ieee80211_amrr_init(struct ieee80211_amrr *,
+ struct ieee80211com *ic, int, int);
+void ieee80211_amrr_node_init(struct ieee80211_amrr *,
+ struct ieee80211_amrr_node *);
+void ieee80211_amrr_choose(struct ieee80211_amrr *, struct ieee80211_node *,
+ struct ieee80211_amrr_node *);
+
+#endif /* _NET80211_IEEE80211_AMRR_H_ */
Index: ieee80211_proto.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_proto.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_proto.h -L sys/net80211/ieee80211_proto.h -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_proto.h
+++ sys/net80211/ieee80211_proto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,7 +23,7 @@
* (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: src/sys/net80211/ieee80211_proto.h,v 1.11.2.5 2006/02/12 19:00:39 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.26.2.1 2007/11/11 17:44:36 sam Exp $
*/
#ifndef _NET80211_IEEE80211_PROTO_H_
#define _NET80211_IEEE80211_PROTO_H_
@@ -43,13 +37,26 @@
IEEE80211_S_SCAN = 1, /* scanning */
IEEE80211_S_AUTH = 2, /* try to authenticate */
IEEE80211_S_ASSOC = 3, /* try to assoc */
- IEEE80211_S_RUN = 4, /* associated */
+ IEEE80211_S_CAC = 4, /* doing channel availability check */
+ IEEE80211_S_RUN = 5, /* operational (e.g. associated) */
+ IEEE80211_S_CSA = 6, /* channel switch announce pending */
+ IEEE80211_S_SLEEP = 7, /* power save */
};
-#define IEEE80211_S_MAX (IEEE80211_S_RUN+1)
+#define IEEE80211_S_MAX (IEEE80211_S_SLEEP+1)
#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
+/*
+ * The formation of some management frames requires guidance to
+ * deal with legacy clients. When the client is identified as
+ * "legacy 11b" this parameter can be passed in the arg param of a
+ * IEEE80211_SEND_MGMT call.
+ */
+#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
+#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
+#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+
extern const char *ieee80211_mgt_subtype_name[];
extern const char *ieee80211_phymode_name[];
@@ -58,27 +65,36 @@
struct ieee80211_node;
int ieee80211_input(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, u_int32_t);
+ struct ieee80211_node *, int, int, uint32_t);
+void ieee80211_deliver_data(struct ieee80211com *,
+ struct ieee80211_node *, struct mbuf *);
+struct mbuf *ieee80211_decap1(struct mbuf *, int *);
int ieee80211_setup_rates(struct ieee80211_node *ni,
- const u_int8_t *rates, const u_int8_t *xrates, int flags);
-void ieee80211_saveie(u_int8_t **, const u_int8_t *);
+ const uint8_t *rates, const uint8_t *xrates, int flags);
+void ieee80211_saveie(uint8_t **, const uint8_t *);
+void ieee80211_saveath(struct ieee80211_node *, uint8_t *);
void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, u_int32_t);
+ struct ieee80211_node *, int, int, int, uint32_t);
+int ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *,
+ struct mbuf *, int type);
+struct ieee80211_bpf_params;
+int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+int ieee80211_output(struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *);
int ieee80211_send_nulldata(struct ieee80211_node *);
-int ieee80211_send_probereq(struct ieee80211_node *ni,
- const u_int8_t sa[IEEE80211_ADDR_LEN],
- const u_int8_t da[IEEE80211_ADDR_LEN],
- const u_int8_t bssid[IEEE80211_ADDR_LEN],
- const u_int8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen);
int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
int, int);
+int ieee80211_send_probereq(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t *ssid, size_t ssidlen,
+ const void *optie, size_t optielen);
int ieee80211_classify(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
-void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *,
- struct mbuf *);
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
@@ -102,12 +118,12 @@
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);
+ size += sizeof(uint16_t);
return size;
}
/*
- * Return the size of the 802.11 header; handles any type of frame.
+ * Like ieee80211_hdrsize, but handles any type of frame.
*/
static __inline int
ieee80211_anyhdrsize(const void *data)
@@ -119,6 +135,8 @@
case IEEE80211_FC0_SUBTYPE_CTS:
case IEEE80211_FC0_SUBTYPE_ACK:
return sizeof(struct ieee80211_frame_ack);
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ return sizeof(struct ieee80211_frame_bar);
}
return sizeof(struct ieee80211_frame_min);
} else
@@ -155,11 +173,11 @@
int (*iac_attach)(struct ieee80211com *);
void (*iac_detach)(struct ieee80211com *);
int (*iac_check)(struct ieee80211com *,
- const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_add)(struct ieee80211com *,
- const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_remove)(struct ieee80211com *,
- const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_flush)(struct ieee80211com *);
int (*iac_setpolicy)(struct ieee80211com *, int);
int (*iac_getpolicy)(struct ieee80211com *);
@@ -175,24 +193,27 @@
#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
-int ieee80211_fix_rate(struct ieee80211_node *, int);
+#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */
+#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */
+int ieee80211_fix_rate(struct ieee80211_node *,
+ struct ieee80211_rateset *, 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) */
+ uint8_t wmep_acm;
+ uint8_t wmep_aifsn;
+ uint8_t wmep_logcwmin; /* log2(cwmin) */
+ uint8_t wmep_logcwmax; /* log2(cwmax) */
+ uint8_t wmep_txopLimit;
+ uint8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */
};
#define IEEE80211_TXOP_TO_US(_txop) ((_txop)<<5)
#define IEEE80211_US_TO_TXOP(_us) ((_us)>>5)
struct chanAccParams {
- u_int8_t cap_info; /* version of the current set */
+ uint8_t cap_info; /* version of the current set */
struct wmeParams cap_wmeParams[WME_NUM_AC];
};
@@ -218,10 +239,14 @@
#define ieee80211_new_state(_ic, _nstate, _arg) \
(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
+int ieee80211_init(struct ieee80211com *, int forcescan);
+void ieee80211_dturbo_switch(struct ieee80211com *, int newflags);
void ieee80211_beacon_miss(struct ieee80211com *);
-void ieee80211_print_essid(const u_int8_t *, int);
-void ieee80211_dump_pkt(const u_int8_t *, int, int, int);
+void ieee80211_print_essid(const uint8_t *, int);
+void ieee80211_dump_pkt(struct ieee80211com *,
+ const uint8_t *, int, int, int);
+extern const char *ieee80211_opmode_name[];
extern const char *ieee80211_state_name[IEEE80211_S_MAX];
extern const char *ieee80211_wme_acnames[];
@@ -231,20 +256,48 @@
* 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 */
- u_int8_t *bo_erp; /* start of ERP element */
- void *bo_pad[8]; /* future expansion */
+ uint8_t bo_flags[4]; /* update/state flags */
+ uint16_t *bo_caps; /* capabilities */
+ uint8_t *bo_cfp; /* start of CFParms element */
+ uint8_t *bo_tim; /* start of atim/dtim */
+ uint8_t *bo_wme; /* start of WME parameters */
+ uint8_t *bo_tim_trailer;/* start of fixed-size trailer */
+ uint16_t bo_tim_len; /* atim/dtim length in bytes */
+ uint16_t bo_tim_trailer_len;/* tim trailer length in bytes */
+ uint8_t *bo_erp; /* start of ERP element */
+ uint8_t *bo_htinfo; /* start of HT info element */
+ uint8_t *bo_appie; /* start of AppIE element */
+ uint16_t bo_appie_len; /* AppIE length in bytes */
+ uint16_t bo_csa_trailer_len;;
+ uint8_t *bo_csa; /* start of CSA element */
+};
+struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
+ struct ieee80211_beacon_offsets *);
+
+/*
+ * Beacon frame updates are signaled through calls to ic_update_beacon
+ * with one of the IEEE80211_BEACON_* tokens defined below. For devices
+ * that construct beacon frames on the host this can trigger a rebuild
+ * or defer the processing. For devices that offload beacon frame
+ * handling this callback can be used to signal a rebuild. The bo_flags
+ * array in the ieee80211_beacon_offsets structure is intended to record
+ * deferred processing requirements; ieee80211_beacon_update uses the
+ * state to optimize work. Since this structure is owned by the driver
+ * and not visible to the 802.11 layer drivers must supply an ic_update_beacon
+ * callback that marks the flag bits and schedules (as necessary) an update.
+ */
+enum {
+ IEEE80211_BEACON_CAPS = 0, /* capabilities */
+ IEEE80211_BEACON_TIM = 1, /* DTIM/ATIM */
+ IEEE80211_BEACON_WME = 2,
+ IEEE80211_BEACON_ERP = 3, /* Extended Rate Phy */
+ IEEE80211_BEACON_HTINFO = 4, /* HT Information */
+ IEEE80211_BEACON_APPIE = 5, /* Application IE's */
+ IEEE80211_BEACON_CFP = 6, /* CFParms */
+ IEEE80211_BEACON_CSA = 7, /* Channel Switch Announcement */
};
-struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *,
- struct ieee80211_node *, struct ieee80211_beacon_offsets *);
-int ieee80211_beacon_update(struct ieee80211com *,
- struct ieee80211_node *, struct ieee80211_beacon_offsets *,
- struct mbuf *, int broadcast);
+int ieee80211_beacon_update(struct ieee80211_node *,
+ struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
/*
* Notification methods called from the 802.11 state machine.
--- /dev/null
+++ sys/net80211/ieee80211_regdomain.h
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2005-2007 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.
+ *
+ * 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: src/sys/net80211/ieee80211_regdomain.h,v 1.1 2007/06/11 03:36:55 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_REGDOMAIN_H_
+#define _NET80211_IEEE80211_REGDOMAIN_H_
+
+/*
+ * 802.11 regulatory domain definitions.
+ */
+
+/*
+ * ISO 3166 Country/Region Codes
+ * http://ftp.ics.uci.edu/pub/ietf/http/related/iso3166.txt
+ */
+enum ISOCountryCode {
+ CTRY_AFGHANISTAN = 4,
+ CTRY_ALBANIA = 8, /* Albania */
+ CTRY_ALGERIA = 12, /* Algeria */
+ CTRY_AMERICAN_SAMOA = 16,
+ CTRY_ANDORRA = 20,
+ CTRY_ANGOLA = 24,
+ CTRY_ANGUILLA = 660,
+ /* XXX correct remainder */
+ CTRY_ARGENTINA = 32, /* Argentina */
+ CTRY_ARMENIA = 51, /* Armenia */
+ CTRY_AUSTRALIA = 36, /* Australia */
+ CTRY_AUSTRIA = 40, /* Austria */
+ CTRY_AZERBAIJAN = 31, /* Azerbaijan */
+ CTRY_BAHRAIN = 48, /* Bahrain */
+ CTRY_BELARUS = 112, /* Belarus */
+ CTRY_BELGIUM = 56, /* Belgium */
+ CTRY_BELIZE = 84, /* Belize */
+ CTRY_BOLIVIA = 68, /* Bolivia */
+ CTRY_BRAZIL = 76, /* Brazil */
+ CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */
+ CTRY_BULGARIA = 100, /* Bulgaria */
+ CTRY_CANADA = 124, /* Canada */
+ CTRY_CHILE = 152, /* Chile */
+ CTRY_CHINA = 156, /* People's Republic of China */
+ CTRY_COLOMBIA = 170, /* Colombia */
+ CTRY_COSTA_RICA = 188, /* Costa Rica */
+ CTRY_CROATIA = 191, /* Croatia */
+ CTRY_CYPRUS = 196, /* Cyprus */
+ CTRY_CZECH = 203, /* Czech Republic */
+ CTRY_DENMARK = 208, /* Denmark */
+ CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */
+ CTRY_ECUADOR = 218, /* Ecuador */
+ CTRY_EGYPT = 818, /* Egypt */
+ CTRY_EL_SALVADOR = 222, /* El Salvador */
+ CTRY_ESTONIA = 233, /* Estonia */
+ CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */
+ CTRY_FINLAND = 246, /* Finland */
+ CTRY_FRANCE = 250, /* France */
+ CTRY_FRANCE2 = 255, /* France2 */
+ CTRY_GEORGIA = 268, /* Georgia */
+ CTRY_GERMANY = 276, /* Germany */
+ CTRY_GREECE = 300, /* Greece */
+ CTRY_GUATEMALA = 320, /* Guatemala */
+ CTRY_HONDURAS = 340, /* Honduras */
+ CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */
+ CTRY_HUNGARY = 348, /* Hungary */
+ CTRY_ICELAND = 352, /* Iceland */
+ CTRY_INDIA = 356, /* India */
+ CTRY_INDONESIA = 360, /* Indonesia */
+ CTRY_IRAN = 364, /* Iran */
+ CTRY_IRAQ = 368, /* Iraq */
+ CTRY_IRELAND = 372, /* Ireland */
+ CTRY_ISRAEL = 376, /* Israel */
+ CTRY_ITALY = 380, /* Italy */
+ CTRY_JAMAICA = 388, /* Jamaica */
+ CTRY_JAPAN = 392, /* Japan */
+ CTRY_JAPAN1 = 393, /* Japan (JP1) */
+ CTRY_JAPAN2 = 394, /* Japan (JP0) */
+ CTRY_JAPAN3 = 395, /* Japan (JP1-1) */
+ CTRY_JAPAN4 = 396, /* Japan (JE1) */
+ CTRY_JAPAN5 = 397, /* Japan (JE2) */
+ CTRY_JORDAN = 400, /* Jordan */
+ CTRY_KAZAKHSTAN = 398, /* Kazakhstan */
+ CTRY_KENYA = 404, /* Kenya */
+ CTRY_KOREA_NORTH = 408, /* North Korea */
+ CTRY_KOREA_ROC = 410, /* South Korea */
+ CTRY_KOREA_ROC2 = 411, /* South Korea */
+ CTRY_KUWAIT = 414, /* Kuwait */
+ CTRY_LATVIA = 428, /* Latvia */
+ CTRY_LEBANON = 422, /* Lebanon */
+ CTRY_LIBYA = 434, /* Libya */
+ CTRY_LIECHTENSTEIN = 438, /* Liechtenstein */
+ CTRY_LITHUANIA = 440, /* Lithuania */
+ CTRY_LUXEMBOURG = 442, /* Luxembourg */
+ CTRY_MACAU = 446, /* Macau */
+ CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */
+ CTRY_MALAYSIA = 458, /* Malaysia */
+ CTRY_MEXICO = 484, /* Mexico */
+ CTRY_MONACO = 492, /* Principality of Monaco */
+ CTRY_MOROCCO = 504, /* Morocco */
+ CTRY_NETHERLANDS = 528, /* Netherlands */
+ CTRY_NEW_ZEALAND = 554, /* New Zealand */
+ CTRY_NICARAGUA = 558, /* Nicaragua */
+ CTRY_NORWAY = 578, /* Norway */
+ CTRY_OMAN = 512, /* Oman */
+ CTRY_PAKISTAN = 586, /* Islamic Republic of Pakistan */
+ CTRY_PANAMA = 591, /* Panama */
+ CTRY_PARAGUAY = 600, /* Paraguay */
+ CTRY_PERU = 604, /* Peru */
+ CTRY_PHILIPPINES = 608, /* Republic of the Philippines */
+ CTRY_POLAND = 616, /* Poland */
+ CTRY_PORTUGAL = 620, /* Portugal */
+ CTRY_PUERTO_RICO = 630, /* Puerto Rico */
+ CTRY_QATAR = 634, /* Qatar */
+ CTRY_ROMANIA = 642, /* Romania */
+ CTRY_RUSSIA = 643, /* Russia */
+ CTRY_SAUDI_ARABIA = 682, /* Saudi Arabia */
+ CTRY_SINGAPORE = 702, /* Singapore */
+ CTRY_SLOVAKIA = 703, /* Slovak Republic */
+ CTRY_SLOVENIA = 705, /* Slovenia */
+ CTRY_SOUTH_AFRICA = 710, /* South Africa */
+ CTRY_SPAIN = 724, /* Spain */
+ CTRY_SWEDEN = 752, /* Sweden */
+ CTRY_SWITZERLAND = 756, /* Switzerland */
+ CTRY_SYRIA = 760, /* Syria */
+ CTRY_TAIWAN = 158, /* Taiwan */
+ CTRY_THAILAND = 764, /* Thailand */
+ CTRY_TRINIDAD_Y_TOBAGO = 780, /* Trinidad y Tobago */
+ CTRY_TUNISIA = 788, /* Tunisia */
+ CTRY_TURKEY = 792, /* Turkey */
+ CTRY_UAE = 784, /* U.A.E. */
+ CTRY_UKRAINE = 804, /* Ukraine */
+ CTRY_UNITED_KINGDOM = 826, /* United Kingdom */
+ CTRY_UNITED_STATES = 840, /* United States */
+ CTRY_URUGUAY = 858, /* Uruguay */
+ CTRY_UZBEKISTAN = 860, /* Uzbekistan */
+ CTRY_VENEZUELA = 862, /* Venezuela */
+ CTRY_VIET_NAM = 704, /* Viet Nam */
+ CTRY_YEMEN = 887, /* Yemen */
+ CTRY_ZIMBABWE = 716, /* Zimbabwe */
+};
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+#define CTRY_DEBUG 0x1ff /* debug */
+#define CTRY_DEFAULT 0 /* default */
+
+void ieee80211_regdomain_attach(struct ieee80211com *);
+void ieee80211_regdomain_detach(struct ieee80211com *);
+
+void ieee80211_init_channels(struct ieee80211com *ic,
+ int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm);
+uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *,
+ enum ISOCountryCode cc, int location);
+const char *ieee80211_cctoiso(enum ISOCountryCode);
+int ieee80211_isotocc(const char iso[2]);
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+#endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */
Index: ieee80211_crypto_ccmp.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto_ccmp.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto_ccmp.c -L sys/net80211/ieee80211_crypto_ccmp.c -u -r1.1.1.2 -r1.2
--- sys/net80211/ieee80211_crypto_ccmp.c
+++ sys/net80211/ieee80211_crypto_ccmp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,12 +10,6 @@
* 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
@@ -30,7 +24,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.10 2007/06/11 03:36:54 sam Exp $");
/*
* IEEE 802.11i AES-CCMP crypto support.
@@ -66,7 +60,7 @@
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_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid);
static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);
static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);
static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -140,11 +134,11 @@
* Add privacy headers appropriate for the specified key.
*/
static int
-ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct ccmp_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->cc_ic;
- u_int8_t *ivp;
+ uint8_t *ivp;
int hdrlen;
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -155,7 +149,7 @@
M_PREPEND(m, ccmp.ic_header, M_NOWAIT);
if (m == NULL)
return 0;
- ivp = mtod(m, u_int8_t *);
+ ivp = mtod(m, uint8_t *);
ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen);
ivp += hdrlen;
@@ -250,7 +244,7 @@
/*
* Copy up 802.11 header and strip crypto bits.
*/
- ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + ccmp.ic_header, hdrlen);
+ ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen);
m_adj(m, ccmp.ic_header);
m_adj(m, -ccmp.ic_trailer);
@@ -356,7 +350,7 @@
b0[1] = aad[30];
aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
} else {
- *(u_int16_t *)&aad[30] = 0;
+ *(uint16_t *)&aad[30] = 0;
b0[1] = 0;
aad[1] = 22 + IEEE80211_ADDR_LEN;
}
@@ -369,12 +363,12 @@
b0[1] = aad[24];
aad[1] = 22 + 2;
} else {
- *(u_int16_t *)&aad[24] = 0;
+ *(uint16_t *)&aad[24] = 0;
b0[1] = 0;
aad[1] = 22;
}
- *(u_int16_t *)&aad[26] = 0;
- *(u_int32_t *)&aad[28] = 0;
+ *(uint16_t *)&aad[26] = 0;
+ *(uint32_t *)&aad[28] = 0;
}
/* Start with the first block and AAD */
@@ -635,32 +629,4 @@
/*
* 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:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_ccmp: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
- }
- if (type == 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_ccmp, wlan, 1, 1, 1);
+IEEE80211_CRYPTO_MODULE(ccmp, 1);
--- /dev/null
+++ sys/net80211/ieee80211_scan_sta.c
@@ -0,0 +1,1479 @@
+/*-
+ * Copyright (c) 2002-2007 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.
+ *
+ * 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>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_scan_sta.c,v 1.4.2.3 2007/11/29 19:05:09 sam Exp $");
+
+/*
+ * IEEE 802.11 station scanning support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+/*
+ * Parameters for managing cache entries:
+ *
+ * o a station with STA_FAILS_MAX failures is not considered
+ * when picking a candidate
+ * o a station that hasn't had an update in STA_PURGE_SCANS
+ * (background) scans is discarded
+ * o after STA_FAILS_AGE seconds we clear the failure count
+ */
+#define STA_FAILS_MAX 2 /* assoc failures before ignored */
+#define STA_FAILS_AGE (2*60) /* time before clearing fails (secs) */
+#define STA_PURGE_SCANS 2 /* age for purging entries (scans) */
+
+/* XXX tunable */
+#define STA_RSSI_MIN 8 /* min acceptable rssi */
+#define STA_RSSI_MAX 40 /* max rssi for comparison */
+
+#define RSSI_LPF_LEN 10
+#define RSSI_DUMMY_MARKER 0x127
+#define RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */
+#define RSSI_IN(x) ((x) * RSSI_EP_MULTIPLIER)
+#define LPF_RSSI(x, y, len) \
+ ((x != RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define RSSI_LPF(x, y) do { \
+ if ((y) >= -20) \
+ x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN); \
+} while (0)
+#define EP_RND(x, mul) \
+ ((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul))
+#define RSSI_GET(x) EP_RND(x, RSSI_EP_MULTIPLIER)
+
+struct sta_entry {
+ struct ieee80211_scan_entry base;
+ TAILQ_ENTRY(sta_entry) se_list;
+ LIST_ENTRY(sta_entry) se_hash;
+ uint8_t se_fails; /* failure to associate count */
+ uint8_t se_seen; /* seen during current scan */
+ uint8_t se_notseen; /* not seen in previous scans */
+ uint8_t se_flags;
+ uint32_t se_avgrssi; /* LPF rssi state */
+ unsigned long se_lastupdate; /* time of last update */
+ unsigned long se_lastfail; /* time of last failure */
+ unsigned long se_lastassoc; /* time of last association */
+ u_int se_scangen; /* iterator scan gen# */
+};
+
+#define STA_HASHSIZE 32
+/* simple hash is enough for variation of macaddr */
+#define STA_HASH(addr) \
+ (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % STA_HASHSIZE)
+
+struct sta_table {
+ struct mtx st_lock; /* on scan table */
+ TAILQ_HEAD(, sta_entry) st_entry; /* all entries */
+ LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE];
+ struct mtx st_scanlock; /* on st_scangen */
+ u_int st_scangen; /* gen# for iterator */
+ int st_newscan;
+};
+
+static void sta_flush_table(struct sta_table *);
+/*
+ * match_bss returns a bitmask describing if an entry is suitable
+ * for use. If non-zero the entry was deemed not suitable and it's
+ * contents explains why. The following flags are or'd to to this
+ * mask and can be used to figure out why the entry was rejected.
+ */
+#define MATCH_CHANNEL 0x001 /* channel mismatch */
+#define MATCH_CAPINFO 0x002 /* capabilities mismatch, e.g. no ess */
+#define MATCH_PRIVACY 0x004 /* privacy mismatch */
+#define MATCH_RATE 0x008 /* rate set mismatch */
+#define MATCH_SSID 0x010 /* ssid mismatch */
+#define MATCH_BSSID 0x020 /* bssid mismatch */
+#define MATCH_FAILS 0x040 /* too many failed auth attempts */
+#define MATCH_NOTSEEN 0x080 /* not seen in recent scans */
+#define MATCH_RSSI 0x100 /* rssi deemed too low to use */
+static int match_bss(struct ieee80211com *,
+ const struct ieee80211_scan_state *, struct sta_entry *, int);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+/*
+ * Attach prior to any scanning work.
+ */
+static int
+sta_attach(struct ieee80211_scan_state *ss)
+{
+ struct sta_table *st;
+
+ MALLOC(st, struct sta_table *, sizeof(struct sta_table),
+ M_80211_SCAN, M_NOWAIT | M_ZERO);
+ if (st == NULL)
+ return 0;
+ mtx_init(&st->st_lock, "scantable", "802.11 scan table", MTX_DEF);
+ mtx_init(&st->st_scanlock, "scangen", "802.11 scangen", MTX_DEF);
+ TAILQ_INIT(&st->st_entry);
+ ss->ss_priv = st;
+ nrefs++; /* NB: we assume caller locking */
+ return 1;
+}
+
+/*
+ * Cleanup any private state.
+ */
+static int
+sta_detach(struct ieee80211_scan_state *ss)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ if (st != NULL) {
+ sta_flush_table(st);
+ mtx_destroy(&st->st_lock);
+ mtx_destroy(&st->st_scanlock);
+ FREE(st, M_80211_SCAN);
+ KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+ nrefs--; /* NB: we assume caller locking */
+ }
+ return 1;
+}
+
+/*
+ * Flush all per-scan state.
+ */
+static int
+sta_flush(struct ieee80211_scan_state *ss)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ mtx_lock(&st->st_lock);
+ sta_flush_table(st);
+ mtx_unlock(&st->st_lock);
+ ss->ss_last = 0;
+ return 0;
+}
+
+/*
+ * Flush all entries in the scan cache.
+ */
+static void
+sta_flush_table(struct sta_table *st)
+{
+ struct sta_entry *se, *next;
+
+ TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
+ TAILQ_REMOVE(&st->st_entry, se, se_list);
+ LIST_REMOVE(se, se_hash);
+ FREE(se, M_80211_SCAN);
+ }
+}
+
+static void
+saveie(uint8_t **iep, const uint8_t *ie)
+{
+
+ if (ie == NULL)
+ *iep = NULL;
+ else
+ ieee80211_saveie(iep, ie);
+}
+
+/*
+ * Process a beacon or probe response frame; create an
+ * entry in the scan cache or update any previous entry.
+ */
+static int
+sta_add(struct ieee80211_scan_state *ss,
+ const struct ieee80211_scanparams *sp,
+ const struct ieee80211_frame *wh,
+ int subtype, int rssi, int noise, int rstamp)
+{
+#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define PICK1ST(_ss) \
+ ((ss->ss_flags & (IEEE80211_SCAN_PICK1ST | IEEE80211_SCAN_GOTPICK)) == \
+ IEEE80211_SCAN_PICK1ST)
+ struct sta_table *st = ss->ss_priv;
+ const uint8_t *macaddr = wh->i_addr2;
+ struct ieee80211com *ic = ss->ss_ic;
+ struct sta_entry *se;
+ struct ieee80211_scan_entry *ise;
+ int hash, offchan;
+
+ hash = STA_HASH(macaddr);
+
+ mtx_lock(&st->st_lock);
+ LIST_FOREACH(se, &st->st_hash[hash], se_hash)
+ if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
+ goto found;
+ MALLOC(se, struct sta_entry *, sizeof(struct sta_entry),
+ M_80211_SCAN, M_NOWAIT | M_ZERO);
+ if (se == NULL) {
+ mtx_unlock(&st->st_lock);
+ return 0;
+ }
+ se->se_scangen = st->st_scangen-1;
+ se->se_avgrssi = RSSI_DUMMY_MARKER;
+ IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr);
+ TAILQ_INSERT_TAIL(&st->st_entry, se, se_list);
+ LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash);
+found:
+ ise = &se->base;
+ /* XXX ap beaconing multiple ssid w/ same bssid */
+ if (sp->ssid[1] != 0 &&
+ (ISPROBE(subtype) || ise->se_ssid[1] == 0))
+ memcpy(ise->se_ssid, sp->ssid, 2+sp->ssid[1]);
+ KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+ ("rate set too large: %u", sp->rates[1]));
+ memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]);
+ if (sp->xrates != NULL) {
+ /* XXX validate xrates[1] */
+ KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+ ("xrate set too large: %u", sp->xrates[1]));
+ memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]);
+ } else
+ ise->se_xrates[1] = 0;
+ IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
+ offchan = (IEEE80211_CHAN2IEEE(sp->curchan) != sp->bchan &&
+ ic->ic_phytype != IEEE80211_T_FH);
+ if (!offchan) {
+ /*
+ * Record rssi data using extended precision LPF filter.
+ *
+ * NB: use only on-channel data to insure we get a good
+ * estimate of the signal we'll see when associated.
+ */
+ RSSI_LPF(se->se_avgrssi, rssi);
+ ise->se_rssi = RSSI_GET(se->se_avgrssi);
+ ise->se_noise = noise;
+ }
+ ise->se_rstamp = rstamp;
+ memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp));
+ ise->se_intval = sp->bintval;
+ ise->se_capinfo = sp->capinfo;
+ /*
+ * Beware of overriding se_chan for frames seen
+ * off-channel; this can cause us to attempt an
+ * assocation on the wrong channel.
+ */
+ if (offchan) {
+ struct ieee80211_channel *c;
+ /*
+ * Off-channel, locate the home/bss channel for the sta
+ * using the value broadcast in the DSPARMS ie.
+ */
+ c = ieee80211_find_channel_byieee(ic, sp->bchan,
+ sp->curchan->ic_flags);
+ if (c != NULL) {
+ ise->se_chan = c;
+ } else if (ise->se_chan == NULL) {
+ /* should not happen, pick something */
+ ise->se_chan = sp->curchan;
+ }
+ } else
+ ise->se_chan = sp->curchan;
+ ise->se_fhdwell = sp->fhdwell;
+ ise->se_fhindex = sp->fhindex;
+ ise->se_erp = sp->erp;
+ ise->se_timoff = sp->timoff;
+ if (sp->tim != NULL) {
+ const struct ieee80211_tim_ie *tim =
+ (const struct ieee80211_tim_ie *) sp->tim;
+ ise->se_dtimperiod = tim->tim_period;
+ }
+ saveie(&ise->se_wme_ie, sp->wme);
+ saveie(&ise->se_wpa_ie, sp->wpa);
+ saveie(&ise->se_rsn_ie, sp->rsn);
+ saveie(&ise->se_ath_ie, sp->ath);
+ saveie(&ise->se_htcap_ie, sp->htcap);
+ saveie(&ise->se_htinfo_ie, sp->htinfo);
+
+ /* clear failure count after STA_FAIL_AGE passes */
+ if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) {
+ se->se_fails = 0;
+ IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr,
+ "%s: fails %u", __func__, se->se_fails);
+ }
+
+ se->se_lastupdate = ticks; /* update time */
+ se->se_seen = 1;
+ se->se_notseen = 0;
+
+ mtx_unlock(&st->st_lock);
+
+ /*
+ * If looking for a quick choice and nothing's
+ * been found check here.
+ */
+ if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0)
+ ss->ss_flags |= IEEE80211_SCAN_GOTPICK;
+
+ return 1;
+#undef PICK1ST
+#undef ISPROBE
+}
+
+/*
+ * Check if a channel is excluded by user request.
+ */
+static int
+isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c)
+{
+ return (isclr(ic->ic_chan_active, c->ic_ieee) ||
+ (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+ c->ic_freq != ic->ic_des_chan->ic_freq));
+}
+
+static struct ieee80211_channel *
+find11gchannel(struct ieee80211com *ic, int i, int freq)
+{
+ struct ieee80211_channel *c;
+ int j;
+
+ /*
+ * The normal ordering in the channel list is b channel
+ * immediately followed by g so optimize the search for
+ * this. We'll still do a full search just in case.
+ */
+ for (j = i+1; j < ic->ic_nchans; j++) {
+ c = &ic->ic_channels[j];
+ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+ return c;
+ }
+ for (j = 0; j < i; j++) {
+ c = &ic->ic_channels[j];
+ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+ return c;
+ }
+ return NULL;
+}
+static const u_int chanflags[IEEE80211_MODE_MAX] = {
+ IEEE80211_CHAN_B, /* IEEE80211_MODE_AUTO */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
+ IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */
+ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_TURBO_A (check base channel)*/
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_TURBO_G */
+ IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA (check legacy) */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG (check legacy) */
+};
+
+static void
+add_channels(struct ieee80211com *ic,
+ struct ieee80211_scan_state *ss,
+ enum ieee80211_phymode mode, const uint16_t freq[], int nfreq)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ struct ieee80211_channel *c, *cg;
+ u_int modeflags;
+ int i;
+
+ KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
+ modeflags = chanflags[mode];
+ for (i = 0; i < nfreq; i++) {
+ if (ss->ss_last >= IEEE80211_SCAN_MAX)
+ break;
+
+ c = ieee80211_find_channel(ic, freq[i], modeflags);
+ if (c != NULL && isexcluded(ic, c))
+ continue;
+ if (mode == IEEE80211_MODE_AUTO) {
+ /*
+ * XXX special-case 11b/g channels so we select
+ * the g channel if both are present or there
+ * are only g channels.
+ */
+ if (c == NULL || IEEE80211_IS_CHAN_B(c)) {
+ cg = find11gchannel(ic, i, freq[i]);
+ if (cg != NULL)
+ c = cg;
+ }
+ }
+ if (c == NULL)
+ continue;
+
+ ss->ss_chans[ss->ss_last++] = c;
+ }
+#undef N
+}
+
+static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
+{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
+{ 2412, 2437, 2462, 2442, 2472 };
+static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
+{ 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
+{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
+{ 2484 };
+static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
+{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
+{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+#ifdef ATH_TURBO_SCAN
+static const uint16_t rcl5[] = /* 3 static turbo channels */
+{ 5210, 5250, 5290 };
+static const uint16_t rcl6[] = /* 2 static turbo channels */
+{ 5760, 5800 };
+static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
+{ 5540, 5580, 5620, 5660 };
+static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
+{ 2437 };
+static const uint16_t rcl13[] = /* dynamic Turbo channels */
+{ 5200, 5240, 5280, 5765, 5805 };
+#endif /* ATH_TURBO_SCAN */
+
+struct scanlist {
+ uint16_t mode;
+ uint16_t count;
+ const uint16_t *list;
+};
+
+#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
+
+static const struct scanlist staScanTable[] = {
+ { IEEE80211_MODE_11B, X(rcl3) },
+ { IEEE80211_MODE_11A, X(rcl1) },
+ { IEEE80211_MODE_11A, X(rcl2) },
+ { IEEE80211_MODE_11B, X(rcl8) },
+ { IEEE80211_MODE_11B, X(rcl9) },
+ { IEEE80211_MODE_11A, X(rcl4) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_STURBO_A, X(rcl5) },
+ { IEEE80211_MODE_STURBO_A, X(rcl6) },
+ { IEEE80211_MODE_TURBO_A, X(rcl6x) },
+ { IEEE80211_MODE_TURBO_A, X(rcl13) },
+#endif /* ATH_TURBO_SCAN */
+ { IEEE80211_MODE_11A, X(rcl7) },
+ { IEEE80211_MODE_11B, X(rcl10) },
+ { IEEE80211_MODE_11A, X(rcl11) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_TURBO_G, X(rcl12) },
+#endif /* ATH_TURBO_SCAN */
+ { .list = NULL }
+};
+
+static int
+checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
+{
+ int i;
+
+ for (; scan->list != NULL; scan++) {
+ for (i = 0; i < scan->count; i++)
+ if (scan->list[i] == c->ic_freq)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Start a station-mode scan by populating the channel list.
+ */
+static int
+sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ struct sta_table *st = ss->ss_priv;
+ const struct scanlist *scan;
+ enum ieee80211_phymode mode;
+ struct ieee80211_channel *c;
+ int i;
+
+ ss->ss_last = 0;
+ /*
+ * Use the table of ordered channels to construct the list
+ * of channels for scanning. Any channels in the ordered
+ * list not in the master list will be discarded.
+ */
+ for (scan = staScanTable; scan->list != NULL; scan++) {
+ mode = scan->mode;
+ if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+ /*
+ * If a desired mode was specified, scan only
+ * channels that satisfy that constraint.
+ */
+ if (ic->ic_des_mode != mode) {
+ /*
+ * The scan table marks 2.4Ghz channels as b
+ * so if the desired mode is 11g, then use
+ * the 11b channel list but upgrade the mode.
+ */
+ if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+ mode != IEEE80211_MODE_11B)
+ continue;
+ mode = IEEE80211_MODE_11G; /* upgrade */
+ }
+ } else {
+ /*
+ * This lets add_channels upgrade an 11b channel
+ * to 11g if available.
+ */
+ if (mode == IEEE80211_MODE_11B)
+ mode = IEEE80211_MODE_AUTO;
+ }
+#ifdef IEEE80211_F_XR
+ /* XR does not operate on turbo channels */
+ if ((ic->ic_flags & IEEE80211_F_XR) &&
+ (mode == IEEE80211_MODE_TURBO_A ||
+ mode == IEEE80211_MODE_TURBO_G ||
+ mode == IEEE80211_MODE_STURBO_A))
+ continue;
+#endif
+ /*
+ * Add the list of the channels; any that are not
+ * in the master channel list will be discarded.
+ */
+ add_channels(ic, ss, mode, scan->list, scan->count);
+ }
+
+ /*
+ * Add the channels from the ic (from HAL) that are not present
+ * in the staScanTable.
+ */
+ for (i = 0; i < ic->ic_nchans; i++) {
+ if (ss->ss_last >= IEEE80211_SCAN_MAX)
+ break;
+
+ c = &ic->ic_channels[i];
+ /*
+ * Ignore dynamic turbo channels; we scan them
+ * in normal mode (i.e. not boosted). Likewise
+ * for HT channels, they get scanned using
+ * legacy rates.
+ */
+ if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c))
+ continue;
+
+ /*
+ * If a desired mode was specified, scan only
+ * channels that satisfy that constraint.
+ */
+ if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+ ic->ic_des_mode != ieee80211_chan2mode(c))
+ continue;
+
+ /*
+ * Skip channels excluded by user request.
+ */
+ if (isexcluded(ic, c))
+ continue;
+
+ /*
+ * Add the channel unless it is listed in the
+ * fixed scan order tables. This insures we
+ * don't sweep back in channels we filtered out
+ * above.
+ */
+ if (checktable(staScanTable, c))
+ continue;
+
+ /* Add channel to scanning list. */
+ ss->ss_chans[ss->ss_last++] = c;
+ }
+
+ ss->ss_next = 0;
+ /* XXX tunables */
+ ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic)) {
+ if_printf(ic->ic_ifp, "scan set ");
+ ieee80211_scan_dump_channels(ss);
+ printf(" dwell min %ld max %ld\n",
+ ss->ss_mindwell, ss->ss_maxdwell);
+ }
+#endif /* IEEE80211_DEBUG */
+
+ st->st_newscan = 1;
+
+ return 0;
+#undef N
+}
+
+/*
+ * Restart a bg scan.
+ */
+static int
+sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ st->st_newscan = 1;
+ return 0;
+}
+
+/*
+ * Cancel an ongoing scan.
+ */
+static int
+sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ return 0;
+}
+
+static uint8_t
+maxrate(const struct ieee80211_scan_entry *se)
+{
+ uint8_t rmax, r;
+ int i;
+
+ rmax = 0;
+ for (i = 0; i < se->se_rates[1]; i++) {
+ r = se->se_rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > rmax)
+ rmax = r;
+ }
+ for (i = 0; i < se->se_xrates[1]; i++) {
+ r = se->se_xrates[2+i] & IEEE80211_RATE_VAL;
+ if (r > rmax)
+ rmax = r;
+ }
+ return rmax;
+}
+
+/*
+ * Compare the capabilities of two entries 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
+sta_compare(const struct sta_entry *a, const struct sta_entry *b)
+{
+#define PREFER(_a,_b,_what) do { \
+ if (((_a) ^ (_b)) & (_what)) \
+ return ((_a) & (_what)) ? 1 : -1; \
+} while (0)
+ uint8_t maxa, maxb;
+ int8_t rssia, rssib;
+ int weight;
+
+ /* privacy support */
+ PREFER(a->base.se_capinfo, b->base.se_capinfo,
+ IEEE80211_CAPINFO_PRIVACY);
+
+ /* compare count of previous failures */
+ weight = b->se_fails - a->se_fails;
+ if (abs(weight) > 1)
+ return weight;
+
+ /*
+ * Compare rssi. If the two are considered equivalent
+ * then fallback to other criteria. We threshold the
+ * comparisons to avoid selecting an ap purely by rssi
+ * when both values may be good but one ap is otherwise
+ * more desirable (e.g. an 11b-only ap with stronger
+ * signal than an 11g ap).
+ */
+ rssia = MIN(a->base.se_rssi, STA_RSSI_MAX);
+ rssib = MIN(b->base.se_rssi, STA_RSSI_MAX);
+ if (abs(rssib - rssia) < 5) {
+ /* best/max rate preferred if signal level close enough XXX */
+ maxa = maxrate(&a->base);
+ maxb = maxrate(&b->base);
+ if (maxa != maxb)
+ 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->base.se_chan) &&
+ !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
+ return 1;
+ if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
+ IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
+ return -1;
+ }
+ /* all things being equal, use signal level */
+ return a->base.se_rssi - b->base.se_rssi;
+#undef PREFER
+}
+
+/*
+ * Check rate set suitability and return the best supported rate.
+ */
+static int
+check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se)
+{
+#define RV(v) ((v) & IEEE80211_RATE_VAL)
+ const struct ieee80211_rateset *srs;
+ int i, j, nrs, r, okrate, badrate, fixedrate;
+ const uint8_t *rs;
+
+ okrate = badrate = fixedrate = 0;
+
+ srs = ieee80211_get_suprates(ic, se->se_chan);
+ nrs = se->se_rates[1];
+ rs = se->se_rates+2;
+ fixedrate = IEEE80211_FIXED_RATE_NONE;
+again:
+ for (i = 0; i < nrs; i++) {
+ r = RV(rs[i]);
+ badrate = r;
+ /*
+ * Check any fixed rate is included.
+ */
+ if (r == ic->ic_fixed_rate)
+ fixedrate = r;
+ /*
+ * Check against our supported rates.
+ */
+ for (j = 0; j < srs->rs_nrates; j++)
+ if (r == RV(srs->rs_rates[j])) {
+ if (r > okrate) /* NB: track max */
+ okrate = r;
+ break;
+ }
+
+ if (j == srs->rs_nrates && (rs[i] & IEEE80211_RATE_BASIC)) {
+ /*
+ * Don't try joining a BSS, if we don't support
+ * one of its basic rates.
+ */
+ okrate = 0;
+ goto back;
+ }
+ }
+ if (rs == se->se_rates+2) {
+ /* scan xrates too; sort of an algol68-style for loop */
+ nrs = se->se_xrates[1];
+ rs = se->se_xrates+2;
+ goto again;
+ }
+
+back:
+ if (okrate == 0 || ic->ic_fixed_rate != fixedrate)
+ return badrate | IEEE80211_RATE_BASIC;
+ else
+ return RV(okrate);
+#undef RV
+}
+
+static int
+match_ssid(const uint8_t *ie,
+ int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ int i;
+
+ for (i = 0; i < nssid; i++) {
+ if (ie[1] == ssids[i].len &&
+ memcmp(ie+2, ssids[i].ssid, ie[1]) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Test a scan candidate for suitability/compatibility.
+ */
+static int
+match_bss(struct ieee80211com *ic,
+ const struct ieee80211_scan_state *ss, struct sta_entry *se0,
+ int debug)
+{
+ struct ieee80211_scan_entry *se = &se0->base;
+ uint8_t rate;
+ int fail;
+
+ fail = 0;
+ if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
+ fail |= MATCH_CHANNEL;
+ /*
+ * NB: normally the desired mode is used to construct
+ * the channel list, but it's possible for the scan
+ * cache to include entries for stations outside this
+ * list so we check the desired mode here to weed them
+ * out.
+ */
+ if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+ (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
+ chanflags[ic->ic_des_mode])
+ fail |= MATCH_CHANNEL;
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ fail |= MATCH_CAPINFO;
+ } else {
+ if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+ fail |= MATCH_CAPINFO;
+ }
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+ fail |= MATCH_PRIVACY;
+ } else {
+ /* XXX does this mean privacy is supported or required? */
+ if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY)
+ fail |= MATCH_PRIVACY;
+ }
+ rate = check_rate(ic, se);
+ if (rate & IEEE80211_RATE_BASIC)
+ fail |= MATCH_RATE;
+ if (ss->ss_nssid != 0 &&
+ !match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid))
+ fail |= MATCH_SSID;
+ if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid))
+ fail |= MATCH_BSSID;
+ if (se0->se_fails >= STA_FAILS_MAX)
+ fail |= MATCH_FAILS;
+ /* NB: entries may be present awaiting purge, skip */
+ if (se0->se_notseen >= STA_PURGE_SCANS)
+ fail |= MATCH_NOTSEEN;
+ if (se->se_rssi < STA_RSSI_MIN)
+ fail |= MATCH_RSSI;
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg(ic, debug)) {
+ printf(" %c %s",
+ fail & MATCH_FAILS ? '=' :
+ fail & MATCH_NOTSEEN ? '^' :
+ fail ? '-' : '+', ether_sprintf(se->se_macaddr));
+ printf(" %s%c", ether_sprintf(se->se_bssid),
+ fail & MATCH_BSSID ? '!' : ' ');
+ printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan),
+ fail & MATCH_CHANNEL ? '!' : ' ');
+ printf(" %+4d%c", se->se_rssi, fail & MATCH_RSSI ? '!' : ' ');
+ printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
+ fail & MATCH_RATE ? '!' : ' ');
+ printf(" %4s%c",
+ (se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
+ (se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
+ "????",
+ fail & MATCH_CAPINFO ? '!' : ' ');
+ printf(" %3s%c ",
+ (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
+ "wep" : "no",
+ fail & MATCH_PRIVACY ? '!' : ' ');
+ ieee80211_print_essid(se->se_ssid+2, se->se_ssid[1]);
+ printf("%s\n", fail & MATCH_SSID ? "!" : "");
+ }
+#endif
+ return fail;
+}
+
+static void
+sta_update_notseen(struct sta_table *st)
+{
+ struct sta_entry *se;
+
+ mtx_lock(&st->st_lock);
+ TAILQ_FOREACH(se, &st->st_entry, se_list) {
+ /*
+ * If seen the reset and don't bump the count;
+ * otherwise bump the ``not seen'' count. Note
+ * that this insures that stations for which we
+ * see frames while not scanning but not during
+ * this scan will not be penalized.
+ */
+ if (se->se_seen)
+ se->se_seen = 0;
+ else
+ se->se_notseen++;
+ }
+ mtx_unlock(&st->st_lock);
+}
+
+static void
+sta_dec_fails(struct sta_table *st)
+{
+ struct sta_entry *se;
+
+ mtx_lock(&st->st_lock);
+ TAILQ_FOREACH(se, &st->st_entry, se_list)
+ if (se->se_fails)
+ se->se_fails--;
+ mtx_unlock(&st->st_lock);
+}
+
+static struct sta_entry *
+select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se, *selbs = NULL;
+
+ IEEE80211_DPRINTF(ic, debug, " %s\n",
+ "macaddr bssid chan rssi rate flag wep essid");
+ mtx_lock(&st->st_lock);
+ TAILQ_FOREACH(se, &st->st_entry, se_list) {
+ if (match_bss(ic, ss, se, debug) == 0) {
+ if (selbs == NULL)
+ selbs = se;
+ else if (sta_compare(se, selbs) > 0)
+ selbs = se;
+ }
+ }
+ mtx_unlock(&st->st_lock);
+
+ return selbs;
+}
+
+/*
+ * Pick an ap or ibss network to join or find a channel
+ * to use to start an ibss network.
+ */
+static int
+sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *selbs;
+
+ KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", ic->ic_opmode));
+
+ if (st->st_newscan) {
+ sta_update_notseen(st);
+ st->st_newscan = 0;
+ }
+ if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
+ /*
+ * Manual/background scan, don't select+join the
+ * bss, just return. The scanning framework will
+ * handle notification that this has completed.
+ */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ return 1;
+ }
+ /*
+ * Automatic sequencing; look for a candidate and
+ * if found join the network.
+ */
+ /* NB: unlocked read should be ok */
+ if (TAILQ_FIRST(&st->st_entry) == NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no scan candidate\n", __func__);
+notfound:
+ /*
+ * If nothing suitable was found decrement
+ * the failure counts so entries will be
+ * reconsidered the next time around. We
+ * really want to do this only for sta's
+ * where we've previously had some success.
+ */
+ sta_dec_fails(st);
+ st->st_newscan = 1;
+ return 0; /* restart scan */
+ }
+ selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
+ if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ goto notfound;
+ return 1; /* terminate scan */
+}
+
+/*
+ * Lookup an entry in the scan cache. We assume we're
+ * called from the bottom half or such that we don't need
+ * to block the bottom half so that it's safe to return
+ * a reference to an entry w/o holding the lock on the table.
+ */
+static struct sta_entry *
+sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+ struct sta_entry *se;
+ int hash = STA_HASH(macaddr);
+
+ mtx_lock(&st->st_lock);
+ LIST_FOREACH(se, &st->st_hash[hash], se_hash)
+ if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
+ break;
+ mtx_unlock(&st->st_lock);
+
+ return se; /* NB: unlocked */
+}
+
+static void
+sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se, *selbs;
+ uint8_t roamRate, curRate;
+ int8_t roamRssi, curRssi;
+
+ se = sta_lookup(st, ni->ni_macaddr);
+ if (se == NULL) {
+ /* XXX something is wrong */
+ return;
+ }
+
+ /* XXX do we need 11g too? */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
+ roamRate = ic->ic_roam.rate11b;
+ roamRssi = ic->ic_roam.rssi11b;
+ } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) {
+ roamRate = ic->ic_roam.rate11bOnly;
+ roamRssi = ic->ic_roam.rssi11bOnly;
+ } else {
+ roamRate = ic->ic_roam.rate11a;
+ roamRssi = ic->ic_roam.rssi11a;
+ }
+ /* NB: the most up to date rssi is in the node, not the scan cache */
+ curRssi = ic->ic_node_getrssi(ni);
+ if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+ curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ "%s: currssi %d currate %u roamrssi %d roamrate %u\n",
+ __func__, curRssi, curRate, roamRssi, roamRate);
+ } else {
+ curRate = roamRate; /* NB: insure compare below fails */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi);
+ }
+ /*
+ * Check if a new ap should be used and switch.
+ * XXX deauth current ap
+ */
+ if (curRate < roamRate || curRssi < roamRssi) {
+ if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ /*
+ * Scan cache contents are too old; force a scan now
+ * if possible so we have current state to make a
+ * decision with. We don't kick off a bg scan if
+ * we're using dynamic turbo and boosted or if the
+ * channel is busy.
+ * XXX force immediate switch on scan complete
+ */
+ if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+ time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle))
+ ieee80211_bg_scan(ic);
+ return;
+ }
+ se->base.se_rssi = curRssi;
+ selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM);
+ if (selbs != NULL && selbs != se) {
+ IEEE80211_DPRINTF(ic,
+ IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG,
+ "%s: ROAM: curRate %u, roamRate %u, "
+ "curRssi %d, roamRssi %d\n", __func__,
+ curRate, roamRate, curRssi, roamRssi);
+ ieee80211_sta_join(ic, &selbs->base);
+ }
+ }
+}
+
+/*
+ * Age entries in the scan cache.
+ * XXX also do roaming since it's convenient
+ */
+static void
+sta_age(struct ieee80211_scan_state *ss)
+{
+ struct ieee80211com *ic = ss->ss_ic;
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se, *next;
+
+ mtx_lock(&st->st_lock);
+ TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
+ if (se->se_notseen > STA_PURGE_SCANS) {
+ TAILQ_REMOVE(&st->st_entry, se, se_list);
+ LIST_REMOVE(se, se_hash);
+ FREE(se, M_80211_SCAN);
+ }
+ }
+ mtx_unlock(&st->st_lock);
+ /*
+ * If rate control is enabled check periodically to see if
+ * we should roam from our current connection to one that
+ * might be better. This only applies when we're operating
+ * in sta mode and automatic roaming is set.
+ * XXX defer if busy
+ * XXX repeater station
+ * XXX do when !bgscan?
+ */
+ KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", ic->ic_opmode));
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
+ (ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ ic->ic_state >= IEEE80211_S_RUN)
+ /* XXX vap is implicit */
+ sta_roam_check(ss, ic);
+}
+
+/*
+ * Iterate over the entries in the scan cache, invoking
+ * the callback function on each one.
+ */
+static void
+sta_iterate(struct ieee80211_scan_state *ss,
+ ieee80211_scan_iter_func *f, void *arg)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se;
+ u_int gen;
+
+ mtx_lock(&st->st_scanlock);
+ gen = st->st_scangen++;
+restart:
+ mtx_lock(&st->st_lock);
+ TAILQ_FOREACH(se, &st->st_entry, se_list) {
+ if (se->se_scangen != gen) {
+ se->se_scangen = gen;
+ /* update public state */
+ se->base.se_age = ticks - se->se_lastupdate;
+ mtx_unlock(&st->st_lock);
+ (*f)(arg, &se->base);
+ goto restart;
+ }
+ }
+ mtx_unlock(&st->st_lock);
+
+ mtx_unlock(&st->st_scanlock);
+}
+
+static void
+sta_assoc_fail(struct ieee80211_scan_state *ss,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se;
+
+ se = sta_lookup(st, macaddr);
+ if (se != NULL) {
+ se->se_fails++;
+ se->se_lastfail = ticks;
+ IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ macaddr, "%s: reason %u fails %u",
+ __func__, reason, se->se_fails);
+ }
+}
+
+static void
+sta_assoc_success(struct ieee80211_scan_state *ss,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se;
+
+ se = sta_lookup(st, macaddr);
+ if (se != NULL) {
+#if 0
+ se->se_fails = 0;
+ IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ macaddr, "%s: fails %u",
+ __func__, se->se_fails);
+#endif
+ se->se_lastassoc = ticks;
+ }
+}
+
+static const struct ieee80211_scanner sta_default = {
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+ .scan_start = sta_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = sta_cancel,
+ .scan_end = sta_pick_bss,
+ .scan_flush = sta_flush,
+ .scan_add = sta_add,
+ .scan_age = sta_age,
+ .scan_iterate = sta_iterate,
+ .scan_assoc_fail = sta_assoc_fail,
+ .scan_assoc_success = sta_assoc_success,
+};
+
+/*
+ * Adhoc mode-specific support.
+ */
+
+static const uint16_t adhocWorld[] = /* 36, 40, 44, 48 */
+{ 5180, 5200, 5220, 5240 };
+static const uint16_t adhocFcc3[] = /* 36, 40, 44, 48 145, 149, 153, 157, 161, 165 */
+{ 5180, 5200, 5220, 5240, 5725, 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t adhocMkk[] = /* 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t adhoc11b[] = /* 10, 11 */
+{ 2457, 2462 };
+
+static const struct scanlist adhocScanTable[] = {
+ { IEEE80211_MODE_11B, X(adhoc11b) },
+ { IEEE80211_MODE_11A, X(adhocWorld) },
+ { IEEE80211_MODE_11A, X(adhocFcc3) },
+ { IEEE80211_MODE_11B, X(adhocMkk) },
+ { .list = NULL }
+};
+#undef X
+
+/*
+ * Start an adhoc-mode scan by populating the channel list.
+ */
+static int
+adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ struct sta_table *st = ss->ss_priv;
+ const struct scanlist *scan;
+ enum ieee80211_phymode mode;
+
+ ss->ss_last = 0;
+ /*
+ * Use the table of ordered channels to construct the list
+ * of channels for scanning. Any channels in the ordered
+ * list not in the master list will be discarded.
+ */
+ for (scan = adhocScanTable; scan->list != NULL; scan++) {
+ mode = scan->mode;
+ if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+ /*
+ * If a desired mode was specified, scan only
+ * channels that satisfy that constraint.
+ */
+ if (ic->ic_des_mode != mode) {
+ /*
+ * The scan table marks 2.4Ghz channels as b
+ * so if the desired mode is 11g, then use
+ * the 11b channel list but upgrade the mode.
+ */
+ if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+ mode != IEEE80211_MODE_11B)
+ continue;
+ mode = IEEE80211_MODE_11G; /* upgrade */
+ }
+ } else {
+ /*
+ * This lets add_channels upgrade an 11b channel
+ * to 11g if available.
+ */
+ if (mode == IEEE80211_MODE_11B)
+ mode = IEEE80211_MODE_AUTO;
+ }
+#ifdef IEEE80211_F_XR
+ /* XR does not operate on turbo channels */
+ if ((ic->ic_flags & IEEE80211_F_XR) &&
+ (mode == IEEE80211_MODE_TURBO_A ||
+ mode == IEEE80211_MODE_TURBO_G))
+ continue;
+#endif
+ /*
+ * Add the list of the channels; any that are not
+ * in the master channel list will be discarded.
+ */
+ add_channels(ic, ss, mode, scan->list, scan->count);
+ }
+ ss->ss_next = 0;
+ /* XXX tunables */
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic)) {
+ if_printf(ic->ic_ifp, "scan set ");
+ ieee80211_scan_dump_channels(ss);
+ printf(" dwell min %ld max %ld\n",
+ ss->ss_mindwell, ss->ss_maxdwell);
+ }
+#endif /* IEEE80211_DEBUG */
+
+ st->st_newscan = 1;
+
+ return 0;
+#undef N
+}
+
+/*
+ * Select a channel to start an adhoc network on.
+ * The channel list was populated with appropriate
+ * channels so select one that looks least occupied.
+ * XXX need regulatory domain constraints
+ */
+static struct ieee80211_channel *
+adhoc_pick_channel(struct ieee80211_scan_state *ss)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se;
+ struct ieee80211_channel *c, *bestchan;
+ int i, bestrssi, maxrssi;
+
+ bestchan = NULL;
+ bestrssi = -1;
+
+ mtx_lock(&st->st_lock);
+ for (i = 0; i < ss->ss_last; i++) {
+ c = ss->ss_chans[i];
+ maxrssi = 0;
+ TAILQ_FOREACH(se, &st->st_entry, se_list) {
+ if (se->base.se_chan != c)
+ continue;
+ if (se->base.se_rssi > maxrssi)
+ maxrssi = se->base.se_rssi;
+ }
+ if (bestchan == NULL || maxrssi < bestrssi)
+ bestchan = c;
+ }
+ mtx_unlock(&st->st_lock);
+
+ return bestchan;
+}
+
+/*
+ * Pick an ibss network to join or find a channel
+ * to use to start an ibss network.
+ */
+static int
+adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *selbs;
+ struct ieee80211_channel *chan;
+
+ KASSERT(ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_AHDEMO,
+ ("wrong opmode %u", ic->ic_opmode));
+
+ if (st->st_newscan) {
+ sta_update_notseen(st);
+ st->st_newscan = 0;
+ }
+ if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
+ /*
+ * Manual/background scan, don't select+join the
+ * bss, just return. The scanning framework will
+ * handle notification that this has completed.
+ */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ return 1;
+ }
+ /*
+ * Automatic sequencing; look for a candidate and
+ * if found join the network.
+ */
+ /* NB: unlocked read should be ok */
+ if (TAILQ_FIRST(&st->st_entry) == NULL) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no scan candidate\n", __func__);
+notfound:
+ if (ic->ic_des_nssid) {
+ /*
+ * No existing adhoc network to join and we have
+ * an ssid; start one up. If no channel was
+ * specified, try to select a channel.
+ */
+ if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
+ chan = ieee80211_ht_adjust_channel(ic,
+ adhoc_pick_channel(ss), ic->ic_flags_ext);
+ else
+ chan = ic->ic_des_chan;
+ if (chan != NULL) {
+ ieee80211_create_ibss(ic, chan);
+ return 1;
+ }
+ }
+ /*
+ * If nothing suitable was found decrement
+ * the failure counts so entries will be
+ * reconsidered the next time around. We
+ * really want to do this only for sta's
+ * where we've previously had some success.
+ */
+ sta_dec_fails(st);
+ st->st_newscan = 1;
+ return 0; /* restart scan */
+ }
+ selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
+ if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ goto notfound;
+ return 1; /* terminate scan */
+}
+
+/*
+ * Age entries in the scan cache.
+ */
+static void
+adhoc_age(struct ieee80211_scan_state *ss)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se, *next;
+
+ mtx_lock(&st->st_lock);
+ TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
+ if (se->se_notseen > STA_PURGE_SCANS) {
+ TAILQ_REMOVE(&st->st_entry, se, se_list);
+ LIST_REMOVE(se, se_hash);
+ FREE(se, M_80211_SCAN);
+ }
+ }
+ mtx_unlock(&st->st_lock);
+}
+
+static const struct ieee80211_scanner adhoc_default = {
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+ .scan_start = adhoc_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = sta_cancel,
+ .scan_end = adhoc_pick_bss,
+ .scan_flush = sta_flush,
+ .scan_add = sta_add,
+ .scan_age = adhoc_age,
+ .scan_iterate = sta_iterate,
+ .scan_assoc_fail = sta_assoc_fail,
+ .scan_assoc_success = sta_assoc_success,
+};
+
+/*
+ * Module glue.
+ */
+static int
+wlan_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ ieee80211_scanner_register(IEEE80211_M_STA, &sta_default);
+ ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default);
+ ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default);
+ return 0;
+ case MOD_UNLOAD:
+ case MOD_QUIESCE:
+ if (nrefs) {
+ printf("wlan_scan_sta: still in use (%u dynamic refs)\n",
+ nrefs);
+ return EBUSY;
+ }
+ if (type == MOD_UNLOAD) {
+ ieee80211_scanner_unregister_all(&sta_default);
+ ieee80211_scanner_unregister_all(&adhoc_default);
+ }
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t wlan_mod = {
+ "wlan_scan_sta",
+ wlan_modevent,
+ 0
+};
+DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_scan_sta, 1);
+MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1);
Index: ieee80211_crypto.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_crypto.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211_crypto.h -L sys/net80211/ieee80211_crypto.h -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211_crypto.h
+++ sys/net80211/ieee80211_crypto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,7 +23,7 @@
* (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: src/sys/net80211/ieee80211_crypto.h,v 1.9.2.1 2005/09/03 22:40:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.13 2007/06/11 03:36:54 sam Exp $
*/
#ifndef _NET80211_IEEE80211_CRYPTO_H_
#define _NET80211_IEEE80211_CRYPTO_H_
@@ -45,7 +39,7 @@
*/
struct ieee80211_wepkey {
u_int wk_len; /* key length in bytes */
- u_int8_t wk_key[IEEE80211_KEYBUF_SIZE];
+ uint8_t wk_key[IEEE80211_KEYBUF_SIZE];
};
struct ieee80211_cipher;
@@ -66,12 +60,12 @@
* Ciphers such as TKIP may also support mixed hardware/software
* encrypt/decrypt and MIC processing.
*/
-typedef u_int16_t ieee80211_keyix; /* h/w key index */
+typedef uint16_t ieee80211_keyix; /* h/w key index */
struct ieee80211_key {
- u_int8_t wk_keylen; /* key length in bytes */
- u_int8_t wk_pad;
- u_int16_t wk_flags;
+ uint8_t wk_keylen; /* key length in bytes */
+ uint8_t wk_pad;
+ uint16_t wk_flags;
#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */
#define IEEE80211_KEY_RECV 0x02 /* key used for recv */
#define IEEE80211_KEY_GROUP 0x04 /* key used for WPA group operation */
@@ -79,11 +73,11 @@
#define IEEE80211_KEY_SWMIC 0x20 /* host-based enmic/demic */
ieee80211_keyix wk_keyix; /* h/w key index */
ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */
- u_int8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+ uint8_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 */
+ uint64_t wk_keyrsc; /* key receive sequence counter */
+ uint64_t wk_keytsc; /* key transmit sequence counter */
const struct ieee80211_cipher *wk_cipher;
void *wk_private; /* private cipher state */
};
@@ -122,7 +116,7 @@
struct ieee80211_crypto_state {
struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID];
ieee80211_keyix cs_def_txkey; /* default/group tx key index */
- u_int16_t cs_max_keyix; /* max h/w key index */
+ uint16_t cs_max_keyix; /* max h/w key index */
int (*cs_key_alloc)(struct ieee80211com *,
const struct ieee80211_key *,
@@ -131,7 +125,7 @@
const struct ieee80211_key *);
int (*cs_key_set)(struct ieee80211com *,
const struct ieee80211_key *,
- const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
void (*cs_key_update_begin)(struct ieee80211com *);
void (*cs_key_update_end)(struct ieee80211com *);
};
@@ -143,7 +137,7 @@
int ieee80211_crypto_delkey(struct ieee80211com *,
struct ieee80211_key *);
int ieee80211_crypto_setkey(struct ieee80211com *,
- struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]);
+ struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]);
void ieee80211_crypto_delglobalkeys(struct ieee80211com *);
/*
@@ -162,13 +156,16 @@
void (*ic_detach)(struct ieee80211_key *);
int (*ic_setkey)(struct ieee80211_key *);
int (*ic_encap)(struct ieee80211_key *, struct mbuf *,
- u_int8_t keyid);
+ uint8_t keyid);
int (*ic_decap)(struct ieee80211_key *, struct mbuf *, int);
int (*ic_enmic)(struct ieee80211_key *, struct mbuf *, int);
int (*ic_demic)(struct ieee80211_key *, struct mbuf *, int);
};
extern const struct ieee80211_cipher ieee80211_cipher_none;
+#define IEEE80211_KEY_UNDEFINED(k) \
+ ((k)->wk_cipher == &ieee80211_cipher_none)
+
void ieee80211_crypto_register(const struct ieee80211_cipher *);
void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
int ieee80211_crypto_available(u_int cipher);
Index: ieee80211.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/net80211/ieee80211.h -L sys/net80211/ieee80211.h -u -r1.1.1.1 -r1.2
--- sys/net80211/ieee80211.h
+++ sys/net80211/ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,12 +11,6 @@
* 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
@@ -29,7 +23,7 @@
* (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: src/sys/net80211/ieee80211.h,v 1.9.2.1 2005/07/29 23:31:02 sam Exp $
+ * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.15.2.3 2007/11/28 06:12:30 sam Exp $
*/
#ifndef _NET80211_IEEE80211_H_
#define _NET80211_IEEE80211_H_
@@ -44,11 +38,11 @@
/* IEEE 802.11 PLCP header */
struct ieee80211_plcp_hdr {
- u_int16_t i_sfd;
- u_int8_t i_signal;
- u_int8_t i_service;
- u_int16_t i_length;
- u_int16_t i_crc;
+ uint16_t i_sfd;
+ uint8_t i_signal;
+ uint8_t i_service;
+ uint16_t i_length;
+ uint16_t i_crc;
} __packed;
#define IEEE80211_PLCP_SFD 0xF3A0
@@ -58,52 +52,52 @@
* generic definitions for IEEE 802.11 frames
*/
struct ieee80211_frame {
- u_int8_t i_fc[2];
- u_int8_t i_dur[2];
- u_int8_t i_addr1[IEEE80211_ADDR_LEN];
- u_int8_t i_addr2[IEEE80211_ADDR_LEN];
- u_int8_t i_addr3[IEEE80211_ADDR_LEN];
- u_int8_t i_seq[2];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_addr3[IEEE80211_ADDR_LEN];
+ uint8_t i_seq[2];
/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
/* see below */
} __packed;
struct ieee80211_qosframe {
- u_int8_t i_fc[2];
- u_int8_t i_dur[2];
- u_int8_t i_addr1[IEEE80211_ADDR_LEN];
- u_int8_t i_addr2[IEEE80211_ADDR_LEN];
- u_int8_t i_addr3[IEEE80211_ADDR_LEN];
- u_int8_t i_seq[2];
- u_int8_t i_qos[2];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_addr3[IEEE80211_ADDR_LEN];
+ uint8_t i_seq[2];
+ uint8_t i_qos[2];
/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
/* see below */
} __packed;
struct ieee80211_qoscntl {
- u_int8_t i_qos[2];
+ uint8_t i_qos[2];
};
struct ieee80211_frame_addr4 {
- u_int8_t i_fc[2];
- u_int8_t i_dur[2];
- u_int8_t i_addr1[IEEE80211_ADDR_LEN];
- u_int8_t i_addr2[IEEE80211_ADDR_LEN];
- u_int8_t i_addr3[IEEE80211_ADDR_LEN];
- u_int8_t i_seq[2];
- u_int8_t i_addr4[IEEE80211_ADDR_LEN];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_addr3[IEEE80211_ADDR_LEN];
+ uint8_t i_seq[2];
+ uint8_t i_addr4[IEEE80211_ADDR_LEN];
} __packed;
struct ieee80211_qosframe_addr4 {
- u_int8_t i_fc[2];
- u_int8_t i_dur[2];
- u_int8_t i_addr1[IEEE80211_ADDR_LEN];
- u_int8_t i_addr2[IEEE80211_ADDR_LEN];
- u_int8_t i_addr3[IEEE80211_ADDR_LEN];
- u_int8_t i_seq[2];
- u_int8_t i_addr4[IEEE80211_ADDR_LEN];
- u_int8_t i_qos[2];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_addr3[IEEE80211_ADDR_LEN];
+ uint8_t i_seq[2];
+ uint8_t i_addr4[IEEE80211_ADDR_LEN];
+ uint8_t i_qos[2];
} __packed;
#define IEEE80211_FC0_VERSION_MASK 0x03
@@ -129,7 +123,9 @@
#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0
#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0
#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0
+#define IEEE80211_FC0_SUBTYPE_ACTION 0xd0
/* for TYPE_CTL */
+#define IEEE80211_FC0_SUBTYPE_BAR 0x80
#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0
#define IEEE80211_FC0_SUBTYPE_RTS 0xb0
#define IEEE80211_FC0_SUBTYPE_CTS 0xc0
@@ -165,13 +161,28 @@
#define IEEE80211_SEQ_FRAG_SHIFT 0
#define IEEE80211_SEQ_SEQ_MASK 0xfff0
#define IEEE80211_SEQ_SEQ_SHIFT 4
+#define IEEE80211_SEQ_RANGE 4096
+
+#define IEEE80211_SEQ_ADD(seq, incr) \
+ (((seq) + (incr)) & (IEEE80211_SEQ_RANGE-1))
+#define IEEE80211_SEQ_INC(seq) IEEE80211_SEQ_ADD(seq,1)
+#define IEEE80211_SEQ_SUB(a, b) \
+ (((a) + IEEE80211_SEQ_RANGE - (b)) & (IEEE80211_SEQ_RANGE-1))
+
+#define IEEE80211_SEQ_BA_RANGE 2048 /* 2^11 */
+#define IEEE80211_SEQ_BA_BEFORE(a, b) \
+ (IEEE80211_SEQ_SUB(b, a+1) < IEEE80211_SEQ_BA_RANGE-1)
#define IEEE80211_NWID_LEN 32
#define IEEE80211_QOS_TXOP 0x00ff
/* bit 8 is reserved */
+#define IEEE80211_QOS_AMSDU 0x80
+#define IEEE80211_QOS_AMSDU_S 7
#define IEEE80211_QOS_ACKPOLICY 0x60
#define IEEE80211_QOS_ACKPOLICY_S 5
+#define IEEE80211_QOS_ACKPOLICY_NOACK 0x20 /* No ACK required */
+#define IEEE80211_QOS_ACKPOLICY_BA 0x60 /* Block ACK */
#define IEEE80211_QOS_ESOP 0x10
#define IEEE80211_QOS_ESOP_S 4
#define IEEE80211_QOS_TID 0x0f
@@ -186,53 +197,54 @@
* 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 */
+ uint8_t wme_id; /* IEEE80211_ELEMID_VENDOR */
+ uint8_t wme_len; /* length in bytes */
+ uint8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */
+ uint8_t wme_type; /* OUI type */
+ uint8_t wme_subtype; /* OUI subtype */
+ uint8_t wme_version; /* spec revision */
+ uint8_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];
+ uint8_t ts_id;
+ uint8_t ts_len;
+ uint8_t ts_oui[3];
+ uint8_t ts_oui_type;
+ uint8_t ts_oui_subtype;
+ uint8_t ts_version;
+ uint8_t ts_tsinfo[3];
+ uint8_t ts_nom_msdu[2];
+ uint8_t ts_max_msdu[2];
+ uint8_t ts_min_svc[4];
+ uint8_t ts_max_svc[4];
+ uint8_t ts_inactv_intv[4];
+ uint8_t ts_susp_intv[4];
+ uint8_t ts_start_svc[4];
+ uint8_t ts_min_rate[4];
+ uint8_t ts_mean_rate[4];
+ uint8_t ts_max_burst[4];
+ uint8_t ts_min_phy[4];
+ uint8_t ts_peak_rate[4];
+ uint8_t ts_delay[4];
+ uint8_t ts_surplus[2];
+ uint8_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;
+ uint8_t acp_aci_aifsn;
+ uint8_t acp_logcwminmax;
+ uint16_t acp_txop;
} __packed;
#define WME_NUM_AC 4 /* 4 AC categories */
+#define WME_NUM_TID 16 /* 16 tids */
#define WME_PARAM_ACI 0x60 /* Mask for ACI field */
#define WME_PARAM_ACI_S 5 /* Shift for ACI field */
@@ -252,7 +264,7 @@
0)
#define TID_TO_WME_AC(_tid) ( \
- ((_tid) < 1) ? WME_AC_BE : \
+ ((_tid) == 0 || (_tid) == 3) ? WME_AC_BE : \
((_tid) < 3) ? WME_AC_BK : \
((_tid) < 6) ? WME_AC_VI : \
WME_AC_VO)
@@ -261,15 +273,15 @@
* 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;
+ uint8_t param_id;
+ uint8_t param_len;
+ uint8_t param_oui[3];
+ uint8_t param_oui_type;
+ uint8_t param_oui_subtype;
+ uint8_t param_version;
+ uint8_t param_qosInfo;
#define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */
- u_int8_t param_reserved;
+ uint8_t param_reserved;
struct ieee80211_wme_acparams params_acParams[WME_NUM_AC];
} __packed;
@@ -277,61 +289,174 @@
* 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;
+ uint8_t mnf_category;
+ uint8_t mnf_action;
+ uint8_t mnf_dialog;
+ uint8_t mnf_status;
} __packed;
#define MNF_SETUP_REQ 0
#define MNF_SETUP_RESP 1
#define MNF_TEARDOWN 2
+/*
+ * 802.11n Management Action Frames
+ */
+/* generic frame format */
+struct ieee80211_action {
+ uint8_t ia_category;
+ uint8_t ia_action;
+} __packed;
+
+#define IEEE80211_ACTION_CAT_QOS 0 /* QoS */
+#define IEEE80211_ACTION_CAT_BA 3 /* BA */
+#define IEEE80211_ACTION_CAT_HT 7 /* HT */
+
+#define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommended xmit chan width*/
+#define IEEE80211_ACTION_HT_MIMOPWRSAVE 1 /* MIMO power save */
+
+/* HT - recommended transmission channel width */
+struct ieee80211_action_ht_txchwidth {
+ struct ieee80211_action at_header;
+ uint8_t at_chwidth;
+} __packed;
+
+#define IEEE80211_A_HT_TXCHWIDTH_20 0
+#define IEEE80211_A_HT_TXCHWIDTH_2040 1
+
+/* HT - MIMO Power Save (NB: D2.04) */
+struct ieee80211_action_ht_mimopowersave {
+ struct ieee80211_action am_header;
+ uint8_t am_control;
+} __packed;
+
+#define IEEE80211_A_HT_MIMOPWRSAVE_ENA 0x01 /* PS enabled */
+#define IEEE80211_A_HT_MIMOPWRSAVE_MODE 0x02
+#define IEEE80211_A_HT_MIMOPWRSAVE_MODE_S 1
+#define IEEE80211_A_HT_MIMOPWRSAVE_DYNAMIC 0x02 /* Dynamic Mode */
+#define IEEE80211_A_HT_MIMOPWRSAVE_STATIC 0x00 /* no SM packets */
+/* bits 2-7 reserved */
+
+/* Block Ack actions */
+#define IEEE80211_ACTION_BA_ADDBA_REQUEST 0 /* ADDBA request */
+#define IEEE80211_ACTION_BA_ADDBA_RESPONSE 1 /* ADDBA response */
+#define IEEE80211_ACTION_BA_DELBA 2 /* DELBA */
+
+/* Block Ack Parameter Set */
+#define IEEE80211_BAPS_BUFSIZ 0xffc0 /* buffer size */
+#define IEEE80211_BAPS_BUFSIZ_S 6
+#define IEEE80211_BAPS_TID 0x003c /* TID */
+#define IEEE80211_BAPS_TID_S 2
+#define IEEE80211_BAPS_POLICY 0x0002 /* block ack policy */
+#define IEEE80211_BAPS_POLICY_S 1
+
+#define IEEE80211_BAPS_POLICY_DELAYED (0<<IEEE80211_BAPS_POLICY_S)
+#define IEEE80211_BAPS_POLICY_IMMEDIATE (1<<IEEE80211_BAPS_POLICY_S)
+
+/* Block Ack Sequence Control */
+#define IEEE80211_BASEQ_START 0xfff0 /* starting seqnum */
+#define IEEE80211_BASEQ_START_S 4
+#define IEEE80211_BASEQ_FRAG 0x000f /* fragment number */
+#define IEEE80211_BASEQ_FRAG_S 0
+
+/* Delayed Block Ack Parameter Set */
+#define IEEE80211_DELBAPS_TID 0xf000 /* TID */
+#define IEEE80211_DELBAPS_TID_S 12
+#define IEEE80211_DELBAPS_INIT 0x0800 /* initiator */
+#define IEEE80211_DELBAPS_INIT_S 11
+
+/* BA - ADDBA request */
+struct ieee80211_action_ba_addbarequest {
+ struct ieee80211_action rq_header;
+ uint8_t rq_dialogtoken;
+ uint16_t rq_baparamset;
+ uint16_t rq_batimeout; /* in TUs */
+ uint16_t rq_baseqctl;
+} __packed;
+
+/* BA - ADDBA response */
+struct ieee80211_action_ba_addbaresponse {
+ struct ieee80211_action rs_header;
+ uint8_t rs_dialogtoken;
+ uint16_t rs_statuscode;
+ uint16_t rs_baparamset;
+ uint16_t rs_batimeout; /* in TUs */
+} __packed;
+
+/* BA - DELBA */
+struct ieee80211_action_ba_delba {
+ struct ieee80211_action dl_header;
+ uint16_t dl_baparamset;
+ uint16_t dl_reasoncode;
+} __packed;
+
+/* BAR Control */
+#define IEEE80211_BAR_TID 0xf000 /* TID */
+#define IEEE80211_BAR_TID_S 12
+#define IEEE80211_BAR_COMP 0x0004 /* compressed */
+#define IEEE80211_BAR_MTID 0x0002
+#define IEEE80211_BAR_NOACK 0x0001 /* no-ack policy */
+
+struct ieee80211_ba_request {
+ uint16_t rq_barctl;
+ uint16_t rq_barseqctl;
+} __packed;
+
/*
* Control frames.
*/
struct ieee80211_frame_min {
- u_int8_t i_fc[2];
- u_int8_t i_dur[2];
- u_int8_t i_addr1[IEEE80211_ADDR_LEN];
- u_int8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
/* FCS */
} __packed;
struct ieee80211_frame_rts {
- u_int8_t i_fc[2];
- u_int8_t i_dur[2];
- u_int8_t i_ra[IEEE80211_ADDR_LEN];
- u_int8_t i_ta[IEEE80211_ADDR_LEN];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_ra[IEEE80211_ADDR_LEN];
+ uint8_t i_ta[IEEE80211_ADDR_LEN];
/* FCS */
} __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];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_ra[IEEE80211_ADDR_LEN];
/* FCS */
} __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];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_ra[IEEE80211_ADDR_LEN];
/* FCS */
} __packed;
struct ieee80211_frame_pspoll {
- u_int8_t i_fc[2];
- u_int8_t i_aid[2];
- u_int8_t i_bssid[IEEE80211_ADDR_LEN];
- u_int8_t i_ta[IEEE80211_ADDR_LEN];
+ uint8_t i_fc[2];
+ uint8_t i_aid[2];
+ uint8_t i_bssid[IEEE80211_ADDR_LEN];
+ uint8_t i_ta[IEEE80211_ADDR_LEN];
/* FCS */
} __packed;
struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */
- u_int8_t i_fc[2];
- u_int8_t i_dur[2]; /* should be zero */
- u_int8_t i_ra[IEEE80211_ADDR_LEN];
- u_int8_t i_bssid[IEEE80211_ADDR_LEN];
+ uint8_t i_fc[2];
+ uint8_t i_dur[2]; /* should be zero */
+ uint8_t i_ra[IEEE80211_ADDR_LEN];
+ uint8_t i_bssid[IEEE80211_ADDR_LEN];
+ /* FCS */
+} __packed;
+
+struct ieee80211_frame_bar {
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_ra[IEEE80211_ADDR_LEN];
+ uint8_t i_ta[IEEE80211_ADDR_LEN];
+ uint16_t i_ctl;
+ uint16_t i_seq;
/* FCS */
} __packed;
@@ -347,8 +472,6 @@
* octet information[length]
*/
-typedef u_int8_t *ieee80211_mgt_beacon_t;
-
#define IEEE80211_BEACON_INTERVAL(beacon) \
((beacon)[8] | ((beacon)[9] << 8))
#define IEEE80211_BEACON_CAPABILITY(beacon) \
@@ -362,7 +485,8 @@
#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020
#define IEEE80211_CAPINFO_PBCC 0x0040
#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080
-/* bits 8-9 are reserved */
+#define IEEE80211_CAPINFO_SPECTRUM_MGMT 0x0100
+/* bit 9 is reserved */
#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400
#define IEEE80211_CAPINFO_RSN 0x0800
/* bit 12 is reserved */
@@ -373,21 +497,153 @@
* 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 */
- u_int32_t wpa_mcipher[1]; /* multicast/group key cipher */
- u_int16_t wpa_uciphercnt; /* # pairwise key ciphers */
- u_int32_t wpa_uciphers[8];/* ciphers */
- u_int16_t wpa_authselcnt; /* authentication selector cnt*/
- u_int32_t wpa_authsels[8];/* selectors */
- 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 */
+ uint8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */
+ uint8_t wpa_len; /* length in bytes */
+ uint8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */
+ uint8_t wpa_type; /* OUI type */
+ uint16_t wpa_version; /* spec revision */
+ uint32_t wpa_mcipher[1]; /* multicast/group key cipher */
+ uint16_t wpa_uciphercnt; /* # pairwise key ciphers */
+ uint32_t wpa_uciphers[8];/* ciphers */
+ uint16_t wpa_authselcnt; /* authentication selector cnt*/
+ uint32_t wpa_authsels[8];/* selectors */
+ uint16_t wpa_caps; /* 802.11i capabilities */
+ uint16_t wpa_pmkidcnt; /* 802.11i pmkid count */
+ uint16_t wpa_pmkids[8]; /* 802.11i pmkids */
+} __packed;
+
+/*
+ * 802.11n HT Capability IE
+ * NB: these reflect D1.10
+ */
+struct ieee80211_ie_htcap {
+ uint8_t hc_id; /* element ID */
+ uint8_t hc_len; /* length in bytes */
+ uint16_t hc_cap; /* HT caps (see below) */
+ uint8_t hc_param; /* HT params (see below) */
+ uint8_t hc_mcsset[16]; /* supported MCS set */
+ uint16_t hc_extcap; /* extended HT capabilities */
+ uint32_t hc_txbf; /* txbf capabilities */
+ uint8_t hc_antenna; /* antenna capabilities */
+} __packed;
+
+/* HT capability flags (ht_cap) */
+#define IEEE80211_HTCAP_LDPC 0x0001 /* LDPC supported */
+#define IEEE80211_HTCAP_CHWIDTH40 0x0002 /* 20/40 supported */
+#define IEEE80211_HTCAP_SMPS 0x000c /* SM Power Save mode */
+#define IEEE80211_HTCAP_SMPS_OFF 0x0000 /* none (static mode) */
+#define IEEE80211_HTCAP_SMPS_DYNAMIC 0x0004 /* send RTS first */
+/* NB: SMPS value 2 is reserved */
+#define IEEE80211_HTCAP_SMPS_ENA 0x000c /* enabled */
+#define IEEE80211_HTCAP_GREENFIELD 0x0010 /* Greenfield supported */
+#define IEEE80211_HTCAP_SHORTGI20 0x0020 /* Short GI in 20MHz */
+#define IEEE80211_HTCAP_SHORTGI40 0x0040 /* Short GI in 40MHz */
+#define IEEE80211_HTCAP_TXSTBC 0x0080 /* STBC tx ok */
+#define IEEE80211_HTCAP_RXSTBC 0x0300 /* STBC rx support */
+#define IEEE80211_HTCAP_RXSTBC_S 8
+#define IEEE80211_HTCAP_RXSTBC_1STREAM 0x0100 /* 1 spatial stream */
+#define IEEE80211_HTCAP_RXSTBC_2STREAM 0x0200 /* 1-2 spatial streams*/
+#define IEEE80211_HTCAP_RXSTBC_3STREAM 0x0300 /* 1-3 spatial streams*/
+#define IEEE80211_HTCAP_DELBA 0x0400 /* HT DELBA supported */
+#define IEEE80211_HTCAP_MAXAMSDU 0x0800 /* max A-MSDU length */
+#define IEEE80211_HTCAP_MAXAMSDU_7935 0x0800 /* 7935 octets */
+#define IEEE80211_HTCAP_MAXAMSDU_3839 0x0000 /* 3839 octets */
+#define IEEE80211_HTCAP_DSSSCCK40 0x1000 /* DSSS/CCK in 40MHz */
+#define IEEE80211_HTCAP_PSMP 0x2000 /* PSMP supported */
+#define IEEE80211_HTCAP_40INTOLERANT 0x4000 /* 40MHz intolerant */
+#define IEEE80211_HTCAP_LSIGTXOPPROT 0x8000 /* L-SIG TXOP prot */
+
+/* HT parameters (hc_param) */
+#define IEEE80211_HTCAP_MAXRXAMPDU 0x03 /* max rx A-MPDU factor */
+#define IEEE80211_HTCAP_MAXRXAMPDU_S 0
+#define IEEE80211_HTCAP_MAXRXAMPDU_8K 0
+#define IEEE80211_HTCAP_MAXRXAMPDU_16K 1
+#define IEEE80211_HTCAP_MAXRXAMPDU_32K 2
+#define IEEE80211_HTCAP_MAXRXAMPDU_64K 3
+#define IEEE80211_HTCAP_MPDUDENSITY 0x1c /* min MPDU start spacing */
+#define IEEE80211_HTCAP_MPDUDENSITY_S 2
+#define IEEE80211_HTCAP_MPDUDENSITY_NA 0 /* no time restriction */
+#define IEEE80211_HTCAP_MPDUDENSITY_025 1 /* 1/4 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_05 2 /* 1/2 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_1 3 /* 1 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_2 4 /* 2 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_4 5 /* 4 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_8 6 /* 8 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_16 7 /* 16 us */
+
+/* HT extended capabilities (hc_extcap) */
+#define IEEE80211_HTCAP_PCO 0x0001 /* PCO capable */
+#define IEEE80211_HTCAP_PCOTRANS 0x0006 /* PCO transition time */
+#define IEEE80211_HTCAP_PCOTRANS_S 1
+#define IEEE80211_HTCAP_PCOTRANS_04 0x0002 /* 400 us */
+#define IEEE80211_HTCAP_PCOTRANS_15 0x0004 /* 1.5 ms */
+#define IEEE80211_HTCAP_PCOTRANS_5 0x0006 /* 5 ms */
+/* bits 3-7 reserved */
+#define IEEE80211_HTCAP_MCSFBACK 0x0300 /* MCS feedback */
+#define IEEE80211_HTCAP_MCSFBACK_S 8
+#define IEEE80211_HTCAP_MCSFBACK_NONE 0x0000 /* nothing provided */
+#define IEEE80211_HTCAP_MCSFBACK_UNSOL 0x0200 /* unsolicited feedback */
+#define IEEE80211_HTCAP_MCSFBACK_MRQ 0x0300 /* " "+respond to MRQ */
+#define IEEE80211_HTCAP_HTC 0x0400 /* +HTC support */
+#define IEEE80211_HTCAP_RDR 0x0800 /* reverse direction responder*/
+/* bits 12-15 reserved */
+
+/*
+ * 802.11n HT Information IE
+ */
+struct ieee80211_ie_htinfo {
+ uint8_t hi_id; /* element ID */
+ uint8_t hi_len; /* length in bytes */
+ uint8_t hi_ctrlchannel; /* primary channel */
+ uint8_t hi_byte1; /* ht ie byte 1 */
+ uint8_t hi_byte2; /* ht ie byte 2 */
+ uint8_t hi_byte3; /* ht ie byte 3 */
+ uint16_t hi_byte45; /* ht ie bytes 4+5 */
+ uint8_t hi_basicmcsset[16]; /* basic MCS set */
} __packed;
+/* byte1 */
+#define IEEE80211_HTINFO_2NDCHAN 0x03 /* secondary/ext chan offset */
+#define IEEE80211_HTINFO_2NDCHAN_S 0
+#define IEEE80211_HTINFO_2NDCHAN_NONE 0x00 /* no secondary/ext channel */
+#define IEEE80211_HTINFO_2NDCHAN_ABOVE 0x01 /* above private channel */
+/* NB: 2 is reserved */
+#define IEEE80211_HTINFO_2NDCHAN_BELOW 0x03 /* below primary channel */
+#define IEEE80211_HTINFO_TXWIDTH 0x04 /* tx channel width */
+#define IEEE80211_HTINFO_TXWIDTH_20 0x00 /* 20MHz width */
+#define IEEE80211_HTINFO_TXWIDTH_2040 0x04 /* any supported width */
+#define IEEE80211_HTINFO_RIFSMODE 0x08 /* Reduced IFS (RIFS) use */
+#define IEEE80211_HTINFO_RIFSMODE_PROH 0x00 /* RIFS use prohibited */
+#define IEEE80211_HTINFO_RIFSMODE_PERM 0x08 /* RIFS use permitted */
+#define IEEE80211_HTINFO_PMSPONLY 0x10 /* PSMP required to associate */
+#define IEEE80211_HTINFO_SIGRAN 0xe0 /* shortest Service Interval */
+#define IEEE80211_HTINFO_SIGRAN_S 5
+#define IEEE80211_HTINFO_SIGRAN_5 0x00 /* 5 ms */
+/* XXX add rest */
+
+/* bytes 2+3 */
+#define IEEE80211_HTINFO_OPMODE 0x03 /* operating mode */
+#define IEEE80211_HTINFO_OPMODE_S 0
+#define IEEE80211_HTINFO_OPMODE_PURE 0x00 /* no protection */
+#define IEEE80211_HTINFO_OPMODE_PROTOPT 0x01 /* protection optional */
+#define IEEE80211_HTINFO_OPMODE_HT20PR 0x02 /* protection for HT20 sta's */
+#define IEEE80211_HTINFO_OPMODE_MIXED 0x03 /* protection for legacy sta's*/
+#define IEEE80211_HTINFO_NONGF_PRESENT 0x04 /* non-GF sta's present */
+#define IEEE80211_HTINFO_TXBL 0x08 /* transmit burst limit */
+#define IEEE80211_HTINFO_NONHT_PRESENT 0x10 /* non-HT sta's present */
+/* bits 5-15 reserved */
+
+/* bytes 4+5 */
+#define IEEE80211_HTINFO_2NDARYBEACON 0x01
+#define IEEE80211_HTINFO_LSIGTXOPPROT 0x02
+#define IEEE80211_HTINFO_PCO_ACTIVE 0x04
+#define IEEE80211_HTINFO_40MHZPHASE 0x08
+
+/* byte5 */
+#define IEEE80211_HTINFO_BASIC_STBCMCS 0x7f
+#define IEEE80211_HTINFO_BASIC_STBCMCS_S 0
+#define IEEE80211_HTINFO_DUALPROTECTED 0x80
+
/*
* Management information element payloads.
*/
@@ -403,36 +659,80 @@
IEEE80211_ELEMID_COUNTRY = 7,
IEEE80211_ELEMID_CHALLENGE = 16,
/* 17-31 reserved for challenge text extension */
+ IEEE80211_ELEMID_PWRCNSTR = 32,
+ IEEE80211_ELEMID_PWRCAP = 33,
+ IEEE80211_ELEMID_TPCREQ = 34,
+ IEEE80211_ELEMID_TPCREP = 35,
+ IEEE80211_ELEMID_SUPPCHAN = 36,
+ IEEE80211_ELEMID_CHANSWITCHANN = 37,
+ IEEE80211_ELEMID_MEASREQ = 38,
+ IEEE80211_ELEMID_MEASREP = 39,
+ IEEE80211_ELEMID_QUIET = 40,
+ IEEE80211_ELEMID_IBSSDFS = 41,
IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_HTCAP = 45,
IEEE80211_ELEMID_RSN = 48,
IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_HTINFO = 61,
IEEE80211_ELEMID_TPC = 150,
IEEE80211_ELEMID_CCKM = 156,
IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
};
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 */
+ uint8_t tim_ie; /* IEEE80211_ELEMID_TIM */
+ uint8_t tim_len;
+ uint8_t tim_count; /* DTIM count */
+ uint8_t tim_period; /* DTIM period */
+ uint8_t tim_bitctl; /* bitmap control */
+ uint8_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 */
+ uint8_t ie; /* IEEE80211_ELEMID_COUNTRY */
+ uint8_t len;
+ uint8_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 */
+ uint8_t schan; /* starting channel */
+ uint8_t nchan; /* number channels */
+ uint8_t maxtxpwr; /* tx power cap */
+ } __packed band[10]; /* sub bands */
} __packed;
-#define IEEE80211_CHALLENGE_LEN 128
+/*
+ * 802.11h Channel Switch Announcement (CSA).
+ */
+struct ieee80211_csa_ie {
+ uint8_t csa_ie; /* IEEE80211_ELEMID_CHANSWITCHANN */
+ uint8_t csa_len;
+ uint8_t csa_mode; /* Channel Switch Mode */
+ uint8_t csa_newchan; /* New Channel Number */
+ uint8_t csa_count; /* Channel Switch Count */
+} __packed;
+/*
+ * Atheros advanced capability information element.
+ */
+struct ieee80211_ath_ie {
+ uint8_t ath_id; /* IEEE80211_ELEMID_VENDOR */
+ uint8_t ath_len; /* length in bytes */
+ uint8_t ath_oui[3]; /* 0x00, 0x03, 0x7f */
+ uint8_t ath_oui_type; /* OUI type */
+ uint8_t ath_oui_subtype; /* OUI subtype */
+ uint8_t ath_version; /* spec revision */
+ uint8_t ath_capability; /* capability info */
+#define ATHEROS_CAP_TURBO_PRIME 0x01 /* dynamic turbo--aka Turbo' */
+#define ATHEROS_CAP_COMPRESSION 0x02 /* data compression */
+#define ATHEROS_CAP_FAST_FRAME 0x04 /* fast (jumbo) frames */
+#define ATHEROS_CAP_XR 0x08 /* Xtended Range support */
+#define ATHEROS_CAP_AR 0x10 /* Advanded Radar support */
+#define ATHEROS_CAP_BURST 0x20 /* Bursting - not negotiated */
+#define ATHEROS_CAP_WME 0x40 /* CWMin tuning */
+#define ATHEROS_CAP_BOOST 0x80 /* use turbo/!turbo mode */
+ uint8_t ath_defkeyix[2];
+} __packed;
+
+/* rate set entries are in .5 Mb/s units, and potentially marked as basic */
#define IEEE80211_RATE_BASIC 0x80
#define IEEE80211_RATE_VAL 0x7f
@@ -441,16 +741,14 @@
#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
-/* bits 3-6 reserved */
-#define ATHEROS_CAP_BOOST 0x80
-
-#define ATH_OUI 0x7f0300 /* Atheros OUI */
+#define ATH_OUI 0x7f0300 /* Atheros OUI */
#define ATH_OUI_TYPE 0x01
-#define ATH_OUI_VERSION 0x01
+#define ATH_OUI_SUBTYPE 0x01
+#define ATH_OUI_VERSION 0x00
+
+#define BCM_OUI 0x4c9000 /* Broadcom OUI */
+#define BCM_OUI_HTCAP 51 /* pre-draft HTCAP ie */
+#define BCM_OUI_HTINFO 52 /* pre-draft HTINFO ie */
#define WPA_OUI 0xf25000
#define WPA_OUI_TYPE 0x01
@@ -502,17 +800,11 @@
* octet status[2]
* octet chal.id
* octet chal.length
- * octet chal.text[253]
+ * octet chal.text[253] NB: 1-253 bytes
*/
-typedef u_int8_t *ieee80211_mgt_auth_t;
-
-#define IEEE80211_AUTH_ALGORITHM(auth) \
- ((auth)[0] | ((auth)[1] << 8))
-#define IEEE80211_AUTH_TRANSACTION(auth) \
- ((auth)[2] | ((auth)[3] << 8))
-#define IEEE80211_AUTH_STATUS(auth) \
- ((auth)[4] | ((auth)[5] << 8))
+/* challenge length for shared key auth */
+#define IEEE80211_CHALLENGE_LEN 128
#define IEEE80211_AUTH_ALG_OPEN 0x0000
#define IEEE80211_AUTH_ALG_SHARED 0x0001
@@ -531,7 +823,11 @@
};
/*
- * Reason codes
+ * Reason and status codes.
+ *
+ * Reason codes are used in management frames to indicate why an
+ * action took place (e.g. on disassociation). Status codes are
+ * used in management frames to indicate the result of an operation.
*
* Unlisted codes are reserved
*/
@@ -546,11 +842,20 @@
IEEE80211_REASON_NOT_ASSOCED = 7,
IEEE80211_REASON_ASSOC_LEAVE = 8,
IEEE80211_REASON_ASSOC_NOT_AUTHED = 9,
-
- IEEE80211_REASON_RSN_REQUIRED = 11,
- IEEE80211_REASON_RSN_INCONSISTENT = 12,
- IEEE80211_REASON_IE_INVALID = 13,
- IEEE80211_REASON_MIC_FAILURE = 14,
+ IEEE80211_REASON_DISASSOC_PWRCAP_BAD = 10, /* 11h */
+ IEEE80211_REASON_DISASSOC_SUPCHAN_BAD = 11, /* 11h */
+ IEEE80211_REASON_IE_INVALID = 13, /* 11i */
+ IEEE80211_REASON_MIC_FAILURE = 14, /* 11i */
+ IEEE80211_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, /* 11i */
+ IEEE80211_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, /* 11i */
+ IEEE80211_REASON_IE_IN_4WAY_DIFFERS = 17, /* 11i */
+ IEEE80211_REASON_GROUP_CIPHER_INVALID = 18, /* 11i */
+ IEEE80211_REASON_PAIRWISE_CIPHER_INVALID= 19, /* 11i */
+ IEEE80211_REASON_AKMP_INVALID = 20, /* 11i */
+ IEEE80211_REASON_UNSUPP_RSN_IE_VERSION = 21, /* 11i */
+ IEEE80211_REASON_INVALID_RSN_IE_CAP = 22, /* 11i */
+ IEEE80211_REASON_802_1X_AUTH_FAILED = 23, /* 11i */
+ IEEE80211_REASON_CIPHER_SUITE_REJECTED = 24, /* 11i */
IEEE80211_STATUS_SUCCESS = 0,
IEEE80211_STATUS_UNSPECIFIED = 1,
@@ -563,13 +868,21 @@
IEEE80211_STATUS_TIMEOUT = 16,
IEEE80211_STATUS_TOOMANY = 17,
IEEE80211_STATUS_BASIC_RATE = 18,
- IEEE80211_STATUS_SP_REQUIRED = 19,
- IEEE80211_STATUS_PBCC_REQUIRED = 20,
- IEEE80211_STATUS_CA_REQUIRED = 21,
- IEEE80211_STATUS_TOO_MANY_STATIONS = 22,
- IEEE80211_STATUS_RATES = 23,
- IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25,
- IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26,
+ IEEE80211_STATUS_SP_REQUIRED = 19, /* 11b */
+ IEEE80211_STATUS_PBCC_REQUIRED = 20, /* 11b */
+ IEEE80211_STATUS_CA_REQUIRED = 21, /* 11b */
+ IEEE80211_STATUS_SPECMGMT_REQUIRED = 22, /* 11h */
+ IEEE80211_STATUS_PWRCAP_REQUIRED = 23, /* 11h */
+ IEEE80211_STATUS_SUPCHAN_REQUIRED = 24, /* 11h */
+ IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, /* 11g */
+ IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, /* 11g */
+ IEEE80211_STATUS_INVALID_IE = 40, /* 11i */
+ IEEE80211_STATUS_GROUP_CIPHER_INVALID = 41, /* 11i */
+ IEEE80211_STATUS_PAIRWISE_CIPHER_INVALID = 42, /* 11i */
+ IEEE80211_STATUS_AKMP_INVALID = 43, /* 11i */
+ IEEE80211_STATUS_UNSUPP_RSN_IE_VERSION = 44, /* 11i */
+ IEEE80211_STATUS_INVALID_RSN_IE_CAP = 45, /* 11i */
+ IEEE80211_STATUS_CIPHER_SUITE_REJECTED = 46, /* 11i */
};
#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
@@ -636,4 +949,93 @@
#define IEEE80211_FRAG_MIN 256
#define IEEE80211_FRAG_MAX 2346
+/*
+ * Beacon interval (TU's). Min+max come from WiFi requirements.
+ * As above, we treat default as implementation-dependent so
+ * define it elsewhere.
+ */
+#define IEEE80211_BINTVAL_MAX 1000 /* max beacon interval (TU's) */
+#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval (TU's) */
+
+/*
+ * DTIM period (beacons). Min+max are not really defined
+ * by the protocol but we want them publicly visible so
+ * define them here.
+ */
+#define IEEE80211_DTIM_MAX 15 /* max DTIM period */
+#define IEEE80211_DTIM_MIN 1 /* min DTIM period */
+
+/*
+ * Beacon miss threshold (beacons). As for DTIM, we define
+ * them here to be publicly visible. Note the max may be
+ * clamped depending on device capabilities.
+ */
+#define IEEE80211_HWBMISS_MIN 1
+#define IEEE80211_HWBMISS_MAX 255
+
+/*
+ * 802.11 frame duration definitions.
+ */
+
+struct ieee80211_duration {
+ uint16_t d_rts_dur;
+ uint16_t d_data_dur;
+ uint16_t d_plcp_len;
+ uint8_t d_residue; /* unused octets in time slot */
+};
+
+/* One Time Unit (TU) is 1Kus = 1024 microseconds. */
+#define IEEE80211_DUR_TU 1024
+
+/* IEEE 802.11b durations for DSSS PHY in microseconds */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE 144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72
+
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48
+#define IEEE80211_DUR_DS_FAST_PLCPHDR 24
+#define IEEE80211_DUR_DS_SLOW_ACK 112
+#define IEEE80211_DUR_DS_FAST_ACK 56
+#define IEEE80211_DUR_DS_SLOW_CTS 112
+#define IEEE80211_DUR_DS_FAST_CTS 56
+
+#define IEEE80211_DUR_DS_SLOT 20
+#define IEEE80211_DUR_DS_SIFS 10
+#define IEEE80211_DUR_DS_PIFS (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SLOT)
+#define IEEE80211_DUR_DS_DIFS (IEEE80211_DUR_DS_SIFS + \
+ 2 * IEEE80211_DUR_DS_SLOT)
+#define IEEE80211_DUR_DS_EIFS (IEEE80211_DUR_DS_SIFS + \
+ IEEE80211_DUR_DS_SLOW_ACK + \
+ IEEE80211_DUR_DS_LONG_PREAMBLE + \
+ IEEE80211_DUR_DS_SLOW_PLCPHDR + \
+ IEEE80211_DUR_DIFS)
+
+/*
+ * Atheros fast-frame encapsulation format.
+ * FF max payload:
+ * 802.2 + FFHDR + HPAD + 802.3 + 802.2 + 1500 + SPAD + 802.3 + 802.2 + 1500:
+ * 8 + 4 + 4 + 14 + 8 + 1500 + 6 + 14 + 8 + 1500
+ * = 3066
+ */
+/* fast frame header is 32-bits */
+#define ATH_FF_PROTO 0x0000003f /* protocol */
+#define ATH_FF_PROTO_S 0
+#define ATH_FF_FTYPE 0x000000c0 /* frame type */
+#define ATH_FF_FTYPE_S 6
+#define ATH_FF_HLEN32 0x00000300 /* optional hdr length */
+#define ATH_FF_HLEN32_S 8
+#define ATH_FF_SEQNUM 0x001ffc00 /* sequence number */
+#define ATH_FF_SEQNUM_S 10
+#define ATH_FF_OFFSET 0xffe00000 /* offset to 2nd payload */
+#define ATH_FF_OFFSET_S 21
+
+#define ATH_FF_MAX_HDR_PAD 4
+#define ATH_FF_MAX_SEP_PAD 6
+#define ATH_FF_MAX_HDR 30
+
+#define ATH_FF_PROTO_L2TUNNEL 0 /* L2 tunnel protocol */
+#define ATH_FF_ETH_TYPE 0x88bd /* Ether type for encapsulated frames */
+#define ATH_FF_SNAP_ORGCODE_0 0x00
+#define ATH_FF_SNAP_ORGCODE_1 0x03
+#define ATH_FF_SNAP_ORGCODE_2 0x7f
+
#endif /* _NET80211_IEEE80211_H_ */
--- /dev/null
+++ sys/net80211/ieee80211_amrr.c
@@ -0,0 +1,164 @@
+/* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini at free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_amrr.c,v 1.3 2007/06/11 03:36:54 sam Exp $");
+
+/*-
+ * Naive implementation of the Adaptive Multi Rate Retry algorithm:
+ *
+ * "IEEE 802.11 Rate Adaptation: A Practical Approach"
+ * Mathieu Lacage, Hossein Manshaei, Thierry Turletti
+ * INRIA Sophia - Projet Planete
+ * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
+
+#define is_success(amn) \
+ ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
+#define is_failure(amn) \
+ ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
+#define is_enough(amn) \
+ ((amn)->amn_txcnt > 10)
+#define is_min_rate(ni) \
+ ((ni)->ni_txrate == 0)
+#define is_max_rate(ni) \
+ ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1)
+#define increase_rate(ni) \
+ ((ni)->ni_txrate++)
+#define decrease_rate(ni) \
+ ((ni)->ni_txrate--)
+#define reset_cnt(amn) \
+ do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0)
+
+void
+ieee80211_amrr_init(struct ieee80211_amrr *amrr,
+ struct ieee80211com *ic, int amin, int amax)
+{
+ /* XXX bounds check? */
+ amrr->amrr_min_success_threshold = amin;
+ amrr->amrr_max_success_threshold = amax;
+ amrr->amrr_ic = ic;
+}
+
+void
+ieee80211_amrr_node_init(struct ieee80211_amrr *amrr,
+ struct ieee80211_amrr_node *amn)
+{
+ amn->amn_success = 0;
+ amn->amn_recovery = 0;
+ amn->amn_txcnt = amn->amn_retrycnt = 0;
+ amn->amn_success_threshold = amrr->amrr_min_success_threshold;
+}
+
+/*
+ * Update ni->ni_txrate.
+ */
+void
+ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
+ struct ieee80211_amrr_node *amn)
+{
+ int need_change = 0;
+
+ if (is_success(amn) && is_enough(amn)) {
+ amn->amn_success++;
+ if (amn->amn_success >= amn->amn_success_threshold &&
+ !is_max_rate(ni)) {
+ amn->amn_recovery = 1;
+ amn->amn_success = 0;
+ increase_rate(ni);
+ IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
+ "AMRR increasing rate %d (txcnt=%d "
+ "retrycnt=%d)\n",
+ ni->ni_rates.rs_rates[ni->ni_txrate] &
+ IEEE80211_RATE_VAL,
+ amn->amn_txcnt, amn->amn_retrycnt);
+ need_change = 1;
+ } else {
+ amn->amn_recovery = 0;
+ }
+ } else if (is_failure(amn)) {
+ amn->amn_success = 0;
+ if (!is_min_rate(ni)) {
+ if (amn->amn_recovery) {
+ amn->amn_success_threshold *= 2;
+ if (amn->amn_success_threshold >
+ amrr->amrr_max_success_threshold)
+ amn->amn_success_threshold =
+ amrr->amrr_max_success_threshold;
+ } else {
+ amn->amn_success_threshold =
+ amrr->amrr_min_success_threshold;
+ }
+ decrease_rate(ni);
+ IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
+ "AMRR decreasing rate %d (txcnt=%d "
+ "retrycnt=%d)\n",
+ ni->ni_rates.rs_rates[ni->ni_txrate] &
+ IEEE80211_RATE_VAL,
+ amn->amn_txcnt, amn->amn_retrycnt);
+ need_change = 1;
+ }
+ amn->amn_recovery = 0;
+ }
+
+ if (is_enough(amn) || need_change)
+ reset_cnt(amn);
+}
+
+/*
+ * Module glue.
+ */
+static int
+amrr_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ if (bootverbose)
+ printf("wlan_amrr: <AMRR Transmit Rate Control Algorithm>\n");
+ return 0;
+ case MOD_UNLOAD:
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t amrr_mod = {
+ "wlan_amrr",
+ amrr_modevent,
+ 0
+};
+DECLARE_MODULE(wlan_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_amrr, 1);
+MODULE_DEPEND(wlan_amrr, wlan, 1, 1, 1);
Index: xform.h
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/xform.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/xform.h -L sys/opencrypto/xform.h -u -r1.1.1.1 -r1.2
--- sys/opencrypto/xform.h
+++ sys/opencrypto/xform.h
@@ -1,4 +1,4 @@
-/* $FreeBSD: src/sys/opencrypto/xform.h,v 1.2 2005/01/07 02:29:16 imp Exp $ */
+/* $FreeBSD: src/sys/opencrypto/xform.h,v 1.4 2007/05/09 19:37:02 gnn Exp $ */
/* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */
/*-
@@ -36,7 +36,7 @@
char *name;
u_int16_t keysize;
u_int16_t hashsize;
- u_int16_t authsize;
+ u_int16_t blocksize;
u_int16_t ctxsize;
void (*Init) (void *);
int (*Update) (void *, u_int8_t *, u_int16_t);
@@ -81,13 +81,14 @@
extern struct enc_xform enc_xform_skipjack;
extern struct enc_xform enc_xform_rijndael128;
extern struct enc_xform enc_xform_arc4;
+extern struct enc_xform enc_xform_camellia;
extern struct auth_hash auth_hash_null;
extern struct auth_hash auth_hash_key_md5;
extern struct auth_hash auth_hash_key_sha1;
-extern struct auth_hash auth_hash_hmac_md5_96;
-extern struct auth_hash auth_hash_hmac_sha1_96;
-extern struct auth_hash auth_hash_hmac_ripemd_160_96;
+extern struct auth_hash auth_hash_hmac_md5;
+extern struct auth_hash auth_hash_hmac_sha1;
+extern struct auth_hash auth_hash_hmac_ripemd_160;
extern struct auth_hash auth_hash_hmac_sha2_256;
extern struct auth_hash auth_hash_hmac_sha2_384;
extern struct auth_hash auth_hash_hmac_sha2_512;
Index: cryptosoft.h
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptosoft.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cryptosoft.h -L sys/opencrypto/cryptosoft.h -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cryptosoft.h
+++ sys/opencrypto/cryptosoft.h
@@ -1,4 +1,4 @@
-/* $FreeBSD: src/sys/opencrypto/cryptosoft.h,v 1.2 2005/01/07 02:29:16 imp Exp $ */
+/* $FreeBSD: src/sys/opencrypto/cryptosoft.h,v 1.4 2007/03/21 03:42:51 sam Exp $ */
/* $OpenBSD: cryptosoft.h,v 1.10 2002/04/22 23:10:09 deraadt Exp $ */
/*-
@@ -32,7 +32,8 @@
struct {
u_int8_t *SW_ictx;
u_int8_t *SW_octx;
- u_int32_t SW_klen;
+ u_int16_t SW_klen;
+ u_int16_t SW_mlen;
struct auth_hash *SW_axf;
} SWCR_AUTH;
struct {
@@ -48,6 +49,7 @@
#define sw_ictx SWCR_UN.SWCR_AUTH.SW_ictx
#define sw_octx SWCR_UN.SWCR_AUTH.SW_octx
#define sw_klen SWCR_UN.SWCR_AUTH.SW_klen
+#define sw_mlen SWCR_UN.SWCR_AUTH.SW_mlen
#define sw_axf SWCR_UN.SWCR_AUTH.SW_axf
#define sw_kschedule SWCR_UN.SWCR_ENC.SW_kschedule
#define sw_exf SWCR_UN.SWCR_ENC.SW_exf
@@ -58,8 +60,8 @@
};
#ifdef _KERNEL
-extern u_int8_t hmac_ipad_buffer[64];
-extern u_int8_t hmac_opad_buffer[64];
+extern u_int8_t hmac_ipad_buffer[];
+extern u_int8_t hmac_opad_buffer[];
#endif /* _KERNEL */
#endif /* _CRYPTO_CRYPTO_H_ */
Index: cryptodev.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptodev.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/opencrypto/cryptodev.c -L sys/opencrypto/cryptodev.c -u -r1.2 -r1.3
--- sys/opencrypto/cryptodev.c
+++ sys/opencrypto/cryptodev.c
@@ -2,6 +2,7 @@
/*-
* Copyright (c) 2001 Theo de Raadt
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,7 +33,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.25.2.2 2006/03/01 21:40:14 wkoszek Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.35 2007/10/08 20:08:34 kib Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -50,6 +51,7 @@
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/fcntl.h>
+#include <sys/bus.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/xform.h>
@@ -71,7 +73,6 @@
caddr_t mackey;
int mackeylen;
- u_char tmp_mac[CRYPTO_MAX_MAC_LEN];
struct iovec iovec;
struct uio uio;
@@ -114,6 +115,7 @@
static int cryptodev_op(struct csession *, struct crypt_op *,
struct ucred *, struct thread *td);
static int cryptodev_key(struct crypt_kop *);
+static int cryptodev_find(struct crypt_find_op *);
static int
cryptof_rw(
@@ -127,6 +129,22 @@
return (EIO);
}
+/*
+ * Check a crypto identifier to see if it requested
+ * a software device/driver. This can be done either
+ * by device name/class or through search constraints.
+ */
+static int
+checkforsoftware(int crid)
+{
+ if (crid & CRYPTOCAP_F_SOFTWARE)
+ return EINVAL; /* XXX */
+ if ((crid & CRYPTOCAP_F_HARDWARE) == 0 &&
+ (crypto_getcaps(crid) & CRYPTOCAP_F_HARDWARE) == 0)
+ return EINVAL; /* XXX */
+ return 0;
+}
+
/* ARGSUSED */
static int
cryptof_ioctl(
@@ -136,6 +154,7 @@
struct ucred *active_cred,
struct thread *td)
{
+#define SES2(p) ((struct session2_op *)p)
struct cryptoini cria, crie;
struct fcrypt *fcr = fp->f_data;
struct csession *cse;
@@ -143,16 +162,14 @@
struct crypt_op *cop;
struct enc_xform *txform = NULL;
struct auth_hash *thash = NULL;
+ struct crypt_kop *kop;
u_int64_t sid;
u_int32_t ses;
- int error = 0;
+ int error = 0, crid;
- /*
- * XXX: Not sure Giant is needed, but better safe than sorry
- */
- mtx_lock(&Giant);
switch (cmd) {
case CIOCGSESSION:
+ case CIOCGSESSION2:
sop = (struct session_op *)data;
switch (sop->cipher) {
case 0:
@@ -181,8 +198,10 @@
case CRYPTO_ARC4:
txform = &enc_xform_arc4;
break;
+ case CRYPTO_CAMELLIA_CBC:
+ txform = &enc_xform_camellia;
+ break;
default:
- mtx_unlock(&Giant);
return (EINVAL);
}
@@ -190,25 +209,22 @@
case 0:
break;
case CRYPTO_MD5_HMAC:
- thash = &auth_hash_hmac_md5_96;
+ thash = &auth_hash_hmac_md5;
break;
case CRYPTO_SHA1_HMAC:
- thash = &auth_hash_hmac_sha1_96;
+ thash = &auth_hash_hmac_sha1;
break;
- case CRYPTO_SHA2_HMAC:
- if (sop->mackeylen == auth_hash_hmac_sha2_256.keysize)
- thash = &auth_hash_hmac_sha2_256;
- else if (sop->mackeylen == auth_hash_hmac_sha2_384.keysize)
- thash = &auth_hash_hmac_sha2_384;
- else if (sop->mackeylen == auth_hash_hmac_sha2_512.keysize)
- thash = &auth_hash_hmac_sha2_512;
- else {
- mtx_unlock(&Giant);
- return (EINVAL);
- }
+ case CRYPTO_SHA2_256_HMAC:
+ thash = &auth_hash_hmac_sha2_256;
+ break;
+ case CRYPTO_SHA2_384_HMAC:
+ thash = &auth_hash_hmac_sha2_384;
+ break;
+ case CRYPTO_SHA2_512_HMAC:
+ thash = &auth_hash_hmac_sha2_512;
break;
case CRYPTO_RIPEMD160_HMAC:
- thash = &auth_hash_hmac_ripemd_160_96;
+ thash = &auth_hash_hmac_ripemd_160;
break;
#ifdef notdef
case CRYPTO_MD5:
@@ -222,7 +238,6 @@
thash = &auth_hash_null;
break;
default:
- mtx_unlock(&Giant);
return (EINVAL);
}
@@ -264,7 +279,15 @@
}
}
- error = crypto_newsession(&sid, (txform ? &crie : &cria), 1);
+ /* NB: CIOGSESSION2 has the crid */
+ if (cmd == CIOCGSESSION2) {
+ crid = SES2(sop)->crid;
+ error = checkforsoftware(crid);
+ if (error)
+ goto bail;
+ } else
+ crid = CRYPTOCAP_F_HARDWARE;
+ error = crypto_newsession(&sid, (txform ? &crie : &cria), crid);
if (error)
goto bail;
@@ -278,7 +301,10 @@
goto bail;
}
sop->ses = cse->ses;
-
+ if (cmd == CIOCGSESSION2) {
+ /* return hardware/driver id */
+ SES2(sop)->crid = CRYPTO_SESID2HID(cse->sid);
+ }
bail:
if (error) {
if (crie.cri_key)
@@ -290,33 +316,53 @@
case CIOCFSESSION:
ses = *(u_int32_t *)data;
cse = csefind(fcr, ses);
- if (cse == NULL) {
- mtx_unlock(&Giant);
+ if (cse == NULL)
return (EINVAL);
- }
csedelete(fcr, cse);
error = csefree(cse);
break;
case CIOCCRYPT:
cop = (struct crypt_op *)data;
cse = csefind(fcr, cop->ses);
- if (cse == NULL) {
- mtx_unlock(&Giant);
+ if (cse == NULL)
return (EINVAL);
- }
error = cryptodev_op(cse, cop, active_cred, td);
break;
case CIOCKEY:
- error = cryptodev_key((struct crypt_kop *)data);
+ case CIOCKEY2:
+ if (!crypto_userasymcrypto)
+ return (EPERM); /* XXX compat? */
+ mtx_lock(&Giant);
+ kop = (struct crypt_kop *)data;
+ if (cmd == CIOCKEY) {
+ /* NB: crypto core enforces s/w driver use */
+ kop->crk_crid =
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+ }
+ error = cryptodev_key(kop);
+ mtx_unlock(&Giant);
break;
case CIOCASYMFEAT:
- error = crypto_getfeat((int *)data);
+ if (!crypto_userasymcrypto) {
+ /*
+ * NB: if user asym crypto operations are
+ * not permitted return "no algorithms"
+ * so well-behaved applications will just
+ * fallback to doing them in software.
+ */
+ *(int *)data = 0;
+ } else
+ error = crypto_getfeat((int *)data);
+ break;
+ case CIOCFINDDEV:
+ error = cryptodev_find((struct crypt_find_op *)data);
break;
default:
error = EINVAL;
+ break;
}
- mtx_unlock(&Giant);
return (error);
+#undef SES2
}
static int cryptodev_cb(void *);
@@ -349,7 +395,10 @@
cse->uio.uio_rw = UIO_WRITE;
cse->uio.uio_td = td;
cse->uio.uio_iov[0].iov_len = cop->len;
- cse->uio.uio_iov[0].iov_base = malloc(cop->len, M_XDATA, M_WAITOK);
+ if (cse->thash)
+ cse->uio.uio_iov[0].iov_len += cse->thash->hashsize;
+ cse->uio.uio_iov[0].iov_base = malloc(cse->uio.uio_iov[0].iov_len,
+ M_XDATA, M_WAITOK);
crp = crypto_getreq((cse->txform != NULL) + (cse->thash != NULL));
if (crp == NULL) {
@@ -376,7 +425,7 @@
if (crda) {
crda->crd_skip = 0;
crda->crd_len = cop->len;
- crda->crd_inject = 0; /* ??? */
+ crda->crd_inject = cop->len;
crda->crd_alg = cse->mac;
crda->crd_key = cse->mackey;
@@ -426,12 +475,9 @@
crde->crd_len -= cse->txform->blocksize;
}
- if (cop->mac) {
- if (crda == NULL) {
- error = EINVAL;
- goto bail;
- }
- crp->crp_mac=cse->tmp_mac;
+ if (cop->mac && crda == NULL) {
+ error = EINVAL;
+ goto bail;
}
/*
@@ -465,7 +511,8 @@
goto bail;
if (cop->mac &&
- (error = copyout(crp->crp_mac, cop->mac, cse->thash->authsize)))
+ (error = copyout((caddr_t)cse->uio.uio_iov[0].iov_base + cop->len,
+ cop->mac, cse->thash->hashsize)))
goto bail;
bail:
@@ -482,12 +529,16 @@
{
struct cryptop *crp = (struct cryptop *) op;
struct csession *cse = (struct csession *)crp->crp_opaque;
+ int error;
- cse->error = crp->crp_etype;
- if (crp->crp_etype == EAGAIN)
- return crypto_dispatch(crp);
+ error = crp->crp_etype;
+ if (error == EAGAIN)
+ error = crypto_dispatch(crp);
mtx_lock(&cse->lock);
- wakeup_one(crp);
+ if (error != 0 || (crp->crp_flags & CRYPTO_F_DONE)) {
+ cse->error = error;
+ wakeup_one(crp);
+ }
mtx_unlock(&cse->lock);
return (0);
}
@@ -497,7 +548,7 @@
{
struct cryptkop *krp = (struct cryptkop *) op;
- wakeup(krp);
+ wakeup_one(krp);
return (0);
}
@@ -539,19 +590,23 @@
return (EINVAL);
}
- krp = (struct cryptkop *)malloc(sizeof *krp, M_XDATA, M_WAITOK);
+ krp = (struct cryptkop *)malloc(sizeof *krp, M_XDATA, M_WAITOK|M_ZERO);
if (!krp)
return (ENOMEM);
- bzero(krp, sizeof *krp);
krp->krp_op = kop->crk_op;
krp->krp_status = kop->crk_status;
krp->krp_iparams = kop->crk_iparams;
krp->krp_oparams = kop->crk_oparams;
+ krp->krp_crid = kop->crk_crid;
krp->krp_status = 0;
krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb;
- for (i = 0; i < CRK_MAXPARAM; i++)
+ for (i = 0; i < CRK_MAXPARAM; i++) {
+ if (kop->crk_param[i].crp_nbits > 65536)
+ /* Limit is the same as in OpenBSD */
+ goto fail;
krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits;
+ }
for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) {
size = (krp->krp_param[i].crp_nbits + 7) / 8;
if (size == 0)
@@ -573,6 +628,7 @@
goto fail;
}
+ kop->crk_crid = krp->krp_crid; /* device that did the work */
if (krp->krp_status != 0) {
error = krp->krp_status;
goto fail;
@@ -599,6 +655,25 @@
return (error);
}
+static int
+cryptodev_find(struct crypt_find_op *find)
+{
+ device_t dev;
+
+ if (find->crid != -1) {
+ dev = crypto_find_device_byhid(find->crid);
+ if (dev == NULL)
+ return (ENOENT);
+ strlcpy(find->name, device_get_nameunit(dev),
+ sizeof(find->name));
+ } else {
+ find->crid = crypto_find_driver(find->name);
+ if (find->crid == -1)
+ return (ENOENT);
+ }
+ return (0);
+}
+
/* ARGSUSED */
static int
cryptof_poll(
@@ -765,13 +840,21 @@
return (error);
}
/* falloc automatically provides an extra reference to 'f'. */
+ FILE_LOCK(f);
f->f_flag = FREAD | FWRITE;
f->f_type = DTYPE_CRYPTO;
- f->f_ops = &cryptofops;
f->f_data = fcr;
+ f->f_ops = &cryptofops;
+ FILE_UNLOCK(f);
*(u_int32_t *)data = fd;
fdrop(f, td);
break;
+ case CRIOFINDDEV:
+ error = cryptodev_find((struct crypt_find_op *)data);
+ break;
+ case CRIOASYMFEAT:
+ error = crypto_getfeat((int *)data);
+ break;
default:
error = EINVAL;
break;
--- /dev/null
+++ sys/opencrypto/cryptodev_if.m
@@ -0,0 +1,55 @@
+#-
+# Copyright (c) 2006, Sam Leffler
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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: src/sys/opencrypto/cryptodev_if.m,v 1.1 2007/03/21 03:43:33 sam Exp $
+#
+
+#include <sys/malloc.h>
+#include <opencrypto/cryptodev.h>
+
+INTERFACE cryptodev;
+
+METHOD int newsession {
+ device_t dev;
+ uint32_t *sid;
+ struct cryptoini *cri;
+};
+
+METHOD int freesession {
+ device_t dev;
+ uint64_t sid;
+};
+
+METHOD int process {
+ device_t dev;
+ struct cryptop *op;
+ int flags;
+};
+
+METHOD int kprocess {
+ device_t dev;
+ struct cryptkop *op;
+ int flags;
+};
Index: cryptodev.h
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptodev.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cryptodev.h -L sys/opencrypto/cryptodev.h -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cryptodev.h
+++ sys/opencrypto/cryptodev.h
@@ -1,8 +1,9 @@
-/* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.10 2005/01/07 02:29:16 imp Exp $ */
+/* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.25 2007/05/09 19:37:02 gnn Exp $ */
/* $OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $ */
/*-
* The author of this code is Angelos D. Keromytis (angelos at cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
*
* This code was written by Angelos D. Keromytis in Athens, Greece, in
* February 2000. Network Security Technologies Inc. (NSTI) kindly
@@ -61,22 +62,43 @@
#define CRYPTO_DRIVERS_INITIAL 4
#define CRYPTO_SW_SESSIONS 32
+/* Hash values */
+#define NULL_HASH_LEN 16
+#define MD5_HASH_LEN 16
+#define SHA1_HASH_LEN 20
+#define RIPEMD160_HASH_LEN 20
+#define SHA2_256_HASH_LEN 32
+#define SHA2_384_HASH_LEN 48
+#define SHA2_512_HASH_LEN 64
+#define MD5_KPDK_HASH_LEN 16
+#define SHA1_KPDK_HASH_LEN 20
+/* Maximum hash algorithm result length */
+#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */
+
/* HMAC values */
-#define HMAC_BLOCK_LEN 64
-#define HMAC_IPAD_VAL 0x36
-#define HMAC_OPAD_VAL 0x5C
+#define NULL_HMAC_BLOCK_LEN 64
+#define MD5_HMAC_BLOCK_LEN 64
+#define SHA1_HMAC_BLOCK_LEN 64
+#define RIPEMD160_HMAC_BLOCK_LEN 64
+#define SHA2_256_HMAC_BLOCK_LEN 64
+#define SHA2_384_HMAC_BLOCK_LEN 128
+#define SHA2_512_HMAC_BLOCK_LEN 128
+/* Maximum HMAC block length */
+#define HMAC_MAX_BLOCK_LEN SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */
+#define HMAC_IPAD_VAL 0x36
+#define HMAC_OPAD_VAL 0x5C
/* Encryption algorithm block sizes */
+#define NULL_BLOCK_LEN 4
#define DES_BLOCK_LEN 8
#define DES3_BLOCK_LEN 8
#define BLOWFISH_BLOCK_LEN 8
#define SKIPJACK_BLOCK_LEN 8
#define CAST128_BLOCK_LEN 8
#define RIJNDAEL128_BLOCK_LEN 16
-#define EALG_MAX_BLOCK_LEN 16 /* Keep this updated */
-
-/* Maximum hash algorithm result length */
-#define AALG_MAX_RESULT_LEN 64 /* Keep this updated */
+#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN
+#define CAMELLIA_BLOCK_LEN 16
+#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */
#define CRYPTO_ALGORITHM_MIN 1
#define CRYPTO_DES_CBC 1
@@ -94,17 +116,32 @@
#define CRYPTO_ARC4 12
#define CRYPTO_MD5 13
#define CRYPTO_SHA1 14
-#define CRYPTO_SHA2_HMAC 15
-#define CRYPTO_NULL_HMAC 16
-#define CRYPTO_NULL_CBC 17
-#define CRYPTO_DEFLATE_COMP 18 /* Deflate compression algorithm */
-#define CRYPTO_ALGORITHM_MAX 18 /* Keep updated - see below */
+#define CRYPTO_NULL_HMAC 15
+#define CRYPTO_NULL_CBC 16
+#define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */
+#define CRYPTO_SHA2_256_HMAC 18
+#define CRYPTO_SHA2_384_HMAC 19
+#define CRYPTO_SHA2_512_HMAC 20
+#define CRYPTO_CAMELLIA_CBC 21
+#define CRYPTO_ALGORITHM_MAX 21 /* Keep updated - see below */
/* Algorithm flags */
#define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */
#define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */
#define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */
+/*
+ * Crypto driver/device flags. They can set in the crid
+ * parameter when creating a session or submitting a key
+ * op to affect the device/driver assigned. If neither
+ * of these are specified then the crid is assumed to hold
+ * the driver id of an existing (and suitable) device that
+ * must be used to satisfy the request.
+ */
+#define CRYPTO_FLAG_HARDWARE 0x01000000 /* hardware accelerated */
+#define CRYPTO_FLAG_SOFTWARE 0x02000000 /* software implementation */
+
+/* NB: deprecated */
struct session_op {
u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
@@ -117,6 +154,20 @@
u_int32_t ses; /* returns: session # */
};
+struct session2_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+ int crid; /* driver id + flags (rw) */
+ int pad[4]; /* for future expansion */
+};
+
struct crypt_op {
u_int32_t ses;
u_int16_t op; /* i.e. COP_ENCRYPT */
@@ -130,7 +181,15 @@
caddr_t iv;
};
-#define CRYPTO_MAX_MAC_LEN 20
+/*
+ * Parameters for looking up a crypto driver/device by
+ * device name or by id. The latter are returned for
+ * created sessions (crid) and completed key operations.
+ */
+struct crypt_find_op {
+ int crid; /* driver id + flags */
+ char name[32]; /* device/driver name */
+};
/* bignum parameter, in packed bytes, ... */
struct crparam {
@@ -145,7 +204,7 @@
u_int crk_status; /* return status */
u_short crk_iparams; /* # of input parameters */
u_short crk_oparams; /* # of output parameters */
- u_int crk_pad1;
+ u_int crk_crid; /* NB: only used by CIOCKEY2 (rw) */
struct crparam crk_param[CRK_MAXPARAM];
};
#define CRK_ALGORITM_MIN 0
@@ -167,14 +226,18 @@
* Please use F_SETFD against the cloned descriptor.
*/
#define CRIOGET _IOWR('c', 100, u_int32_t)
+#define CRIOASYMFEAT CIOCASYMFEAT
+#define CRIOFINDDEV CIOCFINDDEV
/* the following are done against the cloned descriptor */
#define CIOCGSESSION _IOWR('c', 101, struct session_op)
#define CIOCFSESSION _IOW('c', 102, u_int32_t)
#define CIOCCRYPT _IOWR('c', 103, struct crypt_op)
#define CIOCKEY _IOWR('c', 104, struct crypt_kop)
-
#define CIOCASYMFEAT _IOR('c', 105, u_int32_t)
+#define CIOCGSESSION2 _IOWR('c', 106, struct session2_op)
+#define CIOCKEY2 _IOWR('c', 107, struct crypt_kop)
+#define CIOCFINDDEV _IOWR('c', 108, struct crypt_find_op)
struct cryptotstat {
struct timespec acc; /* total accumulated time */
@@ -209,7 +272,8 @@
struct cryptoini {
int cri_alg; /* Algorithm to use */
int cri_klen; /* Key length, in bits */
- int cri_rnd; /* Algorithm rounds, where relevant */
+ int cri_mlen; /* Number of bytes we want from the
+ entire hash. 0 means all. */
caddr_t cri_key; /* key to use */
u_int8_t cri_iv[EALG_MAX_BLOCK_LEN]; /* IV to use */
struct cryptoini *cri_next;
@@ -233,7 +297,6 @@
struct cryptoini CRD_INI; /* Initialization/context data */
#define crd_iv CRD_INI.cri_iv
#define crd_key CRD_INI.cri_key
-#define crd_rnd CRD_INI.cri_rnd
#define crd_alg CRD_INI.cri_alg
#define crd_klen CRD_INI.cri_klen
@@ -274,7 +337,6 @@
int (*crp_callback)(struct cryptop *); /* Callback function */
- caddr_t crp_mac;
struct bintime crp_tstamp; /* performance time stamp */
};
@@ -297,40 +359,12 @@
u_int krp_status; /* return status */
u_short krp_iparams; /* # of input parameters */
u_short krp_oparams; /* # of output parameters */
+ u_int krp_crid; /* desired device, etc. */
u_int32_t krp_hid;
struct crparam krp_param[CRK_MAXPARAM]; /* kvm */
int (*krp_callback)(struct cryptkop *);
};
-/* Crypto capabilities structure */
-struct cryptocap {
- u_int32_t cc_sessions;
-
- /*
- * Largest possible operator length (in bits) for each type of
- * encryption algorithm.
- */
- u_int16_t cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
-
- u_int8_t cc_alg[CRYPTO_ALGORITHM_MAX + 1];
-
- u_int8_t cc_kalg[CRK_ALGORITHM_MAX + 1];
-
- u_int8_t cc_flags;
- u_int8_t cc_qblocked; /* symmetric q blocked */
- u_int8_t cc_kqblocked; /* asymmetric q blocked */
-#define CRYPTOCAP_F_CLEANUP 0x01 /* needs resource cleanup */
-#define CRYPTOCAP_F_SOFTWARE 0x02 /* software implementation */
-#define CRYPTOCAP_F_SYNC 0x04 /* operates synchronously */
-
- void *cc_arg; /* callback argument */
- int (*cc_newsession)(void*, u_int32_t*, struct cryptoini*);
- int (*cc_process)(void*, struct cryptop *, int);
- int (*cc_freesession)(void*, u_int64_t);
- void *cc_karg; /* callback argument */
- int (*cc_kprocess) (void*, struct cryptkop *, int);
-};
-
/*
* Session ids are 64 bits. The lower 32 bits contain a "local id" which
* is a driver-private session identifier. The upper 32 bits contain a
@@ -338,24 +372,24 @@
* a copy of the driver's capabilities that can be used by client code to
* optimize operation.
*/
-#define CRYPTO_SESID2HID(_sid) (((_sid) >> 32) & 0xffffff)
-#define CRYPTO_SESID2CAPS(_sid) (((_sid) >> 56) & 0xff)
+#define CRYPTO_SESID2HID(_sid) (((_sid) >> 32) & 0x00ffffff)
+#define CRYPTO_SESID2CAPS(_sid) (((_sid) >> 32) & 0xff000000)
#define CRYPTO_SESID2LID(_sid) (((u_int32_t) (_sid)) & 0xffffffff)
MALLOC_DECLARE(M_CRYPTO_DATA);
extern int crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard);
extern int crypto_freesession(u_int64_t sid);
-extern int32_t crypto_get_driverid(u_int32_t flags);
+#define CRYPTOCAP_F_HARDWARE CRYPTO_FLAG_HARDWARE
+#define CRYPTOCAP_F_SOFTWARE CRYPTO_FLAG_SOFTWARE
+#define CRYPTOCAP_F_SYNC 0x04000000 /* operates synchronously */
+extern int32_t crypto_get_driverid(device_t dev, int flags);
+extern int crypto_find_driver(const char *);
+extern device_t crypto_find_device_byhid(int hid);
+extern int crypto_getcaps(int hid);
extern int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
- u_int32_t flags,
- int (*newses)(void*, u_int32_t*, struct cryptoini*),
- int (*freeses)(void*, u_int64_t),
- int (*process)(void*, struct cryptop *, int),
- void *arg);
-extern int crypto_kregister(u_int32_t, int, u_int32_t,
- int (*)(void*, struct cryptkop *, int),
- void *arg);
+ u_int32_t flags);
+extern int crypto_kregister(u_int32_t, int, u_int32_t);
extern int crypto_unregister(u_int32_t driverid, int alg);
extern int crypto_unregister_all(u_int32_t driverid);
extern int crypto_dispatch(struct cryptop *crp);
@@ -384,5 +418,14 @@
extern void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp);
extern void cuio_copyback(struct uio* uio, int off, int len, caddr_t cp);
extern struct iovec *cuio_getptr(struct uio *uio, int loc, int *off);
+extern int cuio_apply(struct uio *uio, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg);
+
+extern void crypto_copyback(int flags, caddr_t buf, int off, int size,
+ caddr_t in);
+extern void crypto_copydata(int flags, caddr_t buf, int off, int size,
+ caddr_t out);
+extern int crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg);
#endif /* _KERNEL */
#endif /* _CRYPTO_CRYPTO_H_ */
Index: criov.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/criov.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/criov.c -L sys/opencrypto/criov.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/criov.c
+++ sys/opencrypto/criov.c
@@ -28,7 +28,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.3 2005/01/07 02:29:16 imp Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.5 2006/06/04 22:15:13 pjd Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -36,10 +36,28 @@
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/mbuf.h>
#include <sys/uio.h>
#include <opencrypto/cryptodev.h>
+/*
+ * This macro is only for avoiding code duplication, as we need to skip
+ * given number of bytes in the same way in three functions below.
+ */
+#define CUIO_SKIP() do { \
+ KASSERT(off >= 0, ("%s: off %d < 0", __func__, off)); \
+ KASSERT(len >= 0, ("%s: len %d < 0", __func__, len)); \
+ while (off > 0) { \
+ KASSERT(iol >= 0, ("%s: empty in skip", __func__)); \
+ if (off < iov->iov_len) \
+ break; \
+ off -= iov->iov_len; \
+ iol--; \
+ iov++; \
+ } \
+} while (0)
+
void
cuio_copydata(struct uio* uio, int off, int len, caddr_t cp)
{
@@ -47,22 +65,9 @@
int iol = uio->uio_iovcnt;
unsigned count;
- if (off < 0)
- panic("cuio_copydata: off %d < 0", off);
- if (len < 0)
- panic("cuio_copydata: len %d < 0", len);
- while (off > 0) {
- if (iol == 0)
- panic("iov_copydata: empty in skip");
- if (off < iov->iov_len)
- break;
- off -= iov->iov_len;
- iol--;
- iov++;
- }
+ CUIO_SKIP();
while (len > 0) {
- if (iol == 0)
- panic("cuio_copydata: empty");
+ KASSERT(iol >= 0, ("%s: empty", __func__));
count = min(iov->iov_len - off, len);
bcopy(((caddr_t)iov->iov_base) + off, cp, count);
len -= count;
@@ -80,22 +85,9 @@
int iol = uio->uio_iovcnt;
unsigned count;
- if (off < 0)
- panic("cuio_copyback: off %d < 0", off);
- if (len < 0)
- panic("cuio_copyback: len %d < 0", len);
- while (off > 0) {
- if (iol == 0)
- panic("cuio_copyback: empty in skip");
- if (off < iov->iov_len)
- break;
- off -= iov->iov_len;
- iol--;
- iov++;
- }
+ CUIO_SKIP();
while (len > 0) {
- if (iol == 0)
- panic("uio_copyback: empty");
+ KASSERT(iol >= 0, ("%s: empty", __func__));
count = min(iov->iov_len - off, len);
bcopy(cp, ((caddr_t)iov->iov_base) + off, count);
len -= count;
@@ -137,3 +129,70 @@
return (NULL);
}
+
+/*
+ * Apply function f to the data in an iovec list starting "off" bytes from
+ * the beginning, continuing for "len" bytes.
+ */
+int
+cuio_apply(struct uio *uio, int off, int len, int (*f)(void *, void *, u_int),
+ void *arg)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+ unsigned count;
+ int rval;
+
+ CUIO_SKIP();
+ while (len > 0) {
+ KASSERT(iol >= 0, ("%s: empty", __func__));
+ count = min(iov->iov_len - off, len);
+ rval = (*f)(arg, ((caddr_t)iov->iov_base) + off, count);
+ if (rval)
+ return (rval);
+ len -= count;
+ off = 0;
+ iol--;
+ iov++;
+ }
+ return (0);
+}
+
+void
+crypto_copyback(int flags, caddr_t buf, int off, int size, caddr_t in)
+{
+
+ if ((flags & CRYPTO_F_IMBUF) != 0)
+ m_copyback((struct mbuf *)buf, off, size, in);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ cuio_copyback((struct uio *)buf, off, size, in);
+ else
+ bcopy(in, buf + off, size);
+}
+
+void
+crypto_copydata(int flags, caddr_t buf, int off, int size, caddr_t out)
+{
+
+ if ((flags & CRYPTO_F_IMBUF) != 0)
+ m_copydata((struct mbuf *)buf, off, size, out);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ cuio_copydata((struct uio *)buf, off, size, out);
+ else
+ bcopy(buf + off, out, size);
+}
+
+int
+crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg)
+{
+ int error;
+
+ if ((flags & CRYPTO_F_IMBUF) != 0)
+ error = m_apply((struct mbuf *)buf, off, len, f, arg);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ error = cuio_apply((struct uio *)buf, off, len, f, arg);
+ else
+ error = (*f)(arg, buf + off, len);
+ return (error);
+}
Index: crypto.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/crypto.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/opencrypto/crypto.c -L sys/opencrypto/crypto.c -u -r1.2 -r1.3
--- sys/opencrypto/crypto.c
+++ sys/opencrypto/crypto.c
@@ -1,4 +1,38 @@
-/* $OpenBSD: crypto.c,v 1.38 2002/06/11 11:14:29 beck Exp $ */
+/*-
+ * Copyright (c) 2002-2006 Sam Leffler. 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.
+ *
+ * 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>
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.27 2007/03/21 03:42:51 sam Exp $");
+
+/*
+ * Cryptographic Subsystem.
+ *
+ * This code is derived from the Openbsd Cryptographic Framework (OCF)
+ * that has the copyright shown below. Very little of the original
+ * code remains.
+ */
+
/*-
* The author of this code is Angelos D. Keromytis (angelos at cis.upenn.edu)
*
@@ -20,11 +54,10 @@
* PURPOSE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.16.2.1 2006/03/05 00:48:05 wkoszek Exp $");
-
#define CRYPTO_TIMING /* enable timing support */
+#include "opt_ddb.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/eventhandler.h>
@@ -37,10 +70,16 @@
#include <sys/proc.h>
#include <sys/sysctl.h>
+#include <ddb/ddb.h>
+
#include <vm/uma.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/xform.h> /* XXX for M_XDATA */
+#include <sys/kobj.h>
+#include <sys/bus.h>
+#include "cryptodev_if.h"
+
/*
* Crypto drivers register themselves by allocating a slot in the
* crypto_drivers table with crypto_get_driverid() and then registering
@@ -49,6 +88,33 @@
static struct mtx crypto_drivers_mtx; /* lock on driver table */
#define CRYPTO_DRIVER_LOCK() mtx_lock(&crypto_drivers_mtx)
#define CRYPTO_DRIVER_UNLOCK() mtx_unlock(&crypto_drivers_mtx)
+#define CRYPTO_DRIVER_ASSERT() mtx_assert(&crypto_drivers_mtx, MA_OWNED)
+
+/*
+ * Crypto device/driver capabilities structure.
+ *
+ * Synchronization:
+ * (d) - protected by CRYPTO_DRIVER_LOCK()
+ * (q) - protected by CRYPTO_Q_LOCK()
+ * Not tagged fields are read-only.
+ */
+struct cryptocap {
+ device_t cc_dev; /* (d) device/driver */
+ u_int32_t cc_sessions; /* (d) # of sessions */
+ u_int32_t cc_koperations; /* (d) # os asym operations */
+ /*
+ * Largest possible operator length (in bits) for each type of
+ * encryption algorithm. XXX not used
+ */
+ u_int16_t cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
+ u_int8_t cc_alg[CRYPTO_ALGORITHM_MAX + 1];
+ u_int8_t cc_kalg[CRK_ALGORITHM_MAX + 1];
+
+ int cc_flags; /* (d) flags */
+#define CRYPTOCAP_F_CLEANUP 0x80000000 /* needs resource cleanup */
+ int cc_qblocked; /* (q) symmetric q blocked */
+ int cc_kqblocked; /* (q) asymmetric q blocked */
+};
static struct cryptocap *crypto_drivers = NULL;
static int crypto_drivers_num = 0;
@@ -59,6 +125,7 @@
* have one per-queue but having one simplifies handling of block/unblock
* operations.
*/
+static int crp_sleep = 0;
static TAILQ_HEAD(,cryptop) crp_q; /* request queues */
static TAILQ_HEAD(,cryptkop) crp_kq;
static struct mtx crypto_q_mtx;
@@ -78,6 +145,7 @@
static struct mtx crypto_ret_q_mtx;
#define CRYPTO_RETQ_LOCK() mtx_lock(&crypto_ret_q_mtx)
#define CRYPTO_RETQ_UNLOCK() mtx_unlock(&crypto_ret_q_mtx)
+#define CRYPTO_RETQ_EMPTY() (TAILQ_EMPTY(&crp_ret_q) && TAILQ_EMPTY(&crp_ret_kq))
static uma_zone_t cryptop_zone;
static uma_zone_t cryptodesc_zone;
@@ -98,8 +166,8 @@
static void crypto_ret_proc(void);
static struct proc *cryptoretproc;
static void crypto_destroy(void);
-static int crypto_invoke(struct cryptop *crp, int hint);
-static int crypto_kinvoke(struct cryptkop *krp, int hint);
+static int crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint);
+static int crypto_kinvoke(struct cryptkop *krp, int flags);
static struct cryptostats cryptostats;
SYSCTL_STRUCT(_kern, OID_AUTO, crypto_stats, CTLFLAG_RW, &cryptostats,
@@ -222,112 +290,146 @@
mtx_destroy(&crypto_drivers_mtx);
}
+static struct cryptocap *
+crypto_checkdriver(u_int32_t hid)
+{
+ if (crypto_drivers == NULL)
+ return NULL;
+ return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
+}
+
/*
- * Initialization code, both for static and dynamic loading.
+ * Compare a driver's list of supported algorithms against another
+ * list; return non-zero if all algorithms are supported.
*/
static int
-crypto_modevent(module_t mod, int type, void *unused)
+driver_suitable(const struct cryptocap *cap, const struct cryptoini *cri)
{
- int error = EINVAL;
+ const struct cryptoini *cr;
- switch (type) {
- case MOD_LOAD:
- error = crypto_init();
- if (error == 0 && bootverbose)
- printf("crypto: <crypto core>\n");
- break;
- case MOD_UNLOAD:
- /*XXX disallow if active sessions */
- error = 0;
- crypto_destroy();
- return 0;
- }
- return error;
+ /* See if all the algorithms are supported. */
+ for (cr = cri; cr; cr = cr->cri_next)
+ if (cap->cc_alg[cr->cri_alg] == 0)
+ return 0;
+ return 1;
}
-static moduledata_t crypto_mod = {
- "crypto",
- crypto_modevent,
- 0
-};
-MODULE_VERSION(crypto, 1);
-DECLARE_MODULE(crypto, crypto_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_DEPEND(crypto, zlib, 1, 1, 1);
-
/*
- * Create a new session.
+ * Select a driver for a new session that supports the specified
+ * algorithms and, optionally, is constrained according to the flags.
+ * The algorithm we use here is pretty stupid; just use the
+ * first driver that supports all the algorithms we need. If there
+ * are multiple drivers we choose the driver with the fewest active
+ * sessions. We prefer hardware-backed drivers to software ones.
+ *
+ * XXX We need more smarts here (in real life too, but that's
+ * XXX another story altogether).
*/
-int
-crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
+static struct cryptocap *
+crypto_select_driver(const struct cryptoini *cri, int flags)
{
- struct cryptoini *cr;
- u_int32_t hid, lid;
- int err = EINVAL;
+ struct cryptocap *cap, *best;
+ int match, hid;
- CRYPTO_DRIVER_LOCK();
-
- if (crypto_drivers == NULL)
- goto done;
+ CRYPTO_DRIVER_ASSERT();
/*
- * The algorithm we use here is pretty stupid; just use the
- * first driver that supports all the algorithms we need.
- *
- * XXX We need more smarts here (in real life too, but that's
- * XXX another story altogether).
+ * Look first for hardware crypto devices if permitted.
*/
-
+ if (flags & CRYPTOCAP_F_HARDWARE)
+ match = CRYPTOCAP_F_HARDWARE;
+ else
+ match = CRYPTOCAP_F_SOFTWARE;
+ best = NULL;
+again:
for (hid = 0; hid < crypto_drivers_num; hid++) {
- struct cryptocap *cap = &crypto_drivers[hid];
+ cap = &crypto_drivers[hid];
/*
- * If it's not initialized or has remaining sessions
- * referencing it, skip.
+ * If it's not initialized, is in the process of
+ * going away, or is not appropriate (hardware
+ * or software based on match), then skip.
*/
- if (cap->cc_newsession == NULL ||
- (cap->cc_flags & CRYPTOCAP_F_CLEANUP))
+ if (cap->cc_dev == NULL ||
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+ (cap->cc_flags & match) == 0)
continue;
- /* Hardware required -- ignore software drivers. */
- if (hard > 0 && (cap->cc_flags & CRYPTOCAP_F_SOFTWARE))
- continue;
- /* Software required -- ignore hardware drivers. */
- if (hard < 0 && (cap->cc_flags & CRYPTOCAP_F_SOFTWARE) == 0)
- continue;
-
- /* See if all the algorithms are supported. */
- for (cr = cri; cr; cr = cr->cri_next)
- if (cap->cc_alg[cr->cri_alg] == 0)
- break;
-
- if (cr == NULL) {
- /* Ok, all algorithms are supported. */
+ /* verify all the algorithms are supported. */
+ if (driver_suitable(cap, cri)) {
+ if (best == NULL ||
+ cap->cc_sessions < best->cc_sessions)
+ best = cap;
+ }
+ }
+ if (best != NULL)
+ return best;
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+ /* sort of an Algol 68-style for loop */
+ match = CRYPTOCAP_F_SOFTWARE;
+ goto again;
+ }
+ return best;
+}
- /*
- * Can't do everything in one session.
- *
- * XXX Fix this. We need to inject a "virtual" session layer right
- * XXX about here.
- */
+/*
+ * Create a new session. The crid argument specifies a crypto
+ * driver to use or constraints on a driver to select (hardware
+ * only, software only, either). Whatever driver is selected
+ * must be capable of the requested crypto algorithms.
+ */
+int
+crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int crid)
+{
+ struct cryptocap *cap;
+ u_int32_t hid, lid;
+ int err;
- /* Call the driver initialization routine. */
- lid = hid; /* Pass the driver ID. */
- err = (*cap->cc_newsession)(cap->cc_arg, &lid, cri);
- if (err == 0) {
- /* XXX assert (hid &~ 0xffffff) == 0 */
- /* XXX assert (cap->cc_flags &~ 0xff) == 0 */
- (*sid) = ((cap->cc_flags & 0xff) << 24) | hid;
- (*sid) <<= 32;
- (*sid) |= (lid & 0xffffffff);
- cap->cc_sessions++;
- }
- break;
- }
+ CRYPTO_DRIVER_LOCK();
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ /*
+ * Use specified driver; verify it is capable.
+ */
+ cap = crypto_checkdriver(crid);
+ if (cap != NULL && !driver_suitable(cap, cri))
+ cap = NULL;
+ } else {
+ /*
+ * No requested driver; select based on crid flags.
+ */
+ cap = crypto_select_driver(cri, crid);
+ /*
+ * if NULL then can't do everything in one session.
+ * XXX Fix this. We need to inject a "virtual" session
+ * XXX layer right about here.
+ */
}
-done:
+ if (cap != NULL) {
+ /* Call the driver initialization routine. */
+ hid = cap - crypto_drivers;
+ lid = hid; /* Pass the driver ID. */
+ err = CRYPTODEV_NEWSESSION(cap->cc_dev, &lid, cri);
+ if (err == 0) {
+ (*sid) = (cap->cc_flags & 0xff000000)
+ | (hid & 0x00ffffff);
+ (*sid) <<= 32;
+ (*sid) |= (lid & 0xffffffff);
+ cap->cc_sessions++;
+ }
+ } else
+ err = EINVAL;
CRYPTO_DRIVER_UNLOCK();
return err;
}
+static void
+crypto_remove(struct cryptocap *cap)
+{
+
+ mtx_assert(&crypto_drivers_mtx, MA_OWNED);
+ if (cap->cc_sessions == 0 && cap->cc_koperations == 0)
+ bzero(cap, sizeof(*cap));
+}
+
/*
* Delete an existing session (or a reserved session on an unregistered
* driver).
@@ -335,6 +437,7 @@
int
crypto_freesession(u_int64_t sid)
{
+ struct cryptocap *cap;
u_int32_t hid;
int err;
@@ -352,24 +455,16 @@
err = ENOENT;
goto done;
}
+ cap = &crypto_drivers[hid];
- if (crypto_drivers[hid].cc_sessions)
- crypto_drivers[hid].cc_sessions--;
+ if (cap->cc_sessions)
+ cap->cc_sessions--;
/* Call the driver cleanup routine, if available. */
- if (crypto_drivers[hid].cc_freesession)
- err = crypto_drivers[hid].cc_freesession(
- crypto_drivers[hid].cc_arg, sid);
- else
- err = 0;
+ err = CRYPTODEV_FREESESSION(cap->cc_dev, sid);
- /*
- * If this was the last session of a driver marked as invalid,
- * make the entry available for reuse.
- */
- if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
- crypto_drivers[hid].cc_sessions == 0)
- bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+ crypto_remove(cap);
done:
CRYPTO_DRIVER_UNLOCK();
@@ -381,18 +476,25 @@
* support for the algorithms they handle.
*/
int32_t
-crypto_get_driverid(u_int32_t flags)
+crypto_get_driverid(device_t dev, int flags)
{
struct cryptocap *newdrv;
int i;
+ if ((flags & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ printf("%s: no flags specified when registering driver\n",
+ device_get_nameunit(dev));
+ return -1;
+ }
+
CRYPTO_DRIVER_LOCK();
- for (i = 0; i < crypto_drivers_num; i++)
- if (crypto_drivers[i].cc_process == NULL &&
- (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0 &&
- crypto_drivers[i].cc_sessions == 0)
+ for (i = 0; i < crypto_drivers_num; i++) {
+ if (crypto_drivers[i].cc_dev == NULL &&
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0) {
break;
+ }
+ }
/* Out of entries, allocate some more. */
if (i == crypto_drivers_num) {
@@ -422,21 +524,61 @@
/* NB: state is zero'd on free */
crypto_drivers[i].cc_sessions = 1; /* Mark */
+ crypto_drivers[i].cc_dev = dev;
crypto_drivers[i].cc_flags = flags;
if (bootverbose)
- printf("crypto: assign driver %u, flags %u\n", i, flags);
+ printf("crypto: assign %s driver id %u, flags %u\n",
+ device_get_nameunit(dev), i, flags);
CRYPTO_DRIVER_UNLOCK();
return i;
}
-static struct cryptocap *
-crypto_checkdriver(u_int32_t hid)
+/*
+ * Lookup a driver by name. We match against the full device
+ * name and unit, and against just the name. The latter gives
+ * us a simple widlcarding by device name. On success return the
+ * driver/hardware identifier; otherwise return -1.
+ */
+int
+crypto_find_driver(const char *match)
{
- if (crypto_drivers == NULL)
- return NULL;
- return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
+ int i, len = strlen(match);
+
+ CRYPTO_DRIVER_LOCK();
+ for (i = 0; i < crypto_drivers_num; i++) {
+ device_t dev = crypto_drivers[i].cc_dev;
+ if (dev == NULL ||
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP))
+ continue;
+ if (strncmp(match, device_get_nameunit(dev), len) == 0 ||
+ strncmp(match, device_get_name(dev), len) == 0)
+ break;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+ return i < crypto_drivers_num ? i : -1;
+}
+
+/*
+ * Return the device_t for the specified driver or NULL
+ * if the driver identifier is invalid.
+ */
+device_t
+crypto_find_device_byhid(int hid)
+{
+ struct cryptocap *cap = crypto_checkdriver(hid);
+ return cap != NULL ? cap->cc_dev : NULL;
+}
+
+/*
+ * Return the device/driver capabilities.
+ */
+int
+crypto_getcaps(int hid)
+{
+ struct cryptocap *cap = crypto_checkdriver(hid);
+ return cap != NULL ? cap->cc_flags : 0;
}
/*
@@ -444,9 +586,7 @@
* is called once for each algorithm supported a driver.
*/
int
-crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags,
- int (*kprocess)(void*, struct cryptkop *, int),
- void *karg)
+crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags)
{
struct cryptocap *cap;
int err;
@@ -464,16 +604,11 @@
cap->cc_kalg[kalg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
if (bootverbose)
- printf("crypto: driver %u registers key alg %u flags %u\n"
- , driverid
+ printf("crypto: %s registers key alg %u flags %u\n"
+ , device_get_nameunit(cap->cc_dev)
, kalg
, flags
);
-
- if (cap->cc_kprocess == NULL) {
- cap->cc_karg = karg;
- cap->cc_kprocess = kprocess;
- }
err = 0;
} else
err = EINVAL;
@@ -488,11 +623,7 @@
*/
int
crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
- u_int32_t flags,
- int (*newses)(void*, u_int32_t*, struct cryptoini*),
- int (*freeses)(void*, u_int64_t),
- int (*process)(void*, struct cryptop *, int),
- void *arg)
+ u_int32_t flags)
{
struct cryptocap *cap;
int err;
@@ -512,20 +643,13 @@
cap->cc_alg[alg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
cap->cc_max_op_len[alg] = maxoplen;
if (bootverbose)
- printf("crypto: driver %u registers alg %u flags %u maxoplen %u\n"
- , driverid
+ printf("crypto: %s registers alg %u flags %u maxoplen %u\n"
+ , device_get_nameunit(cap->cc_dev)
, alg
, flags
, maxoplen
);
-
- if (cap->cc_process == NULL) {
- cap->cc_arg = arg;
- cap->cc_newsession = newses;
- cap->cc_process = process;
- cap->cc_freesession = freeses;
- cap->cc_sessions = 0; /* Unmark */
- }
+ cap->cc_sessions = 0; /* Unmark */
err = 0;
} else
err = EINVAL;
@@ -534,6 +658,27 @@
return err;
}
+static void
+driver_finis(struct cryptocap *cap)
+{
+ u_int32_t ses, kops;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ ses = cap->cc_sessions;
+ kops = cap->cc_koperations;
+ bzero(cap, sizeof(*cap));
+ if (ses != 0 || kops != 0) {
+ /*
+ * If there are pending sessions,
+ * just mark as invalid.
+ */
+ cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
+ cap->cc_sessions = ses;
+ cap->cc_koperations = kops;
+ }
+}
+
/*
* Unregister a crypto driver. If there are pending sessions using it,
* leave enough information around so that subsequent calls using those
@@ -543,12 +688,10 @@
int
crypto_unregister(u_int32_t driverid, int alg)
{
- int i, err;
- u_int32_t ses;
struct cryptocap *cap;
+ int i, err;
CRYPTO_DRIVER_LOCK();
-
cap = crypto_checkdriver(driverid);
if (cap != NULL &&
(CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX) &&
@@ -561,22 +704,13 @@
if (cap->cc_alg[i] != 0)
break;
- if (i == CRYPTO_ALGORITHM_MAX + 1) {
- ses = cap->cc_sessions;
- bzero(cap, sizeof(struct cryptocap));
- if (ses != 0) {
- /*
- * If there are pending sessions, just mark as invalid.
- */
- cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
- cap->cc_sessions = ses;
- }
- }
+ if (i == CRYPTO_ALGORITHM_MAX + 1)
+ driver_finis(cap);
err = 0;
} else
err = EINVAL;
-
CRYPTO_DRIVER_UNLOCK();
+
return err;
}
@@ -590,32 +724,18 @@
int
crypto_unregister_all(u_int32_t driverid)
{
- int i, err;
- u_int32_t ses;
struct cryptocap *cap;
+ int err;
CRYPTO_DRIVER_LOCK();
-
cap = crypto_checkdriver(driverid);
if (cap != NULL) {
- for (i = CRYPTO_ALGORITHM_MIN; i <= CRYPTO_ALGORITHM_MAX; i++) {
- cap->cc_alg[i] = 0;
- cap->cc_max_op_len[i] = 0;
- }
- ses = cap->cc_sessions;
- bzero(cap, sizeof(struct cryptocap));
- if (ses != 0) {
- /*
- * If there are pending sessions, just mark as invalid.
- */
- cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
- cap->cc_sessions = ses;
- }
+ driver_finis(cap);
err = 0;
} else
err = EINVAL;
-
CRYPTO_DRIVER_UNLOCK();
+
return err;
}
@@ -627,21 +747,16 @@
crypto_unblock(u_int32_t driverid, int what)
{
struct cryptocap *cap;
- int needwakeup, err;
+ int err;
CRYPTO_Q_LOCK();
cap = crypto_checkdriver(driverid);
if (cap != NULL) {
- needwakeup = 0;
- if (what & CRYPTO_SYMQ) {
- needwakeup |= cap->cc_qblocked;
+ if (what & CRYPTO_SYMQ)
cap->cc_qblocked = 0;
- }
- if (what & CRYPTO_ASYMQ) {
- needwakeup |= cap->cc_kqblocked;
+ if (what & CRYPTO_ASYMQ)
cap->cc_kqblocked = 0;
- }
- if (needwakeup)
+ if (crp_sleep)
wakeup_one(&crp_q);
err = 0;
} else
@@ -657,7 +772,8 @@
int
crypto_dispatch(struct cryptop *crp)
{
- u_int32_t hid = CRYPTO_SESID2HID(crp->crp_sid);
+ struct cryptocap *cap;
+ u_int32_t hid;
int result;
cryptostats.cs_ops++;
@@ -667,57 +783,33 @@
binuptime(&crp->crp_tstamp);
#endif
- CRYPTO_Q_LOCK();
+ hid = CRYPTO_SESID2HID(crp->crp_sid);
+
if ((crp->crp_flags & CRYPTO_F_BATCH) == 0) {
- struct cryptocap *cap;
/*
* Caller marked the request to be processed
* immediately; dispatch it directly to the
* driver unless the driver is currently blocked.
*/
cap = crypto_checkdriver(hid);
- if (cap && !cap->cc_qblocked) {
- result = crypto_invoke(crp, 0);
- if (result == ERESTART) {
- /*
- * The driver ran out of resources, mark the
- * driver ``blocked'' for cryptop's and put
- * the request on the queue.
- *
- * XXX ops are placed at the tail so their
- * order is preserved but this can place them
- * behind batch'd ops.
- */
- crypto_drivers[hid].cc_qblocked = 1;
- TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
- cryptostats.cs_blocks++;
- result = 0;
- }
- } else {
+ /* Driver cannot disappeared when there is an active session. */
+ KASSERT(cap != NULL, ("%s: Driver disappeared.", __func__));
+ if (!cap->cc_qblocked) {
+ result = crypto_invoke(cap, crp, 0);
+ if (result != ERESTART)
+ return (result);
/*
- * The driver is blocked, just queue the op until
- * it unblocks and the kernel thread gets kicked.
+ * The driver ran out of resources, put the request on
+ * the queue.
*/
- TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
- result = 0;
}
- } else {
- int wasempty;
- /*
- * Caller marked the request as ``ok to delay'';
- * queue it for the dispatch thread. This is desirable
- * when the operation is low priority and/or suitable
- * for batching.
- */
- wasempty = TAILQ_EMPTY(&crp_q);
- TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
- if (wasempty)
- wakeup_one(&crp_q);
- result = 0;
}
+ CRYPTO_Q_LOCK();
+ TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
+ if (crp_sleep)
+ wakeup_one(&crp_q);
CRYPTO_Q_UNLOCK();
-
- return result;
+ return 0;
}
/*
@@ -727,78 +819,143 @@
int
crypto_kdispatch(struct cryptkop *krp)
{
- struct cryptocap *cap;
- int result;
+ int error;
cryptostats.cs_kops++;
- CRYPTO_Q_LOCK();
- cap = crypto_checkdriver(krp->krp_hid);
- if (cap && !cap->cc_kqblocked) {
- result = crypto_kinvoke(krp, 0);
- if (result == ERESTART) {
- /*
- * The driver ran out of resources, mark the
- * driver ``blocked'' for cryptkop's and put
- * the request back in the queue. It would
- * best to put the request back where we got
- * it but that's hard so for now we put it
- * at the front. This should be ok; putting
- * it at the end does not work.
- */
- crypto_drivers[krp->krp_hid].cc_kqblocked = 1;
- TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
- cryptostats.cs_kblocks++;
- }
- } else {
- /*
- * The driver is blocked, just queue the op until
- * it unblocks and the kernel thread gets kicked.
- */
+ error = crypto_kinvoke(krp, krp->krp_crid);
+ if (error == ERESTART) {
+ CRYPTO_Q_LOCK();
TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
- result = 0;
+ if (crp_sleep)
+ wakeup_one(&crp_q);
+ CRYPTO_Q_UNLOCK();
+ error = 0;
}
- CRYPTO_Q_UNLOCK();
-
- return result;
+ return error;
}
/*
- * Dispatch an assymetric crypto request to the appropriate crypto devices.
+ * Verify a driver is suitable for the specified operation.
*/
-static int
-crypto_kinvoke(struct cryptkop *krp, int hint)
+static __inline int
+kdriver_suitable(const struct cryptocap *cap, const struct cryptkop *krp)
{
- u_int32_t hid;
- int error;
+ return (cap->cc_kalg[krp->krp_op] & CRYPTO_ALG_FLAG_SUPPORTED) != 0;
+}
- mtx_assert(&crypto_q_mtx, MA_OWNED);
+/*
+ * Select a driver for an asym operation. The driver must
+ * support the necessary algorithm. The caller can constrain
+ * which device is selected with the flags parameter. The
+ * algorithm we use here is pretty stupid; just use the first
+ * driver that supports the algorithms we need. If there are
+ * multiple suitable drivers we choose the driver with the
+ * fewest active operations. We prefer hardware-backed
+ * drivers to software ones when either may be used.
+ */
+static struct cryptocap *
+crypto_select_kdriver(const struct cryptkop *krp, int flags)
+{
+ struct cryptocap *cap, *best, *blocked;
+ int match, hid;
- /* Sanity checks. */
- if (krp == NULL)
- return EINVAL;
- if (krp->krp_callback == NULL) {
- free(krp, M_XDATA); /* XXX allocated in cryptodev */
- return EINVAL;
- }
+ CRYPTO_DRIVER_ASSERT();
+ /*
+ * Look first for hardware crypto devices if permitted.
+ */
+ if (flags & CRYPTOCAP_F_HARDWARE)
+ match = CRYPTOCAP_F_HARDWARE;
+ else
+ match = CRYPTOCAP_F_SOFTWARE;
+ best = NULL;
+ blocked = NULL;
+again:
for (hid = 0; hid < crypto_drivers_num; hid++) {
- if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
- !crypto_devallowsoft)
- continue;
- if (crypto_drivers[hid].cc_kprocess == NULL)
- continue;
- if ((crypto_drivers[hid].cc_kalg[krp->krp_op] &
- CRYPTO_ALG_FLAG_SUPPORTED) == 0)
+ cap = &crypto_drivers[hid];
+ /*
+ * If it's not initialized, is in the process of
+ * going away, or is not appropriate (hardware
+ * or software based on match), then skip.
+ */
+ if (cap->cc_dev == NULL ||
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+ (cap->cc_flags & match) == 0)
continue;
- break;
+
+ /* verify all the algorithms are supported. */
+ if (kdriver_suitable(cap, krp)) {
+ if (best == NULL ||
+ cap->cc_koperations < best->cc_koperations)
+ best = cap;
+ }
}
- if (hid < crypto_drivers_num) {
- krp->krp_hid = hid;
- error = crypto_drivers[hid].cc_kprocess(
- crypto_drivers[hid].cc_karg, krp, hint);
- } else
- error = ENODEV;
+ if (best != NULL)
+ return best;
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+ /* sort of an Algol 68-style for loop */
+ match = CRYPTOCAP_F_SOFTWARE;
+ goto again;
+ }
+ return best;
+}
+
+/*
+ * Dispatch an assymetric crypto request.
+ */
+static int
+crypto_kinvoke(struct cryptkop *krp, int crid)
+{
+ struct cryptocap *cap = NULL;
+ int error;
+
+ KASSERT(krp != NULL, ("%s: krp == NULL", __func__));
+ KASSERT(krp->krp_callback != NULL,
+ ("%s: krp->crp_callback == NULL", __func__));
+
+ CRYPTO_DRIVER_LOCK();
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ cap = crypto_checkdriver(crid);
+ if (cap != NULL) {
+ /*
+ * Driver present, it must support the necessary
+ * algorithm and, if s/w drivers are excluded,
+ * it must be registered as hardware-backed.
+ */
+ if (!kdriver_suitable(cap, krp) ||
+ (!crypto_devallowsoft &&
+ (cap->cc_flags & CRYPTOCAP_F_HARDWARE) == 0))
+ cap = NULL;
+ }
+ } else {
+ /*
+ * No requested driver; select based on crid flags.
+ */
+ if (!crypto_devallowsoft) /* NB: disallow s/w drivers */
+ crid &= ~CRYPTOCAP_F_SOFTWARE;
+ cap = crypto_select_kdriver(krp, crid);
+ }
+ if (cap != NULL && !cap->cc_kqblocked) {
+ krp->krp_hid = cap - crypto_drivers;
+ cap->cc_koperations++;
+ CRYPTO_DRIVER_UNLOCK();
+ error = CRYPTODEV_KPROCESS(cap->cc_dev, krp, 0);
+ CRYPTO_DRIVER_LOCK();
+ if (error == ERESTART) {
+ cap->cc_koperations--;
+ CRYPTO_DRIVER_UNLOCK();
+ return (error);
+ }
+ } else {
+ /*
+ * NB: cap is !NULL if device is blocked; in
+ * that case return ERESTART so the operation
+ * is resubmitted if possible.
+ */
+ error = (cap == NULL) ? ENODEV : ERESTART;
+ }
+ CRYPTO_DRIVER_UNLOCK();
if (error) {
krp->krp_status = error;
@@ -837,49 +994,37 @@
* Dispatch a crypto request to the appropriate crypto devices.
*/
static int
-crypto_invoke(struct cryptop *crp, int hint)
+crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint)
{
- u_int32_t hid;
- int (*process)(void*, struct cryptop *, int);
+
+ KASSERT(crp != NULL, ("%s: crp == NULL", __func__));
+ KASSERT(crp->crp_callback != NULL,
+ ("%s: crp->crp_callback == NULL", __func__));
+ KASSERT(crp->crp_desc != NULL, ("%s: crp->crp_desc == NULL", __func__));
#ifdef CRYPTO_TIMING
if (crypto_timing)
crypto_tstat(&cryptostats.cs_invoke, &crp->crp_tstamp);
#endif
- /* Sanity checks. */
- if (crp == NULL)
- return EINVAL;
- if (crp->crp_callback == NULL) {
- crypto_freereq(crp);
- return EINVAL;
- }
- if (crp->crp_desc == NULL) {
- crp->crp_etype = EINVAL;
- crypto_done(crp);
- return 0;
- }
-
- hid = CRYPTO_SESID2HID(crp->crp_sid);
- if (hid < crypto_drivers_num) {
- if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP)
- crypto_freesession(crp->crp_sid);
- process = crypto_drivers[hid].cc_process;
- } else {
- process = NULL;
- }
-
- if (process == NULL) {
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP) {
struct cryptodesc *crd;
u_int64_t nid;
/*
* Driver has unregistered; migrate the session and return
* an error to the caller so they'll resubmit the op.
+ *
+ * XXX: What if there are more already queued requests for this
+ * session?
*/
+ crypto_freesession(crp->crp_sid);
+
for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
- if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
+ /* XXX propagate flags from initial session? */
+ if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI),
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE) == 0)
crp->crp_sid = nid;
crp->crp_etype = EAGAIN;
@@ -889,7 +1034,7 @@
/*
* Invoke the driver to process the request.
*/
- return (*process)(crypto_drivers[hid].cc_arg, crp, hint);
+ return CRYPTODEV_PROCESS(cap->cc_dev, crp, hint);
}
}
@@ -904,11 +1049,31 @@
if (crp == NULL)
return;
+#ifdef DIAGNOSTIC
+ {
+ struct cryptop *crp2;
+
+ CRYPTO_Q_LOCK();
+ TAILQ_FOREACH(crp2, &crp_q, crp_next) {
+ KASSERT(crp2 != crp,
+ ("Freeing cryptop from the crypto queue (%p).",
+ crp));
+ }
+ CRYPTO_Q_UNLOCK();
+ CRYPTO_RETQ_LOCK();
+ TAILQ_FOREACH(crp2, &crp_ret_q, crp_next) {
+ KASSERT(crp2 != crp,
+ ("Freeing cryptop from the return queue (%p).",
+ crp));
+ }
+ CRYPTO_RETQ_UNLOCK();
+ }
+#endif
+
while ((crd = crp->crp_desc) != NULL) {
crp->crp_desc = crd->crd_next;
uma_zfree(cryptodesc_zone, crd);
}
-
uma_zfree(cryptop_zone, crp);
}
@@ -982,16 +1147,13 @@
#endif
crp->crp_callback(crp);
} else {
- int wasempty;
/*
* Normal case; queue the callback for the thread.
*/
CRYPTO_RETQ_LOCK();
- wasempty = TAILQ_EMPTY(&crp_ret_q);
- TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
-
- if (wasempty)
+ if (CRYPTO_RETQ_EMPTY())
wakeup_one(&crp_ret_q); /* shared wait channel */
+ TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
CRYPTO_RETQ_UNLOCK();
}
}
@@ -1002,16 +1164,24 @@
void
crypto_kdone(struct cryptkop *krp)
{
- int wasempty;
+ struct cryptocap *cap;
if (krp->krp_status != 0)
cryptostats.cs_kerrs++;
+ CRYPTO_DRIVER_LOCK();
+ /* XXX: What if driver is loaded in the meantime? */
+ if (krp->krp_hid < crypto_drivers_num) {
+ cap = &crypto_drivers[krp->krp_hid];
+ cap->cc_koperations--;
+ KASSERT(cap->cc_koperations >= 0, ("cc_koperations < 0"));
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+ crypto_remove(cap);
+ }
+ CRYPTO_DRIVER_UNLOCK();
CRYPTO_RETQ_LOCK();
- wasempty = TAILQ_EMPTY(&crp_ret_kq);
- TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
-
- if (wasempty)
+ if (CRYPTO_RETQ_EMPTY())
wakeup_one(&crp_ret_q); /* shared wait channel */
+ TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
CRYPTO_RETQ_UNLOCK();
}
@@ -1020,24 +1190,19 @@
{
int hid, kalg, feat = 0;
- if (!crypto_userasymcrypto)
- goto out;
-
CRYPTO_DRIVER_LOCK();
for (hid = 0; hid < crypto_drivers_num; hid++) {
- if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
+ const struct cryptocap *cap = &crypto_drivers[hid];
+
+ if ((cap->cc_flags & CRYPTOCAP_F_SOFTWARE) &&
!crypto_devallowsoft) {
continue;
}
- if (crypto_drivers[hid].cc_kprocess == NULL)
- continue;
for (kalg = 0; kalg < CRK_ALGORITHM_MAX; kalg++)
- if ((crypto_drivers[hid].cc_kalg[kalg] &
- CRYPTO_ALG_FLAG_SUPPORTED) != 0)
+ if (cap->cc_kalg[kalg] & CRYPTO_ALG_FLAG_SUPPORTED)
feat |= 1 << kalg;
}
CRYPTO_DRIVER_UNLOCK();
-out:
*featp = feat;
return (0);
}
@@ -1069,6 +1234,7 @@
struct cryptop *crp, *submit;
struct cryptkop *krp;
struct cryptocap *cap;
+ u_int32_t hid;
int result, hint;
CRYPTO_Q_LOCK();
@@ -1081,9 +1247,15 @@
submit = NULL;
hint = 0;
TAILQ_FOREACH(crp, &crp_q, crp_next) {
- u_int32_t hid = CRYPTO_SESID2HID(crp->crp_sid);
+ hid = CRYPTO_SESID2HID(crp->crp_sid);
cap = crypto_checkdriver(hid);
- if (cap == NULL || cap->cc_process == NULL) {
+ /*
+ * Driver cannot disappeared when there is an active
+ * session.
+ */
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+ __func__, __LINE__));
+ if (cap == NULL || cap->cc_dev == NULL) {
/* Op needs to be migrated, process it. */
if (submit == NULL)
submit = crp;
@@ -1112,7 +1284,11 @@
}
if (submit != NULL) {
TAILQ_REMOVE(&crp_q, submit, crp_next);
- result = crypto_invoke(submit, hint);
+ hid = CRYPTO_SESID2HID(submit->crp_sid);
+ cap = crypto_checkdriver(hid);
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+ __func__, __LINE__));
+ result = crypto_invoke(cap, submit, hint);
if (result == ERESTART) {
/*
* The driver ran out of resources, mark the
@@ -1133,8 +1309,18 @@
/* As above, but for key ops */
TAILQ_FOREACH(krp, &crp_kq, krp_next) {
cap = crypto_checkdriver(krp->krp_hid);
- if (cap == NULL || cap->cc_kprocess == NULL) {
- /* Op needs to be migrated, process it. */
+ if (cap == NULL || cap->cc_dev == NULL) {
+ /*
+ * Operation needs to be migrated, invalidate
+ * the assigned device so it will reselect a
+ * new one below. Propagate the original
+ * crid selection flags if supplied.
+ */
+ krp->krp_hid = krp->krp_crid &
+ (CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE);
+ if (krp->krp_hid == 0)
+ krp->krp_hid =
+ CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE;
break;
}
if (!cap->cc_kqblocked)
@@ -1142,7 +1328,7 @@
}
if (krp != NULL) {
TAILQ_REMOVE(&crp_kq, krp, krp_next);
- result = crypto_kinvoke(krp, 0);
+ result = crypto_kinvoke(krp, krp->krp_hid);
if (result == ERESTART) {
/*
* The driver ran out of resources, mark the
@@ -1173,7 +1359,9 @@
* out of order if dispatched to different devices
* and some become blocked while others do not.
*/
+ crp_sleep = 1;
msleep(&crp_q, &crypto_q_mtx, PWAIT, "crypto_wait", 0);
+ crp_sleep = 0;
if (cryptoproc == NULL)
break;
cryptostats.cs_intrs++;
@@ -1246,3 +1434,131 @@
crypto_finis(&crp_ret_q);
}
+
+#ifdef DDB
+static void
+db_show_drivers(void)
+{
+ int hid;
+
+ db_printf("%12s %4s %4s %8s %2s %2s\n"
+ , "Device"
+ , "Ses"
+ , "Kops"
+ , "Flags"
+ , "QB"
+ , "KB"
+ );
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ const struct cryptocap *cap = &crypto_drivers[hid];
+ if (cap->cc_dev == NULL)
+ continue;
+ db_printf("%-12s %4u %4u %08x %2u %2u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , cap->cc_sessions
+ , cap->cc_koperations
+ , cap->cc_flags
+ , cap->cc_qblocked
+ , cap->cc_kqblocked
+ );
+ }
+}
+
+DB_SHOW_COMMAND(crypto, db_show_crypto)
+{
+ struct cryptop *crp;
+
+ db_show_drivers();
+ db_printf("\n");
+
+ db_printf("%4s %8s %4s %4s %4s %4s %8s %8s\n",
+ "HID", "Caps", "Ilen", "Olen", "Etype", "Flags",
+ "Desc", "Callback");
+ TAILQ_FOREACH(crp, &crp_q, crp_next) {
+ db_printf("%4u %08x %4u %4u %4u %04x %8p %8p\n"
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
+ , (int) CRYPTO_SESID2CAPS(crp->crp_sid)
+ , crp->crp_ilen, crp->crp_olen
+ , crp->crp_etype
+ , crp->crp_flags
+ , crp->crp_desc
+ , crp->crp_callback
+ );
+ }
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
+ db_printf("\n%4s %4s %4s %8s\n",
+ "HID", "Etype", "Flags", "Callback");
+ TAILQ_FOREACH(crp, &crp_ret_q, crp_next) {
+ db_printf("%4u %4u %04x %8p\n"
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
+ , crp->crp_etype
+ , crp->crp_flags
+ , crp->crp_callback
+ );
+ }
+ }
+}
+
+DB_SHOW_COMMAND(kcrypto, db_show_kcrypto)
+{
+ struct cryptkop *krp;
+
+ db_show_drivers();
+ db_printf("\n");
+
+ db_printf("%4s %5s %4s %4s %8s %4s %8s\n",
+ "Op", "Status", "#IP", "#OP", "CRID", "HID", "Callback");
+ TAILQ_FOREACH(krp, &crp_kq, krp_next) {
+ db_printf("%4u %5u %4u %4u %08x %4u %8p\n"
+ , krp->krp_op
+ , krp->krp_status
+ , krp->krp_iparams, krp->krp_oparams
+ , krp->krp_crid, krp->krp_hid
+ , krp->krp_callback
+ );
+ }
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
+ db_printf("%4s %5s %8s %4s %8s\n",
+ "Op", "Status", "CRID", "HID", "Callback");
+ TAILQ_FOREACH(krp, &crp_ret_kq, krp_next) {
+ db_printf("%4u %5u %08x %4u %8p\n"
+ , krp->krp_op
+ , krp->krp_status
+ , krp->krp_crid, krp->krp_hid
+ , krp->krp_callback
+ );
+ }
+ }
+}
+#endif
+
+int crypto_modevent(module_t mod, int type, void *unused);
+
+/*
+ * Initialization code, both for static and dynamic loading.
+ * Note this is not invoked with the usual MODULE_DECLARE
+ * mechanism but instead is listed as a dependency by the
+ * cryptosoft driver. This guarantees proper ordering of
+ * calls on module load/unload.
+ */
+int
+crypto_modevent(module_t mod, int type, void *unused)
+{
+ int error = EINVAL;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = crypto_init();
+ if (error == 0 && bootverbose)
+ printf("crypto: <crypto core>\n");
+ break;
+ case MOD_UNLOAD:
+ /*XXX disallow if active sessions */
+ error = 0;
+ crypto_destroy();
+ return 0;
+ }
+ return error;
+}
+MODULE_VERSION(crypto, 1);
+MODULE_DEPEND(crypto, zlib, 1, 1, 1);
Index: xform.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/xform.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/xform.c -L sys/opencrypto/xform.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/xform.c
+++ sys/opencrypto/xform.c
@@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/xform.c,v 1.5.2.1 2005/08/30 15:01:50 pjd Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/xform.c,v 1.9 2007/05/09 19:37:02 gnn Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -51,6 +51,7 @@
#include <crypto/blowfish/blowfish.h>
#include <crypto/des/des.h>
#include <crypto/rijndael/rijndael.h>
+#include <crypto/camellia/camellia.h>
#include <crypto/sha1.h>
#include <opencrypto/cast.h>
@@ -74,24 +75,28 @@
static int cast5_setkey(u_int8_t **, u_int8_t *, int);
static int skipjack_setkey(u_int8_t **, u_int8_t *, int);
static int rijndael128_setkey(u_int8_t **, u_int8_t *, int);
+static int cml_setkey(u_int8_t **, u_int8_t *, int);
static void des1_encrypt(caddr_t, u_int8_t *);
static void des3_encrypt(caddr_t, u_int8_t *);
static void blf_encrypt(caddr_t, u_int8_t *);
static void cast5_encrypt(caddr_t, u_int8_t *);
static void skipjack_encrypt(caddr_t, u_int8_t *);
static void rijndael128_encrypt(caddr_t, u_int8_t *);
+static void cml_encrypt(caddr_t, u_int8_t *);
static void des1_decrypt(caddr_t, u_int8_t *);
static void des3_decrypt(caddr_t, u_int8_t *);
static void blf_decrypt(caddr_t, u_int8_t *);
static void cast5_decrypt(caddr_t, u_int8_t *);
static void skipjack_decrypt(caddr_t, u_int8_t *);
static void rijndael128_decrypt(caddr_t, u_int8_t *);
+static void cml_decrypt(caddr_t, u_int8_t *);
static void des1_zerokey(u_int8_t **);
static void des3_zerokey(u_int8_t **);
static void blf_zerokey(u_int8_t **);
static void cast5_zerokey(u_int8_t **);
static void skipjack_zerokey(u_int8_t **);
static void rijndael128_zerokey(u_int8_t **);
+static void cml_zerokey(u_int8_t **);
static void null_init(void *);
static int null_update(void *, u_int8_t *, u_int16_t);
@@ -114,7 +119,7 @@
struct enc_xform enc_xform_null = {
CRYPTO_NULL_CBC, "NULL",
/* NB: blocksize of 4 is to generate a properly aligned ESP header */
- 4, 0, 256, /* 2048 bits, max key */
+ NULL_BLOCK_LEN, 0, 256, /* 2048 bits, max key */
null_encrypt,
null_decrypt,
null_setkey,
@@ -123,7 +128,7 @@
struct enc_xform enc_xform_des = {
CRYPTO_DES_CBC, "DES",
- 8, 8, 8,
+ DES_BLOCK_LEN, 8, 8,
des1_encrypt,
des1_decrypt,
des1_setkey,
@@ -132,7 +137,7 @@
struct enc_xform enc_xform_3des = {
CRYPTO_3DES_CBC, "3DES",
- 8, 24, 24,
+ DES3_BLOCK_LEN, 24, 24,
des3_encrypt,
des3_decrypt,
des3_setkey,
@@ -141,7 +146,7 @@
struct enc_xform enc_xform_blf = {
CRYPTO_BLF_CBC, "Blowfish",
- 8, 5, 56 /* 448 bits, max key */,
+ BLOWFISH_BLOCK_LEN, 5, 56 /* 448 bits, max key */,
blf_encrypt,
blf_decrypt,
blf_setkey,
@@ -150,7 +155,7 @@
struct enc_xform enc_xform_cast5 = {
CRYPTO_CAST_CBC, "CAST-128",
- 8, 5, 16,
+ CAST128_BLOCK_LEN, 5, 16,
cast5_encrypt,
cast5_decrypt,
cast5_setkey,
@@ -159,7 +164,7 @@
struct enc_xform enc_xform_skipjack = {
CRYPTO_SKIPJACK_CBC, "Skipjack",
- 8, 10, 10,
+ SKIPJACK_BLOCK_LEN, 10, 10,
skipjack_encrypt,
skipjack_decrypt,
skipjack_setkey,
@@ -168,7 +173,7 @@
struct enc_xform enc_xform_rijndael128 = {
CRYPTO_RIJNDAEL128_CBC, "Rijndael-128/AES",
- 16, 8, 32,
+ RIJNDAEL128_BLOCK_LEN, 8, 32,
rijndael128_encrypt,
rijndael128_decrypt,
rijndael128_setkey,
@@ -184,63 +189,72 @@
NULL,
};
+struct enc_xform enc_xform_camellia = {
+ CRYPTO_CAMELLIA_CBC, "Camellia",
+ CAMELLIA_BLOCK_LEN, 8, 32,
+ cml_encrypt,
+ cml_decrypt,
+ cml_setkey,
+ cml_zerokey,
+};
+
/* Authentication instances */
struct auth_hash auth_hash_null = {
CRYPTO_NULL_HMAC, "NULL-HMAC",
- 0, 0, 12, sizeof(int), /* NB: context isn't used */
+ 0, NULL_HASH_LEN, NULL_HMAC_BLOCK_LEN, sizeof(int), /* NB: context isn't used */
null_init, null_update, null_final
};
-struct auth_hash auth_hash_hmac_md5_96 = {
+struct auth_hash auth_hash_hmac_md5 = {
CRYPTO_MD5_HMAC, "HMAC-MD5",
- 16, 16, 12, sizeof(MD5_CTX),
+ 16, MD5_HASH_LEN, MD5_HMAC_BLOCK_LEN, sizeof(MD5_CTX),
(void (*) (void *)) MD5Init, MD5Update_int,
(void (*) (u_int8_t *, void *)) MD5Final
};
-struct auth_hash auth_hash_hmac_sha1_96 = {
+struct auth_hash auth_hash_hmac_sha1 = {
CRYPTO_SHA1_HMAC, "HMAC-SHA1",
- 20, 20, 12, sizeof(SHA1_CTX),
+ 20, SHA1_HASH_LEN, SHA1_HMAC_BLOCK_LEN, sizeof(SHA1_CTX),
SHA1Init_int, SHA1Update_int, SHA1Final_int
};
-struct auth_hash auth_hash_hmac_ripemd_160_96 = {
+struct auth_hash auth_hash_hmac_ripemd_160 = {
CRYPTO_RIPEMD160_HMAC, "HMAC-RIPEMD-160",
- 20, 20, 12, sizeof(RMD160_CTX),
+ 20, RIPEMD160_HASH_LEN, RIPEMD160_HMAC_BLOCK_LEN, sizeof(RMD160_CTX),
(void (*)(void *)) RMD160Init, RMD160Update_int,
(void (*)(u_int8_t *, void *)) RMD160Final
};
struct auth_hash auth_hash_key_md5 = {
CRYPTO_MD5_KPDK, "Keyed MD5",
- 0, 16, 12, sizeof(MD5_CTX),
+ 0, MD5_KPDK_HASH_LEN, 0, sizeof(MD5_CTX),
(void (*)(void *)) MD5Init, MD5Update_int,
(void (*)(u_int8_t *, void *)) MD5Final
};
struct auth_hash auth_hash_key_sha1 = {
CRYPTO_SHA1_KPDK, "Keyed SHA1",
- 0, 20, 12, sizeof(SHA1_CTX),
+ 0, SHA1_KPDK_HASH_LEN, 0, sizeof(SHA1_CTX),
SHA1Init_int, SHA1Update_int, SHA1Final_int
};
struct auth_hash auth_hash_hmac_sha2_256 = {
- CRYPTO_SHA2_HMAC, "HMAC-SHA2",
- 32, 32, 12, sizeof(SHA256_CTX),
+ CRYPTO_SHA2_256_HMAC, "HMAC-SHA2-256",
+ 32, SHA2_256_HASH_LEN, SHA2_256_HMAC_BLOCK_LEN, sizeof(SHA256_CTX),
(void (*)(void *)) SHA256_Init, SHA256Update_int,
(void (*)(u_int8_t *, void *)) SHA256_Final
};
struct auth_hash auth_hash_hmac_sha2_384 = {
- CRYPTO_SHA2_HMAC, "HMAC-SHA2-384",
- 48, 48, 12, sizeof(SHA384_CTX),
+ CRYPTO_SHA2_384_HMAC, "HMAC-SHA2-384",
+ 48, SHA2_384_HASH_LEN, SHA2_384_HMAC_BLOCK_LEN, sizeof(SHA384_CTX),
(void (*)(void *)) SHA384_Init, SHA384Update_int,
(void (*)(u_int8_t *, void *)) SHA384_Final
};
struct auth_hash auth_hash_hmac_sha2_512 = {
- CRYPTO_SHA2_HMAC, "HMAC-SHA2-512",
- 64, 64, 12, sizeof(SHA512_CTX),
+ CRYPTO_SHA2_512_HMAC, "HMAC-SHA2-512",
+ 64, SHA2_512_HASH_LEN, SHA2_512_HMAC_BLOCK_LEN, sizeof(SHA512_CTX),
(void (*)(void *)) SHA512_Init, SHA512Update_int,
(void (*)(u_int8_t *, void *)) SHA512_Final
};
@@ -533,6 +547,45 @@
*sched = NULL;
}
+static void
+cml_encrypt(caddr_t key, u_int8_t *blk)
+{
+ camellia_encrypt((camellia_ctx *) key, (u_char *) blk, (u_char *) blk);
+}
+
+static void
+cml_decrypt(caddr_t key, u_int8_t *blk)
+{
+ camellia_decrypt(((camellia_ctx *) key), (u_char *) blk,
+ (u_char *) blk);
+}
+
+static int
+cml_setkey(u_int8_t **sched, u_int8_t *key, int len)
+{
+ int err;
+
+ if (len != 16 && len != 24 && len != 32)
+ return (EINVAL);
+ MALLOC(*sched, u_int8_t *, sizeof(camellia_ctx), M_CRYPTO_DATA,
+ M_NOWAIT|M_ZERO);
+ if (*sched != NULL) {
+ camellia_set_key((camellia_ctx *) *sched, (u_char *) key,
+ len * 8);
+ err = 0;
+ } else
+ err = ENOMEM;
+ return err;
+}
+
+static void
+cml_zerokey(u_int8_t **sched)
+{
+ bzero(*sched, sizeof(camellia_ctx));
+ FREE(*sched, M_CRYPTO_DATA);
+ *sched = NULL;
+}
+
/*
* And now for auth.
*/
--- sys/opencrypto/crypto_if.m
+++ /dev/null
@@ -1,128 +0,0 @@
-#-
-# Copyright (c) 2002, Sam Leffler
-# 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. All advertising materials mentioning features or use of this software
-# must display the following acknowledgement:
-# This product includes software developed by Boris Popov.
-# 4. Neither the name of the author nor the names of any co-contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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: src/sys/opencrypto/crypto_if.m,v 1.2 2005/01/07 02:29:16 imp Exp $
-#
-
-#include <crypto/cryptodev.h>
-
-INTERFACE crypto;
-
-METHOD int32_t get_driverid {
- u_int32_t flags;
-};
-
-# XXX define typedefs to work around inadequate parser
-HEADER {
- typedef int crypto_newsession_cb(void*, u_int32_t*, struct cryptoini*);
- typedef int crypto_freesession_cb(void*, u_int64_t*);
- typedef int crypto_process_cb(void*, struct cryptop*);
- typedef int crypto_kprocess_cb(void*, struct cryptkop*);
-};
-
-METHOD int register {
- u_int32_t driverid;
- int alg;
- u_int16_t maxoplen;
- u_int32_t flags;
- crypto_newsession_cb* newses;
- crypto_freesession_cb* freeses;
- crypto_process_cb* process;
- void *arg;
-};
-
-METHOD int kregister {
- u_int32_t driverid;
- int kalg;
- u_int32_t flags;
- crypto_kprocess_cb* kprocess;
- void *arg;
-};
-
-METHOD int unregister {
- u_int32_t driverid;
- int alg;
-};
-
-METHOD int unregister_all {
- u_int32_t driverid;
-};
-
-METHOD int newsession {
- u_int64_t *sid;
- struct cryptoini *cri;
- int hard;
-};
-
-METHOD int freesession {
- u_int64_t sid;
-};
-
-METHOD int dispatch {
- struct cryptop *crp;
-};
-
-METHOD int kdispatch {
- struct cryptkop *krp;
-};
-
-METHOD int crypto_unblock {
- u_int32_t driverid;
- int what;
-};
-
-METHOD int invoke {
- struct cryptop *crp;
-};
-
-METHOD int kinvoke {
- struct cryptkop *krp;
-};
-
-METHOD struct cryptop * getreq {
- int num;
-};
-
-METHOD void freereq {
- struct cryptop *crp;
-};
-
-METHOD void done {
- struct cryptop *crp;
-};
-
-METHOD void kdone {
- struct cryptkop *krp;
-};
-
-METHOD int getfeat {
- int *featp;
-};
Index: cast.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cast.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cast.c -L sys/opencrypto/cast.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cast.c
+++ sys/opencrypto/cast.c
@@ -7,7 +7,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/cast.c,v 1.3 2005/01/07 02:29:16 imp Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/cast.c,v 1.4 2007/07/05 06:59:14 peter Exp $");
#include <sys/types.h>
#include <opencrypto/cast.h>
@@ -131,7 +131,7 @@
void cast_setkey(cast_key* key, u_int8_t* rawkey, int keybytes)
{
-u_int32_t t[4], z[4], x[4];
+u_int32_t t[4] = {0, 0, 0, 0}, z[4] = {0, 0, 0, 0}, x[4];
int i;
/* Set number of rounds to 12 or 16, depending on key length */
Index: cryptosoft.c
===================================================================
RCS file: /home/cvs/src/sys/opencrypto/cryptosoft.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/opencrypto/cryptosoft.c -L sys/opencrypto/cryptosoft.c -u -r1.1.1.1 -r1.2
--- sys/opencrypto/cryptosoft.c
+++ sys/opencrypto/cryptosoft.c
@@ -2,6 +2,7 @@
/*-
* The author of this code is Angelos D. Keromytis (angelos at cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
*
* This code was written by Angelos D. Keromytis in Athens, Greece, in
* February 2000. Network Security Technologies Inc. (NSTI) kindly
@@ -22,12 +23,13 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/opencrypto/cryptosoft.c,v 1.9 2005/03/11 12:37:06 ume Exp $");
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptosoft.c,v 1.19 2007/05/09 19:37:02 gnn Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
+#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/errno.h>
#include <sys/random.h>
@@ -45,54 +47,28 @@
#include <opencrypto/cryptosoft.h>
#include <opencrypto/xform.h>
-u_int8_t hmac_ipad_buffer[64] = {
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
-};
-
-u_int8_t hmac_opad_buffer[64] = {
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C
-};
+#include <sys/kobj.h>
+#include <sys/bus.h>
+#include "cryptodev_if.h"
+
+static int32_t swcr_id;
+static struct swcr_data **swcr_sessions = NULL;
+static u_int32_t swcr_sesnum;
-
-struct swcr_data **swcr_sessions = NULL;
-u_int32_t swcr_sesnum = 0;
-int32_t swcr_id = -1;
-
-#define COPYBACK(x, a, b, c, d) \
- (x) == CRYPTO_BUF_MBUF ? m_copyback((struct mbuf *)a,b,c,d) \
- : cuio_copyback((struct uio *)a,b,c,d)
-#define COPYDATA(x, a, b, c, d) \
- (x) == CRYPTO_BUF_MBUF ? m_copydata((struct mbuf *)a,b,c,d) \
- : cuio_copydata((struct uio *)a,b,c,d)
+u_int8_t hmac_ipad_buffer[HMAC_MAX_BLOCK_LEN];
+u_int8_t hmac_opad_buffer[HMAC_MAX_BLOCK_LEN];
static int swcr_encdec(struct cryptodesc *, struct swcr_data *, caddr_t, int);
-static int swcr_authcompute(struct cryptop *crp, struct cryptodesc *crd,
- struct swcr_data *sw, caddr_t buf, int outtype);
+static int swcr_authcompute(struct cryptodesc *, struct swcr_data *, caddr_t, int);
static int swcr_compdec(struct cryptodesc *, struct swcr_data *, caddr_t, int);
-static int swcr_process(void *, struct cryptop *, int);
-static int swcr_newsession(void *, u_int32_t *, struct cryptoini *);
-static int swcr_freesession(void *, u_int64_t);
+static int swcr_freesession(device_t dev, u_int64_t tid);
/*
* Apply a symmetric encryption/decryption algorithm.
*/
static int
swcr_encdec(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf,
- int outtype)
+ int flags)
{
unsigned char iv[EALG_MAX_BLOCK_LEN], blk[EALG_MAX_BLOCK_LEN], *idat;
unsigned char *ivp, piv[EALG_MAX_BLOCK_LEN];
@@ -111,32 +87,12 @@
/* IV explicitly provided ? */
if (crd->crd_flags & CRD_F_IV_EXPLICIT)
bcopy(crd->crd_iv, iv, blks);
- else {
- /* Get random IV */
- for (i = 0;
- i + sizeof (u_int32_t) < EALG_MAX_BLOCK_LEN;
- i += sizeof (u_int32_t)) {
- u_int32_t temp = arc4random();
-
- bcopy(&temp, iv + i, sizeof(u_int32_t));
- }
- /*
- * What if the block size is not a multiple
- * of sizeof (u_int32_t), which is the size of
- * what arc4random() returns ?
- */
- if (EALG_MAX_BLOCK_LEN % sizeof (u_int32_t) != 0) {
- u_int32_t temp = arc4random();
-
- bcopy (&temp, iv + i,
- EALG_MAX_BLOCK_LEN - i);
- }
- }
+ else
+ arc4rand(iv, blks, 0);
/* Do we need to write the IV */
- if (!(crd->crd_flags & CRD_F_IV_PRESENT)) {
- COPYBACK(outtype, buf, crd->crd_inject, blks, iv);
- }
+ if (!(crd->crd_flags & CRD_F_IV_PRESENT))
+ crypto_copyback(flags, buf, crd->crd_inject, blks, iv);
} else { /* Decryption */
/* IV explicitly provided ? */
@@ -144,7 +100,7 @@
bcopy(crd->crd_iv, iv, blks);
else {
/* Get IV off buf */
- COPYDATA(outtype, buf, crd->crd_inject, blks, iv);
+ crypto_copydata(flags, buf, crd->crd_inject, blks, iv);
}
}
@@ -160,40 +116,7 @@
}
ivp = iv;
- if (outtype == CRYPTO_BUF_CONTIG) {
- if (crd->crd_flags & CRD_F_ENCRYPT) {
- for (i = crd->crd_skip;
- i < crd->crd_skip + crd->crd_len; i += blks) {
- /* XOR with the IV/previous block, as appropriate. */
- if (i == crd->crd_skip)
- for (k = 0; k < blks; k++)
- buf[i + k] ^= ivp[k];
- else
- for (k = 0; k < blks; k++)
- buf[i + k] ^= buf[i + k - blks];
- exf->encrypt(sw->sw_kschedule, buf + i);
- }
- } else { /* Decrypt */
- /*
- * Start at the end, so we don't need to keep the encrypted
- * block as the IV for the next block.
- */
- for (i = crd->crd_skip + crd->crd_len - blks;
- i >= crd->crd_skip; i -= blks) {
- exf->decrypt(sw->sw_kschedule, buf + i);
-
- /* XOR with the IV/previous block, as appropriate */
- if (i == crd->crd_skip)
- for (k = 0; k < blks; k++)
- buf[i + k] ^= ivp[k];
- else
- for (k = 0; k < blks; k++)
- buf[i + k] ^= buf[i + k - blks];
- }
- }
-
- return 0;
- } else if (outtype == CRYPTO_BUF_MBUF) {
+ if (flags & CRYPTO_F_IMBUF) {
struct mbuf *m = (struct mbuf *) buf;
/* Find beginning of data */
@@ -318,7 +241,7 @@
}
return 0; /* Done with mbuf encryption/decryption */
- } else if (outtype == CRYPTO_BUF_IOV) {
+ } else if (flags & CRYPTO_F_IOV) {
struct uio *uio = (struct uio *) buf;
struct iovec *iov;
@@ -431,21 +354,101 @@
}
}
- return 0; /* Done with mbuf encryption/decryption */
+ return 0; /* Done with iovec encryption/decryption */
+ } else { /* contiguous buffer */
+ if (crd->crd_flags & CRD_F_ENCRYPT) {
+ for (i = crd->crd_skip;
+ i < crd->crd_skip + crd->crd_len; i += blks) {
+ /* XOR with the IV/previous block, as appropriate. */
+ if (i == crd->crd_skip)
+ for (k = 0; k < blks; k++)
+ buf[i + k] ^= ivp[k];
+ else
+ for (k = 0; k < blks; k++)
+ buf[i + k] ^= buf[i + k - blks];
+ exf->encrypt(sw->sw_kschedule, buf + i);
+ }
+ } else { /* Decrypt */
+ /*
+ * Start at the end, so we don't need to keep the encrypted
+ * block as the IV for the next block.
+ */
+ for (i = crd->crd_skip + crd->crd_len - blks;
+ i >= crd->crd_skip; i -= blks) {
+ exf->decrypt(sw->sw_kschedule, buf + i);
+
+ /* XOR with the IV/previous block, as appropriate */
+ if (i == crd->crd_skip)
+ for (k = 0; k < blks; k++)
+ buf[i + k] ^= ivp[k];
+ else
+ for (k = 0; k < blks; k++)
+ buf[i + k] ^= buf[i + k - blks];
+ }
+ }
+
+ return 0; /* Done with contiguous buffer encryption/decryption */
}
/* Unreachable */
return EINVAL;
}
+static void
+swcr_authprepare(struct auth_hash *axf, struct swcr_data *sw, u_char *key,
+ int klen)
+{
+ int k;
+
+ klen /= 8;
+
+ switch (axf->type) {
+ case CRYPTO_MD5_HMAC:
+ case CRYPTO_SHA1_HMAC:
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
+ case CRYPTO_NULL_HMAC:
+ case CRYPTO_RIPEMD160_HMAC:
+ for (k = 0; k < klen; k++)
+ key[k] ^= HMAC_IPAD_VAL;
+
+ axf->Init(sw->sw_ictx);
+ axf->Update(sw->sw_ictx, key, klen);
+ axf->Update(sw->sw_ictx, hmac_ipad_buffer, axf->blocksize - klen);
+
+ for (k = 0; k < klen; k++)
+ key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
+
+ axf->Init(sw->sw_octx);
+ axf->Update(sw->sw_octx, key, klen);
+ axf->Update(sw->sw_octx, hmac_opad_buffer, axf->blocksize - klen);
+
+ for (k = 0; k < klen; k++)
+ key[k] ^= HMAC_OPAD_VAL;
+ break;
+ case CRYPTO_MD5_KPDK:
+ case CRYPTO_SHA1_KPDK:
+ sw->sw_klen = klen;
+ bcopy(key, sw->sw_octx, klen);
+ axf->Init(sw->sw_ictx);
+ axf->Update(sw->sw_ictx, key, klen);
+ axf->Final(NULL, sw->sw_ictx);
+ break;
+ default:
+ printf("%s: CRD_F_KEY_EXPLICIT flag given, but algorithm %d "
+ "doesn't use keys.\n", __func__, axf->type);
+ }
+}
+
/*
* Compute keyed-hash authenticator.
*/
static int
-swcr_authcompute(struct cryptop *crp, struct cryptodesc *crd,
- struct swcr_data *sw, caddr_t buf, int outtype)
+swcr_authcompute(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf,
+ int flags)
{
- unsigned char aalg[AALG_MAX_RESULT_LEN];
+ unsigned char aalg[HASH_MAX_LEN];
struct auth_hash *axf;
union authctx ctx;
int err;
@@ -455,28 +458,22 @@
axf = sw->sw_axf;
+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT)
+ swcr_authprepare(axf, sw, crd->crd_key, crd->crd_klen);
+
bcopy(sw->sw_ictx, &ctx, axf->ctxsize);
- switch (outtype) {
- case CRYPTO_BUF_CONTIG:
- axf->Update(&ctx, buf + crd->crd_skip, crd->crd_len);
- break;
- case CRYPTO_BUF_MBUF:
- err = m_apply((struct mbuf *) buf, crd->crd_skip, crd->crd_len,
- (int (*)(void *, void *, unsigned int)) axf->Update,
- (caddr_t) &ctx);
- if (err)
- return err;
- break;
- case CRYPTO_BUF_IOV:
- default:
- return EINVAL;
- }
+ err = crypto_apply(flags, buf, crd->crd_skip, crd->crd_len,
+ (int (*)(void *, void *, unsigned int))axf->Update, (caddr_t)&ctx);
+ if (err)
+ return err;
switch (sw->sw_alg) {
case CRYPTO_MD5_HMAC:
case CRYPTO_SHA1_HMAC:
- case CRYPTO_SHA2_HMAC:
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
case CRYPTO_RIPEMD160_HMAC:
if (sw->sw_octx == NULL)
return EINVAL;
@@ -502,11 +499,8 @@
}
/* Inject the authentication data */
- if (outtype == CRYPTO_BUF_CONTIG)
- bcopy(aalg, buf + crd->crd_inject, axf->authsize);
- else
- m_copyback((struct mbuf *) buf, crd->crd_inject,
- axf->authsize, aalg);
+ crypto_copyback(flags, buf, crd->crd_inject,
+ sw->sw_mlen == 0 ? axf->hashsize : sw->sw_mlen, aalg);
return 0;
}
@@ -515,7 +509,7 @@
*/
static int
swcr_compdec(struct cryptodesc *crd, struct swcr_data *sw,
- caddr_t buf, int outtype)
+ caddr_t buf, int flags)
{
u_int8_t *data, *out;
struct comp_algo *cxf;
@@ -532,7 +526,7 @@
MALLOC(data, u_int8_t *, crd->crd_len, M_CRYPTO_DATA, M_NOWAIT);
if (data == NULL)
return (EINVAL);
- COPYDATA(outtype, buf, crd->crd_skip, crd->crd_len, data);
+ crypto_copydata(flags, buf, crd->crd_skip, crd->crd_len, data);
if (crd->crd_flags & CRD_F_COMP)
result = cxf->compress(data, crd->crd_len, &out);
@@ -556,13 +550,13 @@
}
}
- COPYBACK(outtype, buf, crd->crd_skip, result, out);
+ crypto_copyback(flags, buf, crd->crd_skip, result, out);
if (result < crd->crd_len) {
adj = result - crd->crd_len;
- if (outtype == CRYPTO_BUF_MBUF) {
+ if (flags & CRYPTO_F_IMBUF) {
adj = result - crd->crd_len;
m_adj((struct mbuf *)buf, adj);
- } else {
+ } else if (flags & CRYPTO_F_IOV) {
struct uio *uio = (struct uio *)buf;
int ind;
@@ -590,14 +584,14 @@
* Generate a new software session.
*/
static int
-swcr_newsession(void *arg, u_int32_t *sid, struct cryptoini *cri)
+swcr_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
{
struct swcr_data **swd;
struct auth_hash *axf;
struct enc_xform *txf;
struct comp_algo *cxf;
u_int32_t i;
- int k, error;
+ int error;
if (sid == NULL || cri == NULL)
return EINVAL;
@@ -628,7 +622,7 @@
}
/* Copy existing sessions */
- if (swcr_sessions) {
+ if (swcr_sessions != NULL) {
bcopy(swcr_sessions, swd,
(swcr_sesnum / 2) * sizeof(struct swcr_data *));
free(swcr_sessions, M_CRYPTO_DATA);
@@ -644,7 +638,7 @@
MALLOC(*swd, struct swcr_data *, sizeof(struct swcr_data),
M_CRYPTO_DATA, M_NOWAIT|M_ZERO);
if (*swd == NULL) {
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return ENOBUFS;
}
@@ -667,77 +661,65 @@
case CRYPTO_RIJNDAEL128_CBC:
txf = &enc_xform_rijndael128;
goto enccommon;
+ case CRYPTO_CAMELLIA_CBC:
+ txf = &enc_xform_camellia;
+ goto enccommon;
case CRYPTO_NULL_CBC:
txf = &enc_xform_null;
goto enccommon;
enccommon:
- error = txf->setkey(&((*swd)->sw_kschedule),
- cri->cri_key, cri->cri_klen / 8);
- if (error) {
- swcr_freesession(NULL, i);
- return error;
+ if (cri->cri_key != NULL) {
+ error = txf->setkey(&((*swd)->sw_kschedule),
+ cri->cri_key, cri->cri_klen / 8);
+ if (error) {
+ swcr_freesession(dev, i);
+ return error;
+ }
}
(*swd)->sw_exf = txf;
break;
case CRYPTO_MD5_HMAC:
- axf = &auth_hash_hmac_md5_96;
+ axf = &auth_hash_hmac_md5;
goto authcommon;
case CRYPTO_SHA1_HMAC:
- axf = &auth_hash_hmac_sha1_96;
+ axf = &auth_hash_hmac_sha1;
goto authcommon;
- case CRYPTO_SHA2_HMAC:
- if (cri->cri_klen == 256)
- axf = &auth_hash_hmac_sha2_256;
- else if (cri->cri_klen == 384)
- axf = &auth_hash_hmac_sha2_384;
- else if (cri->cri_klen == 512)
- axf = &auth_hash_hmac_sha2_512;
- else {
- swcr_freesession(NULL, i);
- return EINVAL;
- }
+ case CRYPTO_SHA2_256_HMAC:
+ axf = &auth_hash_hmac_sha2_256;
+ goto authcommon;
+ case CRYPTO_SHA2_384_HMAC:
+ axf = &auth_hash_hmac_sha2_384;
+ goto authcommon;
+ case CRYPTO_SHA2_512_HMAC:
+ axf = &auth_hash_hmac_sha2_512;
goto authcommon;
case CRYPTO_NULL_HMAC:
axf = &auth_hash_null;
goto authcommon;
case CRYPTO_RIPEMD160_HMAC:
- axf = &auth_hash_hmac_ripemd_160_96;
+ axf = &auth_hash_hmac_ripemd_160;
authcommon:
(*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
if ((*swd)->sw_ictx == NULL) {
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return ENOBUFS;
}
(*swd)->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
if ((*swd)->sw_octx == NULL) {
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return ENOBUFS;
}
-
- for (k = 0; k < cri->cri_klen / 8; k++)
- cri->cri_key[k] ^= HMAC_IPAD_VAL;
-
- axf->Init((*swd)->sw_ictx);
- axf->Update((*swd)->sw_ictx, cri->cri_key,
- cri->cri_klen / 8);
- axf->Update((*swd)->sw_ictx, hmac_ipad_buffer,
- HMAC_BLOCK_LEN - (cri->cri_klen / 8));
-
- for (k = 0; k < cri->cri_klen / 8; k++)
- cri->cri_key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
-
- axf->Init((*swd)->sw_octx);
- axf->Update((*swd)->sw_octx, cri->cri_key,
- cri->cri_klen / 8);
- axf->Update((*swd)->sw_octx, hmac_opad_buffer,
- HMAC_BLOCK_LEN - (cri->cri_klen / 8));
-
- for (k = 0; k < cri->cri_klen / 8; k++)
- cri->cri_key[k] ^= HMAC_OPAD_VAL;
+
+ if (cri->cri_key != NULL) {
+ swcr_authprepare(axf, *swd, cri->cri_key,
+ cri->cri_klen);
+ }
+
+ (*swd)->sw_mlen = cri->cri_mlen;
(*swd)->sw_axf = axf;
break;
@@ -751,24 +733,24 @@
(*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
if ((*swd)->sw_ictx == NULL) {
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return ENOBUFS;
}
- /* Store the key so we can "append" it to the payload */
- (*swd)->sw_octx = malloc(cri->cri_klen / 8, M_CRYPTO_DATA,
- M_NOWAIT);
+ (*swd)->sw_octx = malloc(cri->cri_klen / 8,
+ M_CRYPTO_DATA, M_NOWAIT);
if ((*swd)->sw_octx == NULL) {
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return ENOBUFS;
}
-
- (*swd)->sw_klen = cri->cri_klen / 8;
- bcopy(cri->cri_key, (*swd)->sw_octx, cri->cri_klen / 8);
- axf->Init((*swd)->sw_ictx);
- axf->Update((*swd)->sw_ictx, cri->cri_key,
- cri->cri_klen / 8);
- axf->Final(NULL, (*swd)->sw_ictx);
+
+ /* Store the key so we can "append" it to the payload */
+ if (cri->cri_key != NULL) {
+ swcr_authprepare(axf, *swd, cri->cri_key,
+ cri->cri_klen);
+ }
+
+ (*swd)->sw_mlen = cri->cri_mlen;
(*swd)->sw_axf = axf;
break;
#ifdef notdef
@@ -782,11 +764,12 @@
(*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
if ((*swd)->sw_ictx == NULL) {
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return ENOBUFS;
}
axf->Init((*swd)->sw_ictx);
+ (*swd)->sw_mlen = cri->cri_mlen;
(*swd)->sw_axf = axf;
break;
#endif
@@ -795,7 +778,7 @@
(*swd)->sw_cxf = cxf;
break;
default:
- swcr_freesession(NULL, i);
+ swcr_freesession(dev, i);
return EINVAL;
}
@@ -810,7 +793,7 @@
* Free a session.
*/
static int
-swcr_freesession(void *arg, u_int64_t tid)
+swcr_freesession(device_t dev, u_int64_t tid)
{
struct swcr_data *swd;
struct enc_xform *txf;
@@ -836,6 +819,7 @@
case CRYPTO_CAST_CBC:
case CRYPTO_SKIPJACK_CBC:
case CRYPTO_RIJNDAEL128_CBC:
+ case CRYPTO_CAMELLIA_CBC:
case CRYPTO_NULL_CBC:
txf = swd->sw_exf;
@@ -845,7 +829,9 @@
case CRYPTO_MD5_HMAC:
case CRYPTO_SHA1_HMAC:
- case CRYPTO_SHA2_HMAC:
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
case CRYPTO_RIPEMD160_HMAC:
case CRYPTO_NULL_HMAC:
axf = swd->sw_axf;
@@ -896,12 +882,11 @@
* Process a software request.
*/
static int
-swcr_process(void *arg, struct cryptop *crp, int hint)
+swcr_process(device_t dev, struct cryptop *crp, int hint)
{
struct cryptodesc *crd;
struct swcr_data *sw;
u_int32_t lid;
- int type;
/* Sanity check */
if (crp == NULL)
@@ -918,14 +903,6 @@
goto done;
}
- if (crp->crp_flags & CRYPTO_F_IMBUF) {
- type = CRYPTO_BUF_MBUF;
- } else if (crp->crp_flags & CRYPTO_F_IOV) {
- type = CRYPTO_BUF_IOV;
- } else {
- type = CRYPTO_BUF_CONTIG;
- }
-
/* Go through crypto descriptors, processing as we go */
for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
/*
@@ -955,8 +932,9 @@
case CRYPTO_CAST_CBC:
case CRYPTO_SKIPJACK_CBC:
case CRYPTO_RIJNDAEL128_CBC:
+ case CRYPTO_CAMELLIA_CBC:
if ((crp->crp_etype = swcr_encdec(crd, sw,
- crp->crp_buf, type)) != 0)
+ crp->crp_buf, crp->crp_flags)) != 0)
goto done;
break;
case CRYPTO_NULL_CBC:
@@ -964,21 +942,23 @@
break;
case CRYPTO_MD5_HMAC:
case CRYPTO_SHA1_HMAC:
- case CRYPTO_SHA2_HMAC:
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
case CRYPTO_RIPEMD160_HMAC:
case CRYPTO_NULL_HMAC:
case CRYPTO_MD5_KPDK:
case CRYPTO_SHA1_KPDK:
case CRYPTO_MD5:
case CRYPTO_SHA1:
- if ((crp->crp_etype = swcr_authcompute(crp, crd, sw,
- crp->crp_buf, type)) != 0)
+ if ((crp->crp_etype = swcr_authcompute(crd, sw,
+ crp->crp_buf, crp->crp_flags)) != 0)
goto done;
break;
case CRYPTO_DEFLATE_COMP:
if ((crp->crp_etype = swcr_compdec(crd, sw,
- crp->crp_buf, type)) != 0)
+ crp->crp_buf, crp->crp_flags)) != 0)
goto done;
else
crp->crp_olen = (int)sw->sw_size;
@@ -996,19 +976,37 @@
return 0;
}
-/*
- * Initialize the driver, called from the kernel main().
- */
static void
-swcr_init(void)
+swcr_identify(device_t *dev, device_t parent)
+{
+ /* NB: order 10 is so we get attached after h/w devices */
+ if (device_find_child(parent, "cryptosoft", -1) == NULL &&
+ BUS_ADD_CHILD(parent, 10, "cryptosoft", -1) == 0)
+ panic("cryptosoft: could not attach");
+}
+
+static int
+swcr_probe(device_t dev)
+{
+ device_set_desc(dev, "software crypto");
+ return (0);
+}
+
+static int
+swcr_attach(device_t dev)
{
- swcr_id = crypto_get_driverid(CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
- if (swcr_id < 0)
- panic("Software crypto device cannot initialize!");
- crypto_register(swcr_id, CRYPTO_DES_CBC,
- 0, 0, swcr_newsession, swcr_freesession, swcr_process, NULL);
+ memset(hmac_ipad_buffer, HMAC_IPAD_VAL, HMAC_MAX_BLOCK_LEN);
+ memset(hmac_opad_buffer, HMAC_OPAD_VAL, HMAC_MAX_BLOCK_LEN);
+
+ swcr_id = crypto_get_driverid(dev,
+ CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
+ if (swcr_id < 0) {
+ device_printf(dev, "cannot initialize!");
+ return ENOMEM;
+ }
#define REGISTER(alg) \
- crypto_register(swcr_id, alg, 0,0,NULL,NULL,NULL,NULL)
+ crypto_register(swcr_id, alg, 0,0)
+ REGISTER(CRYPTO_DES_CBC);
REGISTER(CRYPTO_3DES_CBC);
REGISTER(CRYPTO_BLF_CBC);
REGISTER(CRYPTO_CAST_CBC);
@@ -1016,7 +1014,9 @@
REGISTER(CRYPTO_NULL_CBC);
REGISTER(CRYPTO_MD5_HMAC);
REGISTER(CRYPTO_SHA1_HMAC);
- REGISTER(CRYPTO_SHA2_HMAC);
+ REGISTER(CRYPTO_SHA2_256_HMAC);
+ REGISTER(CRYPTO_SHA2_384_HMAC);
+ REGISTER(CRYPTO_SHA2_512_HMAC);
REGISTER(CRYPTO_RIPEMD160_HMAC);
REGISTER(CRYPTO_NULL_HMAC);
REGISTER(CRYPTO_MD5_KPDK);
@@ -1024,7 +1024,50 @@
REGISTER(CRYPTO_MD5);
REGISTER(CRYPTO_SHA1);
REGISTER(CRYPTO_RIJNDAEL128_CBC);
+ REGISTER(CRYPTO_CAMELLIA_CBC);
REGISTER(CRYPTO_DEFLATE_COMP);
#undef REGISTER
+
+ return 0;
}
-SYSINIT(cryptosoft_init, SI_SUB_PSEUDO, SI_ORDER_ANY, swcr_init, NULL)
+
+static void
+swcr_detach(device_t dev)
+{
+ crypto_unregister_all(swcr_id);
+ if (swcr_sessions != NULL)
+ FREE(swcr_sessions, M_CRYPTO_DATA);
+}
+
+static device_method_t swcr_methods[] = {
+ DEVMETHOD(device_identify, swcr_identify),
+ DEVMETHOD(device_probe, swcr_probe),
+ DEVMETHOD(device_attach, swcr_attach),
+ DEVMETHOD(device_detach, swcr_detach),
+
+ DEVMETHOD(cryptodev_newsession, swcr_newsession),
+ DEVMETHOD(cryptodev_freesession,swcr_freesession),
+ DEVMETHOD(cryptodev_process, swcr_process),
+
+ {0, 0},
+};
+
+static driver_t swcr_driver = {
+ "cryptosoft",
+ swcr_methods,
+ 0, /* NB: no softc */
+};
+static devclass_t swcr_devclass;
+
+/*
+ * NB: We explicitly reference the crypto module so we
+ * get the necessary ordering when built as a loadable
+ * module. This is required because we bundle the crypto
+ * module code together with the cryptosoft driver (otherwise
+ * normal module dependencies would handle things).
+ */
+extern int crypto_modevent(struct module *, int, void *);
+/* XXX where to attach */
+DRIVER_MODULE(cryptosoft, nexus, swcr_driver, swcr_devclass, crypto_modevent,0);
+MODULE_VERSION(cryptosoft, 1);
+MODULE_DEPEND(cryptosoft, crypto, 1, 1, 1);
More information about the Midnightbsd-cvs
mailing list