summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/conf/files12
-rw-r--r--sys/net/if_ieee80211subr.c3691
-rw-r--r--sys/net80211/ieee80211.c1051
-rw-r--r--sys/net80211/ieee80211.h548
-rw-r--r--sys/net80211/ieee80211_compat.c54
-rw-r--r--sys/net80211/ieee80211_compat.h64
-rw-r--r--sys/net80211/ieee80211_crypto.c364
-rw-r--r--sys/net80211/ieee80211_crypto.h53
-rw-r--r--sys/net80211/ieee80211_input.c1607
-rw-r--r--sys/net80211/ieee80211_ioctl.c1384
-rw-r--r--sys/net80211/ieee80211_ioctl.h211
-rw-r--r--sys/net80211/ieee80211_node.c877
-rw-r--r--sys/net80211/ieee80211_node.h206
-rw-r--r--sys/net80211/ieee80211_output.c671
-rw-r--r--sys/net80211/ieee80211_proto.c566
-rw-r--r--sys/net80211/ieee80211_proto.h83
-rw-r--r--sys/net80211/ieee80211_radiotap.h213
-rw-r--r--sys/net80211/ieee80211_rssadapt.c409
-rw-r--r--sys/net80211/ieee80211_rssadapt.h102
-rw-r--r--sys/net80211/ieee80211_var.h320
20 files changed, 8793 insertions, 3693 deletions
diff --git a/sys/conf/files b/sys/conf/files
index e0bf11f7b2e..ab754407126 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.305 2004/06/20 17:19:26 itojun Exp $
+# $OpenBSD: files,v 1.306 2004/06/22 22:53:51 millert Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -693,7 +693,15 @@ file net/slcompress.c sl | ppp | strip
file net/if_enc.c enc needs-count
file net/if_gre.c gre needs-count
#file net/if_ieee1394subr.c ieee1394
-file net/if_ieee80211subr.c wlan
+file net80211/ieee80211.c wlan
+file net80211/ieee80211_compat.c wlan
+file net80211/ieee80211_crypto.c wlan
+file net80211/ieee80211_input.c wlan
+file net80211/ieee80211_ioctl.c wlan
+file net80211/ieee80211_node.c wlan
+file net80211/ieee80211_output.c wlan
+file net80211/ieee80211_proto.c wlan
+file net80211/ieee80211_rssadapt.c wlan
file netccitt/ccitt_proto.c ccitt
file netccitt/hd_debug.c hdlc
file netccitt/hd_input.c hdlc
diff --git a/sys/net/if_ieee80211subr.c b/sys/net/if_ieee80211subr.c
deleted file mode 100644
index a9a1f22698e..00000000000
--- a/sys/net/if_ieee80211subr.c
+++ /dev/null
@@ -1,3691 +0,0 @@
-/* $OpenBSD: if_ieee80211subr.c,v 1.2 2004/01/26 03:35:36 fgsch Exp $ */
-/* $NetBSD: if_ieee80211subr.c,v 1.43 2003/07/06 20:54:25 dyoung Exp $ */
-/* $FreeBSD: src/sys/net/if_ieee80211subr.c,v 1.4 2003/01/21 08:55:59 alfred Exp $ */
-
-/*-
- * Copyright (c) 2001 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Atsushi Onoe.
- *
- * 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 the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
- */
-
-/*
- * IEEE 802.11 generic handler
- */
-
-#include "bpfilter.h"
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/mbuf.h>
-#include <sys/malloc.h>
-#include <sys/kernel.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/errno.h>
-#include <sys/device.h>
-#include <sys/proc.h>
-
-#include <machine/endian.h>
-
-#include <dev/rndvar.h>
-#include <crypto/arc4.h>
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/if_media.h>
-#include <net/if_llc.h>
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#include <net/if_ieee80211.h>
-
-#include <dev/ic/if_wi_ieee.h> /* XXX */
-
-#if NBPFILTER > 0
-#include <net/bpf.h>
-#endif
-
-#ifdef IEEE80211_DEBUG
-int ieee80211_debug = 0;
-#define DPRINTF(X) if (ieee80211_debug) printf X
-#define DPRINTF2(X) if (ieee80211_debug>1) printf X
-#else
-#define DPRINTF(X)
-#define DPRINTF2(X)
-#endif
-
-static int ieee80211_send_prreq(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-static int ieee80211_send_prresp(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-static int ieee80211_send_auth(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-static int ieee80211_send_deauth(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-static int ieee80211_send_asreq(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-static int ieee80211_send_asresp(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-static int ieee80211_send_disassoc(struct ieee80211com *,
- struct ieee80211_node *, int, int);
-
-static void ieee80211_recv_beacon(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-static void ieee80211_recv_prreq(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-static void ieee80211_recv_auth(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-static void ieee80211_recv_asreq(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-static void ieee80211_recv_asresp(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-static void ieee80211_recv_disassoc(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-static void ieee80211_recv_deauth(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-
-static void ieee80211_recv_pspoll(struct ieee80211com *,
- struct mbuf *, int, u_int32_t);
-
-static void ieee80211_crc_init(void);
-static u_int32_t ieee80211_crc_update(u_int32_t, u_int8_t *, int);
-
-static const char *ieee80211_mgt_subtype_name[] = {
- "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
- "probe_req", "probe_resp", "reserved#6", "reserved#7",
- "beacon", "atim", "disassoc", "auth",
- "deauth", "reserved#13", "reserved#14", "reserved#15"
-};
-
-void
-ieee80211_ifattach(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
- int i, rate, mword;
- struct ifmediareq imr;
-
- bcopy(ic->ic_myaddr, ((struct arpcom *)ifp)->ac_enaddr,
- ETHER_ADDR_LEN);
- ether_ifattach(ifp);
-#if NBPFILTER > 0
- bpfattach(&ic->ic_rawbpf, ifp, DLT_IEEE802_11,
- sizeof(struct ieee80211_frame_addr4));
-#endif
- ieee80211_crc_init();
- memcpy(ic->ic_chan_active, ic->ic_chan_avail,
- sizeof(ic->ic_chan_active));
- if (isclr(ic->ic_chan_active, ic->ic_ibss_chan)) {
- for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
- if (isset(ic->ic_chan_active, i)) {
- ic->ic_ibss_chan = i;
- break;
- }
- }
- }
- /* ic->ic_bss.ni_chan must always be a valid channel */
- if (isclr(ic->ic_chan_active, ic->ic_bss.ni_chan))
- ic->ic_bss.ni_chan = ic->ic_ibss_chan;
-
- ic->ic_des_chan = IEEE80211_CHAN_ANY;
- ic->ic_fixed_rate = -1;
- if (ic->ic_lintval == 0)
- ic->ic_lintval = 100; /* default sleep */
- TAILQ_INIT(&ic->ic_node);
- rate = 0;
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- if (ic->ic_sup_rates[i] != 0)
- rate = (ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) / 2;
- }
- if (rate)
- ifp->if_baudrate = IF_Mbps(rate);
- ifp->if_hdrlen = sizeof(struct ieee80211_frame);
-
- if (ic->ic_max_aid == 0)
- ic->ic_max_aid = IEEE80211_MAX_AID;
-
- /* initialize management frame handlers */
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_BEACON
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_prreq;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_AUTH
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_auth;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_deauth;
- ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_disassoc;
-
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prreq;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prresp;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_AUTH
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_auth;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_deauth;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp;
- ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC
- >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_disassoc;
-
-#define ADD(s, o) ifmedia_add(&ic->ic_media, \
- IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL)
-
- ifmedia_init(&ic->ic_media, 0, ieee80211_media_change,
- ieee80211_media_status);
- ADD(IFM_AUTO, 0); /* infrastructure */
- if (ic->ic_flags & IEEE80211_F_HASAHDEMO)
- ADD(IFM_AUTO, IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_flags & IEEE80211_F_HASHOSTAP)
- ADD(IFM_AUTO, IFM_IEEE80211_HOSTAP);
- if (ic->ic_flags & IEEE80211_F_HASIBSS)
- ADD(IFM_AUTO, IFM_IEEE80211_ADHOC);
- if (ic->ic_flags & IEEE80211_F_HASMONITOR)
- ADD(IFM_AUTO, IFM_IEEE80211_MONITOR);
-
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- rate = ic->ic_sup_rates[i];
- mword = ieee80211_rate2media(rate, ic->ic_phytype);
- if (mword == 0)
- continue;
- ADD(mword, 0); /* infrastructure */
- if (ic->ic_flags & IEEE80211_F_HASAHDEMO)
- ADD(mword, IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_flags & IEEE80211_F_HASHOSTAP)
- ADD(mword, IFM_IEEE80211_HOSTAP);
- if (ic->ic_flags & IEEE80211_F_HASIBSS)
- ADD(mword, IFM_IEEE80211_ADHOC);
- if (ic->ic_flags & IEEE80211_F_HASMONITOR)
- ADD(mword, IFM_IEEE80211_MONITOR);
- }
- (*ic->ic_media.ifm_status)(ifp, &imr);
- ifmedia_set(&ic->ic_media, imr.ifm_active);
-#undef ADD
-}
-
-void
-ieee80211_ifdetach(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
- int s;
-
- s = splnet();
- IF_PURGE(&ic->ic_mgtq);
- IF_PURGE(&ic->ic_pwrsaveq);
- if (ic->ic_wep_ctx != NULL) {
- free(ic->ic_wep_ctx, M_DEVBUF);
- ic->ic_wep_ctx = NULL;
- }
- ieee80211_free_allnodes(ic);
- ifmedia_delete_instance(&ic->ic_media, IFM_INST_ANY);
-#if NBPFILTER > 0
- bpfdetach(ifp);
-#endif
- ether_ifdetach(ifp);
- splx(s);
-}
-
-void
-ieee80211_input(struct ifnet *ifp, struct mbuf *m, int rssi, u_int32_t rstamp)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_node *ni;
- struct ieee80211_frame *wh;
- struct ether_header *eh;
- void (*rh)(struct ieee80211com *, struct mbuf *, int, u_int);
- struct mbuf *m1;
- int error, len;
- u_int8_t dir, subtype;
- u_int8_t *bssid;
- u_int16_t rxseq;
-
- wh = mtod(m, struct ieee80211_frame *);
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: receive packet with wrong version: %x\n",
- ifp->if_xname, wh->i_fc[0]);
- goto err;
- }
-
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
-
- if (ic->ic_state != IEEE80211_S_SCAN) {
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ni = &ic->ic_bss;
- if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
- DPRINTF2(("ieee80211_input: other bss %s\n",
- ether_sprintf(wh->i_addr2)));
- /* not interested in */
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- if (dir == IEEE80211_FC1_DIR_NODS)
- bssid = wh->i_addr3;
- else
- bssid = wh->i_addr1;
- if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss.ni_bssid) &&
- !IEEE80211_ADDR_EQ(bssid, etherbroadcastaddr) &&
- (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
- IEEE80211_FC0_TYPE_CTL) {
- /* not interested in */
- DPRINTF2(("ieee80211_input: other bss %s\n",
- ether_sprintf(wh->i_addr3)));
- goto out;
- }
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
- DPRINTF2(("ieee80211_input: unknown src %s\n",
- ether_sprintf(wh->i_addr2)));
- ni = &ic->ic_bss; /* XXX allocate? */
- }
- break;
- case IEEE80211_M_MONITOR:
- goto out;
- }
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- rxseq = ni->ni_rxseq;
- ni->ni_rxseq =
- letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
- /* TODO: fragment */
- if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
- rxseq == ni->ni_rxseq) {
- /* duplicate, silently discarded */
- goto out;
- }
- ni->ni_inact = 0;
- }
-
- if (ic->ic_set_tim != NULL &&
- (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT)
- && ni->ni_pwrsave == 0) {
- /* turn on power save mode */
-
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: power save mode on for %s\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2));
-
- ni->ni_pwrsave = IEEE80211_PS_SLEEP;
- }
- if (ic->ic_set_tim != NULL &&
- (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0 &&
- ni->ni_pwrsave != 0) {
- /* turn off power save mode, dequeue stored packets */
-
- ni->ni_pwrsave = 0;
- if (ic->ic_set_tim)
- ic->ic_set_tim(ic, ni->ni_associd, 0);
-
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: power save mode off for %s\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2));
-
- while (!IF_IS_EMPTY(&ni->ni_savedq)) {
- struct mbuf *m;
- IF_DEQUEUE(&ni->ni_savedq, m);
- IF_ENQUEUE(&ic->ic_pwrsaveq, m);
- (*ifp->if_start)(ifp);
- }
- }
-
- switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
- case IEEE80211_FC0_TYPE_DATA:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- if (dir != IEEE80211_FC1_DIR_FROMDS)
- goto out;
- if ((ifp->if_flags & IFF_SIMPLEX) &&
- IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- IEEE80211_ADDR_EQ(wh->i_addr3,
- ic->ic_myaddr)) {
- /*
- * In IEEE802.11 network, multicast packet
- * sent from me is broadcasted from AP.
- * It should be silently discarded for
- * SIMPLEX interface.
- */
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- if (dir != IEEE80211_FC1_DIR_NODS)
- goto out;
- break;
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_TODS)
- goto out;
- /* check if source STA is associated */
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
- DPRINTF(("ieee80211_input: "
- "data from unknown src %s\n",
- ether_sprintf(wh->i_addr2)));
- if ((ni = ieee80211_alloc_node(ic, wh->i_addr2,
- 1)) != NULL) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_AUTHED);
- ieee80211_free_node(ic, ni);
- }
- goto err;
- }
- if (ni->ni_associd == 0) {
- DPRINTF(("ieee80211_input: "
- "data from unassoc src %s\n",
- ether_sprintf(wh->i_addr2)));
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_NOT_ASSOCED);
- goto err;
- }
- break;
- case IEEE80211_M_MONITOR:
- /* Should never get here */
- break;
- }
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- m = ieee80211_wep_crypt(ifp, m, 0);
- if (m == NULL)
- goto err;
- wh = mtod(m, struct ieee80211_frame *);
- } else
- goto out;
- }
- /* copy to listener after decrypt */
-#if NBPFILTER > 0
- if (ic->ic_rawbpf)
- bpf_mtap(ic->ic_rawbpf, m);
-#endif
- m = ieee80211_decap(ifp, m);
- if (m == NULL)
- goto err;
- ifp->if_ipackets++;
-
- /* perform as a bridge within the AP */
- m1 = NULL;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- eh = mtod(m, struct ether_header *);
- if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
- m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
- if (m1 == NULL)
- ifp->if_oerrors++;
- else
- m1->m_flags |= M_MCAST;
- } else {
- ni = ieee80211_find_node(ic, eh->ether_dhost);
- if (ni != NULL && ni->ni_associd != 0) {
- m1 = m;
- m = NULL;
- }
- }
- if (m1 != NULL) {
- len = m1->m_pkthdr.len;
- IFQ_ENQUEUE(&ifp->if_snd, m1, NULL, error);
- if (error)
- ifp->if_oerrors++;
- else {
- if (m != NULL)
- ifp->if_omcasts++;
- ifp->if_obytes += len;
- }
- }
- }
- if (m != NULL) {
-#if NBPFILTER > 0
- /*
- * If we forward packet into transmitter of the AP,
- * we don't need to duplicate for DLT_EN10MB.
- */
- if (ifp->if_bpf && m1 == NULL)
- bpf_mtap(ifp->if_bpf, m);
-#endif
- ether_input_mbuf(ifp, m);
- }
- return;
-
- case IEEE80211_FC0_TYPE_MGT:
- if (dir != IEEE80211_FC1_DIR_NODS)
- goto err;
- if (ic->ic_opmode == IEEE80211_M_AHDEMO)
- goto out;
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-
- /* drop frames without interest */
- if (ic->ic_state == IEEE80211_S_SCAN) {
- if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
- subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
- goto out;
- } else {
- if (ic->ic_opmode != IEEE80211_M_IBSS &&
- subtype == IEEE80211_FC0_SUBTYPE_BEACON)
- goto out;
- }
-
- if (ifp->if_flags & IFF_DEBUG) {
- /* avoid to print too many frames */
- int doprint = 0;
-
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_BEACON:
- if (ic->ic_state == IEEE80211_S_SCAN)
- doprint = 1;
- break;
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- doprint = 1;
- break;
- default:
- doprint = 1;
- break;
- }
-#ifdef IEEE80211_DEBUG
- doprint += ieee80211_debug;
-#endif
- if (doprint)
- printf("%s: received %s from %s rssi %d\n",
- ifp->if_xname,
- ieee80211_mgt_subtype_name[subtype
- >> IEEE80211_FC0_SUBTYPE_SHIFT],
- ether_sprintf(wh->i_addr2), rssi);
- }
-#if NBPFILTER > 0
- if (ic->ic_rawbpf)
- bpf_mtap(ic->ic_rawbpf, m);
-#endif
- rh = ic->ic_recv_mgmt[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT];
- if (rh != NULL)
- (*rh)(ic, m, rssi, rstamp);
- m_freem(m);
- return;
-
- case IEEE80211_FC0_TYPE_CTL:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- goto out;
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- if (subtype == IEEE80211_FC0_SUBTYPE_PS_POLL) {
- /* Dump out a single packet from the host */
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: got power save probe from %s\n",
- ifp->if_xname,
- ether_sprintf(wh->i_addr2));
- ieee80211_recv_pspoll(ic, m, rssi, rstamp);
- }
- goto out;
-
- default:
- DPRINTF(("ieee80211_input: bad type %x\n", wh->i_fc[0]));
- /* should not come here */
- break;
- }
- err:
- ifp->if_ierrors++;
- out:
- if (m != NULL) {
-#if NBPFILTER > 0
- if (ic->ic_rawbpf)
- bpf_mtap(ic->ic_rawbpf, m);
-#endif
- m_freem(m);
- }
-}
-
-int
-ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
- struct mbuf *m, int type)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_frame *wh;
-
- if (ni == NULL)
- ni = &ic->ic_bss;
- ni->ni_inact = 0;
- M_PREPEND(m, IEEE80211_HEADER_LEN(ic), M_DONTWAIT);
- if (m == NULL)
- return (ENOMEM);
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
- wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
- *(u_int16_t *)wh->i_dur = 0;
- *(u_int16_t *)wh->i_seq =
- htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
- ni->ni_txseq++;
- IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
- IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
-
- if (ifp->if_flags & IFF_DEBUG) {
- /* avoid to print too many frames */
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
-#ifdef IEEE80211_DEBUG
- ieee80211_debug > 1 ||
-#endif
- (type & IEEE80211_FC0_SUBTYPE_MASK) !=
- IEEE80211_FC0_SUBTYPE_PROBE_RESP)
- printf("%s: sending %s to %s on channel %u\n",
- ifp->if_xname,
- ieee80211_mgt_subtype_name[
- (type & IEEE80211_FC0_SUBTYPE_MASK)
- >> IEEE80211_FC0_SUBTYPE_SHIFT],
- ether_sprintf(ni->ni_macaddr),
- ni->ni_chan);
- }
- IF_ENQUEUE(&ic->ic_mgtq, m);
- ifp->if_timer = 1;
- (*ifp->if_start)(ifp);
- return (0);
-}
-
-struct mbuf *
-ieee80211_encap(struct ifnet *ifp, struct mbuf *m)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ether_header eh;
- struct ieee80211_frame *wh;
- struct llc *llc;
- struct ieee80211_node *ni;
-
- if (m->m_len < sizeof(struct ether_header)) {
- m = m_pullup(m, sizeof(struct ether_header));
- if (m == NULL)
- return (NULL);
- }
- memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
-
- if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) &&
- (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_HOSTAP)) {
- ni = ieee80211_find_node(ic, eh.ether_dhost);
- if (ni == NULL)
- ni = &ic->ic_bss; /*XXX*/
- } else
- ni = &ic->ic_bss;
- ni->ni_inact = 0;
-
- 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;
- M_PREPEND(m, IEEE80211_HEADER_LEN(ic), M_DONTWAIT);
- if (m == NULL)
- return (NULL);
- 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;
- *(u_int16_t *)wh->i_seq =
- htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
- ni->ni_txseq++;
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
- IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
- IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
- IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
- IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
- IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
- IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
- break;
- case IEEE80211_M_HOSTAP:
- wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
- IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
- IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
- IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
- break;
- case IEEE80211_M_MONITOR:
- /* Should never get here! */
- m_freem(m);
- return (NULL);
- }
- return (m);
-}
-
-struct mbuf *
-ieee80211_decap(struct ifnet *ifp, struct mbuf *m)
-{
- struct ether_header *eh;
- struct ieee80211_frame wh;
- struct llc *llc;
-
- if (m->m_len < sizeof(wh) + sizeof(*llc)) {
- m = m_pullup(m, sizeof(wh) + sizeof(*llc));
- if (m == NULL)
- return (NULL);
- }
- memcpy(&wh, mtod(m, caddr_t), sizeof(wh));
- llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh));
- if (llc->llc_dsap == LLC_SNAP_LSAP && 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) {
- m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh));
- llc = NULL;
- } else {
- m_adj(m, sizeof(wh) - sizeof(*eh));
- }
- eh = mtod(m, struct ether_header *);
- switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
- case IEEE80211_FC1_DIR_NODS:
- IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
- IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
- break;
- case IEEE80211_FC1_DIR_TODS:
- IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
- IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
- break;
- case IEEE80211_FC1_DIR_FROMDS:
- IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
- IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
- break;
- case IEEE80211_FC1_DIR_DSTODS:
- /* not yet supported */
- DPRINTF(("ieee80211_decap: DS to DS\n"));
- m_freem(m);
- return (NULL);
- }
- if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
- struct mbuf *n, *n0, **np;
- caddr_t newdata;
- int off, pktlen;
-
- n0 = NULL;
- np = &n0;
- off = 0;
- pktlen = m->m_pkthdr.len;
- while (pktlen > off) {
- if (n0 == NULL) {
- MGETHDR(n, M_DONTWAIT, MT_DATA);
- if (n == NULL) {
- m_freem(m);
- return (NULL);
- }
- M_MOVE_PKTHDR(n, m);
- n->m_len = MHLEN;
- } else {
- MGET(n, M_DONTWAIT, MT_DATA);
- if (n == NULL) {
- m_freem(m);
- m_freem(n0);
- return (NULL);
- }
- n->m_len = MLEN;
- }
- if (pktlen - off >= MINCLSIZE) {
- MCLGET(n, M_DONTWAIT);
- if (n->m_flags & M_EXT)
- n->m_len = n->m_ext.ext_size;
- }
- if (n0 == NULL) {
- newdata =
- (caddr_t)ALIGN(n->m_data + sizeof(*eh)) -
- sizeof(*eh);
- n->m_len -= newdata - n->m_data;
- n->m_data = newdata;
- }
- if (n->m_len > pktlen - off)
- n->m_len = pktlen - off;
- m_copydata(m, off, n->m_len, mtod(n, caddr_t));
- off += n->m_len;
- *np = n;
- np = &n->m_next;
- }
- m_freem(m);
- m = n0;
- }
- if (llc != NULL) {
- eh = mtod(m, struct ether_header *);
- eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
- }
- return (m);
-}
-
-int
-ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ifreq *ifr = (struct ifreq *)data;
- int i, error = 0;
- struct ieee80211_nwid nwid;
- struct ieee80211_nwkey *nwkey;
- struct ieee80211_power *power;
- struct ieee80211_bssid *bssid;
- struct ieee80211_channel *chan;
- struct ieee80211_wepkey keys[IEEE80211_WEP_NKID];
- static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- switch (cmd) {
- case SIOCSIFMEDIA:
- case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
- break;
- case SIOCS80211NWID:
- if ((error = copyin(ifr->ifr_data, &nwid, sizeof(nwid))) != 0)
- break;
- if (nwid.i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_esslen = nwid.i_len;
- memcpy(ic->ic_des_essid, nwid.i_nwid, nwid.i_len);
- error = ENETRESET;
- break;
- case SIOCG80211NWID:
- memset(&nwid, 0, sizeof(nwid));
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- nwid.i_len = ic->ic_des_esslen;
- memcpy(nwid.i_nwid, ic->ic_des_essid, nwid.i_len);
- break;
- default:
- nwid.i_len = ic->ic_bss.ni_esslen;
- memcpy(nwid.i_nwid, ic->ic_bss.ni_essid, nwid.i_len);
- break;
- }
- error = copyout(&nwid, ifr->ifr_data, sizeof(nwid));
- break;
- case SIOCS80211NWKEY:
- nwkey = (struct ieee80211_nwkey *)data;
- if ((ic->ic_flags & IEEE80211_F_HASWEP) == 0 &&
- nwkey->i_wepon != IEEE80211_NWKEY_OPEN) {
- error = EINVAL;
- break;
- }
- /* check and copy keys */
- memset(keys, 0, sizeof(keys));
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- keys[i].wk_len = nwkey->i_key[i].i_keylen;
- if ((keys[i].wk_len > 0 &&
- keys[i].wk_len < IEEE80211_WEP_KEYLEN) ||
- keys[i].wk_len > sizeof(keys[i].wk_key)) {
- error = EINVAL;
- break;
- }
- if (keys[i].wk_len <= 0)
- continue;
- if ((error = copyin(nwkey->i_key[i].i_keydat,
- keys[i].wk_key, keys[i].wk_len)) != 0)
- break;
- }
- if (error)
- break;
- i = nwkey->i_defkid - 1;
- if (i < 0 || i >= IEEE80211_WEP_NKID ||
- keys[i].wk_len == 0 ||
- (keys[i].wk_len == -1 && ic->ic_nw_keys[i].wk_len == 0)) {
- if (nwkey->i_wepon != IEEE80211_NWKEY_OPEN) {
- error = EINVAL;
- break;
- }
- } else
- ic->ic_wep_txkey = i;
- /* save the key */
- if (nwkey->i_wepon == IEEE80211_NWKEY_OPEN)
- ic->ic_flags &= ~IEEE80211_F_WEPON;
- else
- ic->ic_flags |= IEEE80211_F_WEPON;
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- if (keys[i].wk_len < 0)
- continue;
- ic->ic_nw_keys[i].wk_len = keys[i].wk_len;
- memcpy(ic->ic_nw_keys[i].wk_key, keys[i].wk_key,
- sizeof(keys[i].wk_key));
- }
- error = ENETRESET;
- break;
- case SIOCG80211NWKEY:
- nwkey = (struct ieee80211_nwkey *)data;
- if (ic->ic_flags & IEEE80211_F_WEPON)
- nwkey->i_wepon = IEEE80211_NWKEY_WEP;
- else
- nwkey->i_wepon = IEEE80211_NWKEY_OPEN;
- nwkey->i_defkid = ic->ic_wep_txkey + 1;
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- if (nwkey->i_key[i].i_keydat == NULL)
- continue;
- /* do not show any keys to non-root user */
- if ((error = suser(curproc, 0)) != 0)
- break;
- nwkey->i_key[i].i_keylen = ic->ic_nw_keys[i].wk_len;
- if ((error = copyout(ic->ic_nw_keys[i].wk_key,
- nwkey->i_key[i].i_keydat,
- ic->ic_nw_keys[i].wk_len)) != 0)
- break;
- }
- break;
- case SIOCS80211POWER:
- power = (struct ieee80211_power *)data;
- ic->ic_lintval = power->i_maxsleep;
- if (power->i_enabled != 0) {
- if ((ic->ic_flags & IEEE80211_F_HASPMGT) == 0)
- error = EINVAL;
- else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
- }
- } else {
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
- ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
- }
- }
- break;
- case SIOCG80211POWER:
- power = (struct ieee80211_power *)data;
- power->i_enabled = (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0;
- power->i_maxsleep = ic->ic_lintval;
- break;
- case SIOCS80211BSSID:
- bssid = (struct ieee80211_bssid *)data;
- if (IEEE80211_ADDR_EQ(bssid->i_bssid, empty_macaddr))
- ic->ic_flags &= ~IEEE80211_F_DESBSSID;
- else {
- ic->ic_flags |= IEEE80211_F_DESBSSID;
- IEEE80211_ADDR_COPY(ic->ic_des_bssid, bssid->i_bssid);
- }
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- break;
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- error = ENETRESET;
- break;
- default:
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid,
- ic->ic_bss.ni_bssid))
- error = ENETRESET;
- break;
- }
- break;
- case SIOCG80211BSSID:
- bssid = (struct ieee80211_bssid *)data;
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- IEEE80211_ADDR_COPY(bssid->i_bssid,
- ic->ic_myaddr);
- else if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(bssid->i_bssid,
- ic->ic_des_bssid);
- else
- memset(bssid->i_bssid, 0, IEEE80211_ADDR_LEN);
- break;
- default:
- IEEE80211_ADDR_COPY(bssid->i_bssid,
- ic->ic_bss.ni_bssid);
- break;
- }
- break;
- case SIOCS80211CHANNEL:
- chan = (struct ieee80211_channel *)data;
- if (chan->i_channel == IEEE80211_CHAN_ANY)
- ic->ic_des_chan = IEEE80211_CHAN_ANY;
- else if (chan->i_channel > IEEE80211_CHAN_MAX ||
- isclr(ic->ic_chan_active, chan->i_channel)) {
- error = EINVAL;
- break;
- } else
- ic->ic_ibss_chan = ic->ic_des_chan = chan->i_channel;
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- error = ENETRESET;
- break;
- default:
- if (ic->ic_opmode == IEEE80211_M_STA) {
- if (ic->ic_des_chan != IEEE80211_CHAN_ANY &&
- ic->ic_bss.ni_chan != ic->ic_des_chan)
- error = ENETRESET;
- } else {
- if (ic->ic_bss.ni_chan != ic->ic_ibss_chan)
- error = ENETRESET;
- }
- break;
- }
- break;
- case SIOCG80211CHANNEL:
- chan = (struct ieee80211_channel *)data;
- chan->i_channel = ieee80211_get_channel(ic);
- break;
- case SIOCGWAVELAN:
- error = ieee80211_cfgget(ifp, cmd, data);
- break;
- case SIOCSWAVELAN:
- error = suser(curproc, 0);
- if (error)
- break;
- error = ieee80211_cfgset(ifp, cmd, data);
- break;
- default:
- error = ether_ioctl(ifp, &ic->ic_ac, cmd, data);
- break;
- }
- return (error);
-}
-
-void
-ieee80211_print_essid(u_int8_t *essid, int len)
-{
- int i;
- u_int8_t *p;
-
- if (len > IEEE80211_NWID_LEN)
- len = IEEE80211_NWID_LEN;
- /* determine printable or not */
- for (i = 0, p = essid; i < len; i++, p++) {
- if (*p < ' ' || *p > 0x7e)
- break;
- }
- if (i == len) {
- printf("\"");
- for (i = 0, p = essid; i < len; i++, p++)
- printf("%c", *p);
- printf("\"");
- } else {
- printf("0x");
- for (i = 0, p = essid; i < len; i++, p++)
- printf("%02x", *p);
- }
-}
-
-void
-ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
-{
- struct ieee80211_frame *wh;
- int i;
-
- wh = (struct ieee80211_frame *)buf;
- switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
- case IEEE80211_FC1_DIR_NODS:
- printf("NODS %s", ether_sprintf(wh->i_addr2));
- printf("->%s", ether_sprintf(wh->i_addr1));
- printf("(%s)", ether_sprintf(wh->i_addr3));
- break;
- case IEEE80211_FC1_DIR_TODS:
- printf("TODS %s", ether_sprintf(wh->i_addr2));
- printf("->%s", ether_sprintf(wh->i_addr3));
- printf("(%s)", ether_sprintf(wh->i_addr1));
- break;
- case IEEE80211_FC1_DIR_FROMDS:
- printf("FRDS %s", ether_sprintf(wh->i_addr3));
- printf("->%s", ether_sprintf(wh->i_addr1));
- printf("(%s)", ether_sprintf(wh->i_addr2));
- break;
- case IEEE80211_FC1_DIR_DSTODS:
- printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
- printf("->%s", ether_sprintf(wh->i_addr3));
- printf("(%s", ether_sprintf(wh->i_addr2));
- printf("->%s)", ether_sprintf(wh->i_addr1));
- break;
- }
- switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
- case IEEE80211_FC0_TYPE_DATA:
- printf(" data");
- break;
- case IEEE80211_FC0_TYPE_MGT:
- printf(" %s", ieee80211_mgt_subtype_name[
- (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
- >> IEEE80211_FC0_SUBTYPE_SHIFT]);
- break;
- default:
- printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
- break;
- }
- if (wh->i_fc[1] & IEEE80211_FC1_WEP)
- printf(" WEP");
- if (rate >= 0)
- printf(" %dM", rate / 2);
- if (rssi >= 0)
- printf(" +%d", rssi);
- printf("\n");
- if (len > 0) {
- for (i = 0; i < len; i++) {
- if ((i & 1) == 0)
- printf(" ");
- printf("%02x", buf[i]);
- }
- printf("\n");
- }
-}
-
-void
-ieee80211_watchdog(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
- if (ic->ic_mgt_timer) {
- if (--ic->ic_mgt_timer == 0)
- ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
- }
- if (ic->ic_inact_timer) {
- if (--ic->ic_inact_timer == 0) {
- struct ieee80211_node *ni, *nextbs;
- for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL; ) {
- if (++ni->ni_inact <= IEEE80211_INACT_MAX) {
- ni = TAILQ_NEXT(ni, ni_list);
- continue;
- }
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s deauthenticate"
- " (reason %d)\n",
- ifp->if_xname,
- ether_sprintf(ni->ni_macaddr),
- IEEE80211_REASON_AUTH_EXPIRE);
- nextbs = TAILQ_NEXT(ni, ni_list);
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_EXPIRE);
- ieee80211_free_node(ic, ni);
- ni = nextbs;
- }
- if (!TAILQ_EMPTY(&ic->ic_node))
- ic->ic_inact_timer = IEEE80211_INACT_WAIT;
- }
- }
- if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
- ifp->if_timer = 1;
-}
-
-static void
-ieee80211_begin_scan(struct ifnet *ifp, struct ieee80211_node *ni)
-{
- struct ieee80211com *ic = (void *)ifp;
- int i;
-
- memcpy(ic->ic_chan_scan, ic->ic_chan_active,
- sizeof(ic->ic_chan_scan));
-
- DPRINTF(("ieee80211_begin_scan: scanning"));
- for (i = 0; i < sizeof(ic->ic_chan_scan) * NBBY; i++) {
- if (!isset(ic->ic_chan_scan, i))
- continue;
- DPRINTF((" %d", i));
- ni->ni_chan = i;
- }
- DPRINTF(("\n"));
- clrbit(ic->ic_chan_scan, ni->ni_chan);
- ic->ic_flags |= IEEE80211_F_ASCAN;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
-}
-
-void
-ieee80211_next_scan(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
- int chan, s;
-
- s = splnet();
-
- chan = ic->ic_bss.ni_chan;
- for (;;) {
- chan = (chan + 1) % (IEEE80211_CHAN_MAX + 1);
- if (isset(ic->ic_chan_scan, chan))
- break;
- if (chan == ic->ic_bss.ni_chan) {
- DPRINTF(("ieee80211_next_scan: no chan available\n"));
- ieee80211_end_scan(ifp);
- splx(s);
- return;
- }
- }
- clrbit(ic->ic_chan_scan, chan);
- DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
- ic->ic_bss.ni_chan, chan));
- ic->ic_bss.ni_chan = chan;
- ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
- splx(s);
-}
-
-void
-ieee80211_create_ibss(struct ieee80211com *ic)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_node *ni;
- int i;
-
- ni = &ic->ic_bss;
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: creating ibss\n", ifp->if_xname);
- ic->ic_flags |= IEEE80211_F_SIBSS;
- ni->ni_nrate = 0;
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- if (ic->ic_sup_rates[i])
- ni->ni_rates[ni->ni_nrate++] =
- ic->ic_sup_rates[i];
- }
- IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
- ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */
- ni->ni_esslen = ic->ic_des_esslen;
- memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
- ni->ni_rssi = 0;
- ni->ni_rstamp = 0;
- memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
- ni->ni_intval = ic->ic_lintval;
- ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
- ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
- ni->ni_chan = ic->ic_ibss_chan;
- if (ic->ic_phytype == IEEE80211_T_FH) {
- ni->ni_fhdwell = 200; /* XXX */
- ni->ni_fhindex = 1;
- }
- ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
-}
-
-int
-ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
-{
- struct ifnet *ifp = &ic->ic_if;
- u_int8_t rate;
- int fail;
-
- fail = 0;
- if (isclr(ic->ic_chan_active, ni->ni_chan))
- fail |= 0x01;
- if (ic->ic_des_chan != IEEE80211_CHAN_ANY &&
- 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;
- } else {
- if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
- fail |= 0x02;
- }
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
- fail |= 0x04;
- } else {
- if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
- fail |= 0x04;
- }
- rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
- 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))
- fail |= 0x10;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
- fail |= 0x20;
- if (ifp->if_flags & IFF_DEBUG) {
- printf(" %c %s", fail ? '-' : '+',
- ether_sprintf(ni->ni_macaddr));
- printf(" %s%c", ether_sprintf(ni->ni_bssid),
- fail & 0x20 ? '!' : ' ');
- printf(" %3d%c", 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 ? "!" : "");
- }
- return (fail);
-}
-
-void
-ieee80211_end_scan(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_node *ni, *nextbs, *selbs;
- void *p;
-
- ni = TAILQ_FIRST(&ic->ic_node);
- if (ni == NULL) {
- DPRINTF(("ieee80211_end_scan: no scan candidate\n"));
- notfound:
- if (ic->ic_opmode == IEEE80211_M_IBSS &&
- (ic->ic_flags & IEEE80211_F_IBSSON) &&
- ic->ic_des_esslen != 0) {
- ieee80211_create_ibss(ic);
- return;
- }
- if (ic->ic_flags & IEEE80211_F_ASCAN) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: entering passive scan mode\n",
- ifp->if_xname);
- ic->ic_flags &= ~IEEE80211_F_ASCAN;
- }
- memcpy(ic->ic_chan_scan, ic->ic_chan_active,
- sizeof(ic->ic_chan_active));
- ieee80211_next_scan(ifp);
- return;
- }
- selbs = NULL;
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s:\tmacaddr bssid chan rssi rate flag wep essid\n",
- ifp->if_xname);
- for (; ni != NULL; ni = nextbs) {
- nextbs = TAILQ_NEXT(ni, ni_list);
- if (ni->ni_fails) {
- /*
- * The configuration of the access points may change
- * during my scan. So delete the entry for the AP
- * and retry to associate if there is another beacon.
- */
- if (ni->ni_fails++ > 2)
- ieee80211_free_node(ic, ni);
- continue;
- }
- if (ieee80211_match_bss(ic, ni) == 0) {
- if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi)
- selbs = ni;
- }
- }
- if (selbs == NULL)
- goto notfound;
- p = ic->ic_bss.ni_private;
- ic->ic_bss = *selbs;
- ic->ic_bss.ni_private = p;
- if (p != NULL && ic->ic_node_privlen)
- memcpy(p, selbs->ni_private, ic->ic_node_privlen);
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- ieee80211_fix_rate(ic, &ic->ic_bss, IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (ic->ic_bss.ni_nrate == 0) {
- selbs->ni_fails++;
- goto notfound;
- }
- ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
- } else
- ieee80211_new_state(ifp, IEEE80211_S_AUTH, -1);
-}
-
-int
-ieee80211_get_rate(struct ieee80211com *ic)
-{
- int rate;
- if (ic->ic_fixed_rate == -1) {
- if (ic->ic_state == IEEE80211_S_RUN)
- rate = ic->ic_bss.ni_rates[ic->ic_bss.ni_txrate];
- else
- rate = 0;
- } else
- rate = ic->ic_sup_rates[ic->ic_fixed_rate];
- return (rate & IEEE80211_RATE_VAL);
-}
-
-int
-ieee80211_get_channel(struct ieee80211com *ic)
-{
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- if (ic->ic_opmode == IEEE80211_M_STA)
- return (ic->ic_des_chan);
- else
- return (ic->ic_ibss_chan);
- case IEEE80211_S_SCAN: /* XXX could be confusing if ic_des_chan is
- * set
- */
- default:
- return (ic->ic_bss.ni_chan);
- }
-}
-
-
-struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr, int copy)
-{
- struct ieee80211_node *ni;
- int hash;
- int s;
-
- ni = malloc(sizeof(struct ieee80211_node) + ic->ic_node_privlen,
- M_DEVBUF, M_NOWAIT);
- if (ni == NULL)
- return (NULL);
- if (copy)
- memcpy(ni, &ic->ic_bss, sizeof(struct ieee80211_node));
- else
- memset(ni, 0, sizeof(struct ieee80211_node));
- IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
- if (ic->ic_node_privlen) {
- ni->ni_private = &ni[1];
- memset(ni->ni_private, 0, ic->ic_node_privlen);
- } else
- ni->ni_private = NULL;
-
- hash = IEEE80211_NODE_HASH(macaddr);
- s = splnet();
- TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
- LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
- splx(s);
- ic->ic_inact_timer = IEEE80211_INACT_WAIT;
- return (ni);
-}
-
-struct ieee80211_node *
-ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
-{
- struct ieee80211_node *ni;
- int hash;
- int s;
-
- hash = IEEE80211_NODE_HASH(macaddr);
- s = splnet();
- LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
- if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr))
- break;
- }
- splx(s);
- return (ni);
-}
-
-void
-ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
-{
- int s;
-
- s = splnet();
- if (ic->ic_node_free != NULL)
- (*ic->ic_node_free)(ic, ni);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
- TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
- LIST_REMOVE(ni, ni_hash);
- if (!IF_IS_EMPTY(&ni->ni_savedq)) {
- IF_PURGE(&ni->ni_savedq);
- if (ic->ic_set_tim)
- ic->ic_set_tim(ic, ni->ni_associd, 0);
- }
- splx(s);
- free(ni, M_DEVBUF);
- if (TAILQ_EMPTY(&ic->ic_node))
- ic->ic_inact_timer = 0;
-}
-
-void
-ieee80211_free_allnodes(struct ieee80211com *ic)
-{
- struct ieee80211_node *ni;
-
- while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
- ieee80211_free_node(ic, ni);
-}
-
-int
-ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
-{
- int i, j, ignore, error;
- int okrate, badrate;
- u_int8_t r;
-
- error = 0;
- okrate = badrate = 0;
- for (i = 0; i < ni->ni_nrate; ) {
- ignore = 0;
- if (flags & IEEE80211_F_DOSORT) {
- for (j = i + 1; j < ni->ni_nrate; j++) {
- if ((ni->ni_rates[i] & IEEE80211_RATE_VAL) >
- (ni->ni_rates[j] & IEEE80211_RATE_VAL)) {
- r = ni->ni_rates[i];
- ni->ni_rates[i] = ni->ni_rates[j];
- ni->ni_rates[j] = r;
- }
- }
- }
- r = ni->ni_rates[i] & IEEE80211_RATE_VAL;
- badrate = r;
- if (flags & IEEE80211_F_DOFRATE) {
- if (ic->ic_fixed_rate >= 0 &&
- r != (ic->ic_sup_rates[ic->ic_fixed_rate] &
- IEEE80211_RATE_VAL))
- ignore++;
- }
- if (flags & IEEE80211_F_DONEGO) {
- for (j = 0; j < IEEE80211_RATE_SIZE; j++) {
- if (r ==
- (ic->ic_sup_rates[j] & IEEE80211_RATE_VAL))
- break;
- }
- if (j == IEEE80211_RATE_SIZE) {
- if (ni->ni_rates[i] & IEEE80211_RATE_BASIC)
- error++;
- ignore++;
- }
- }
- if (flags & IEEE80211_F_DODEL) {
- if (ignore) {
- ni->ni_nrate--;
- for (j = i; j < ni->ni_nrate; j++)
- ni->ni_rates[j] = ni->ni_rates[j + 1];
- ni->ni_rates[j] = 0;
- continue;
- }
- }
- if (!ignore)
- okrate = ni->ni_rates[i];
- i++;
- }
- if (okrate == 0 || error != 0)
- return (badrate | IEEE80211_RATE_BASIC);
- return (okrate & IEEE80211_RATE_VAL);
-}
-
-static u_int8_t *
-ieee80211_add_rates(u_int8_t *frm, const u_int8_t rates[IEEE80211_RATE_SIZE])
-{
- int i, j;
-
- *frm++ = IEEE80211_ELEMID_RATES;
- j = 0;
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- if (rates[i] != 0) {
- frm[1 + j] = rates[i];
- j++;
- }
- }
- *frm++ = j;
- return (frm + j);
-}
-
-static u_int8_t *
-ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
-{
- *frm++ = IEEE80211_ELEMID_SSID;
- *frm++ = len;
- memcpy(frm, ssid, len);
- return (frm + len);
-}
-
-void
-ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m)
-{
- /* Store the new packet on our queue, changing the TIM if necessary */
-
- if (IF_IS_EMPTY(&ni->ni_savedq)) {
- ic->ic_set_tim(ic, ni->ni_associd, 1);
- }
- if (ni->ni_savedq.ifq_len >= IEEE80211_PS_MAX_QUEUE) {
- IF_DROP(&ni->ni_savedq);
- m_freem(m);
- if (ic->ic_if.if_flags & IFF_DEBUG)
- printf("%s: station %s power save queue overflow"
- " of size %d drops %d\n",
- ic->ic_if.if_xname,
- ether_sprintf(ni->ni_macaddr),
- IEEE80211_PS_MAX_QUEUE,
- ni->ni_savedq.ifq_drops);
- } else {
- IF_ENQUEUE(&ni->ni_savedq, m);
- }
-}
-
-static int
-ieee80211_send_prreq(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int dummy)
-{
- int ret;
- struct mbuf *m;
- u_int8_t *frm;
-
- /*
- * prreq frame format
- * [tlv] ssid
- * [tlv] supported rates
- */
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- m->m_data += IEEE80211_HEADER_LEN(ic);
- frm = mtod(m, u_int8_t *);
- frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
- frm = ieee80211_add_rates(frm, ic->ic_sup_rates);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
-
- ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
- ic->ic_mgt_timer = IEEE80211_TRANS_WAIT;
- return (ret);
-}
-
-static int
-ieee80211_send_prresp(struct ieee80211com *ic, struct ieee80211_node *bs0,
- int type, int dummy)
-{
- struct mbuf *m;
- u_int8_t *frm;
- struct ieee80211_node *ni = &ic->ic_bss;
- u_int16_t capinfo;
-
- /*
- * probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] cabability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] parameter set (IBSS)
- */
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- m->m_data += IEEE80211_HEADER_LEN(ic);
- frm = mtod(m, u_int8_t *);
-
- memset(frm, 0, 8); /* timestamp should be filled later */
- frm += 8;
- *(u_int16_t *)frm = htole16(ni->ni_intval);
- frm += 2;
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- capinfo = IEEE80211_CAPINFO_IBSS;
- else
- capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
- capinfo |= IEEE80211_CAPINFO_PRIVACY;
- *(u_int16_t *)frm = htole16(capinfo);
- frm += 2;
-
- frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
- frm = ieee80211_add_rates(frm, ni->ni_rates);
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- *frm++ = IEEE80211_ELEMID_IBSSPARMS;
- *frm++ = 2;
- *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
- } else { /* IEEE80211_M_HOSTAP */
- /* TODO: TIM */
- *frm++ = IEEE80211_ELEMID_TIM;
- *frm++ = 4; /* length */
- *frm++ = 0; /* DTIM count */
- *frm++ = 1; /* DTIM period */
- *frm++ = 0; /* bitmap control */
- *frm++ = 0; /* Partial Virtual Bitmap (variable length) */
- }
- /* TODO: check MHLEN limit */
- m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
-
- return (ieee80211_mgmt_output(&ic->ic_if, bs0, m, type));
-}
-
-static int
-ieee80211_send_auth(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int seq)
-{
- struct mbuf *m;
- u_int16_t *frm;
- int ret;
-
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- MH_ALIGN(m, 2 * 3);
- m->m_pkthdr.len = m->m_len = 6;
- frm = mtod(m, u_int16_t *);
- /* TODO: shared key auth */
- frm[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
- frm[1] = htole16(seq);
- frm[2] = 0; /* status */
- ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
- if (ic->ic_opmode == IEEE80211_M_STA)
- ic->ic_mgt_timer = IEEE80211_TRANS_WAIT;
- return (ret);
-}
-
-static int
-ieee80211_send_deauth(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int reason)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct mbuf *m;
-
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s deauthenticate (reason %d)\n",
- ifp->if_xname, ether_sprintf(ni->ni_macaddr), reason);
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- MH_ALIGN(m, 2);
- m->m_pkthdr.len = m->m_len = 2;
- *mtod(m, u_int16_t *) = htole16(reason);
- return (ieee80211_mgmt_output(&ic->ic_if, ni, m, type));
-}
-
-static int
-ieee80211_send_asreq(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int dummy)
-{
- struct mbuf *m;
- u_int8_t *frm;
- u_int16_t capinfo;
- int ret;
-
- /*
- * asreq frame format
- * [2] capability information
- * [2] listen interval
- * [6*] current AP address (reassoc only)
- * [tlv] ssid
- * [tlv] supported rates
- */
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- m->m_data += IEEE80211_HEADER_LEN(ic);
- frm = mtod(m, u_int8_t *);
-
- capinfo = 0;
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- capinfo |= IEEE80211_CAPINFO_IBSS;
- else /* IEEE80211_M_STA */
- capinfo |= IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
- capinfo |= IEEE80211_CAPINFO_PRIVACY;
- *(u_int16_t *)frm = htole16(capinfo);
- frm += 2;
-
- *(u_int16_t *)frm = htole16(ic->ic_lintval);
- frm += 2;
-
- if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- IEEE80211_ADDR_COPY(frm, ic->ic_bss.ni_bssid);
- frm += IEEE80211_ADDR_LEN;
- }
-
- frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
- frm = ieee80211_add_rates(frm, ic->ic_sup_rates);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
-
- ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
- ic->ic_mgt_timer = IEEE80211_TRANS_WAIT;
- return (ret);
-}
-
-static int
-ieee80211_send_asresp(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int status)
-{
- struct mbuf *m;
- u_int8_t *frm;
- u_int16_t capinfo;
-
- /*
- * asreq frame format
- * [2] capability information
- * [2] status
- * [2] association ID
- * [tlv] supported rates
- */
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- m->m_data += IEEE80211_HEADER_LEN(ic);
- frm = mtod(m, u_int8_t *);
-
- capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
- capinfo |= IEEE80211_CAPINFO_PRIVACY;
- *(u_int16_t *)frm = htole16(capinfo);
- frm += 2;
-
- *(u_int16_t *)frm = htole16(status);
- frm += 2;
-
- if (status == IEEE80211_STATUS_SUCCESS && ni != NULL)
- *(u_int16_t *)frm = htole16(ni->ni_associd);
- else
- *(u_int16_t *)frm = htole16(0);
- frm += 2;
-
- if (ni != NULL)
- frm = ieee80211_add_rates(frm, ni->ni_rates);
- else
- frm = ieee80211_add_rates(frm, ic->ic_bss.ni_rates);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
-
- return (ieee80211_mgmt_output(&ic->ic_if, ni, m, type));
-}
-
-static int
-ieee80211_send_disassoc(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int reason)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct mbuf *m;
-
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s disassociate (reason %d)\n",
- ifp->if_xname, ether_sprintf(ni->ni_macaddr), reason);
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return (ENOMEM);
- MH_ALIGN(m, 2);
- m->m_pkthdr.len = m->m_len = 2;
- *mtod(m, u_int16_t *) = htole16(reason);
- return (ieee80211_mgmt_output(&ic->ic_if, ni, m,
- IEEE80211_FC0_SUBTYPE_DISASSOC));
-}
-
-/* Verify the existence and length of __elem or get out. */
-#define IEEE80211_VERIFY_ELEMENT(__subr_name, __wh, __elem, __maxlen) \
- do { \
- if (__elem == NULL) { \
- DPRINTF((#__subr_name ": no " #__elem "\n")); \
- return; \
- } \
- if (__elem[1] > __maxlen) { \
- DPRINTF((#__subr_name ": bad " #__elem \
- " len %d from %s\n", \
- __elem[1], ether_sprintf(__wh->i_addr2))); \
- return; \
- } \
- } while (0)
-
-static void
-ieee80211_recv_beacon(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
- u_int8_t *frm, *efrm, *tstamp, *bintval, *capinfo, *ssid, *rates;
- u_int8_t chan, fhindex;
- u_int16_t fhdwell;
-
- if (ic->ic_opmode != IEEE80211_M_IBSS &&
- ic->ic_state != IEEE80211_S_SCAN) {
- /* XXX: may be useful for background scan */
- return;
- }
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- /*
- * beacon frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] cabability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] parameter set (FH/DS)
- */
- tstamp = frm; frm += 8;
- bintval = frm; frm += 2;
- capinfo = frm; frm += 2;
- ssid = rates = NULL;
- chan = ic->ic_bss.ni_chan;
- fhdwell = 0;
- fhindex = 0;
- while (frm < efrm) {
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_FHPARMS:
- if (ic->ic_phytype == IEEE80211_T_FH) {
- fhdwell = (frm[3] << 8) | frm[2];
- chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
- fhindex = frm[6];
- }
- break;
- case IEEE80211_ELEMID_DSPARMS:
- if (ic->ic_phytype == IEEE80211_T_DS)
- chan = frm[2];
- break;
- }
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_beacon, wh, rates,
- IEEE80211_RATE_SIZE);
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_beacon, wh, ssid,
- IEEE80211_NWID_LEN);
- ni = ieee80211_find_node(ic, wh->i_addr2);
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug &&
- (ieee80211_debug > 1 || ni == NULL ||
- ic->ic_state == IEEE80211_S_SCAN)) {
- int is_prresp = ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_PROBE_RESP);
- printf("ieee80211_recv_beacon: %s%s on chan %u (bss chan %u) ",
- (ni == NULL ? "new " : ""),
- is_prresp ? "probe response" : "beacon",
- chan, ic->ic_bss.ni_chan);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf(" from %s\n", ether_sprintf(wh->i_addr2));
- }
-#endif
- if (ni == NULL) {
- if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 0)) == NULL)
- return;
- ni->ni_esslen = ssid[1];
- memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
- memcpy(ni->ni_essid, ssid + 2, ssid[1]);
- } else if (ssid[1] != 0) {
- ni->ni_esslen = ssid[1];
- memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
- memcpy(ni->ni_essid, ssid + 2, ssid[1]);
- }
- IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
- memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
- ni->ni_nrate = rates[1];
- memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
- ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT);
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
- ni->ni_intval = letoh16(*(u_int16_t *)bintval);
- ni->ni_capinfo = letoh16(*(u_int16_t *)capinfo);
- ni->ni_chan = chan;
- ni->ni_fhdwell = fhdwell;
- ni->ni_fhindex = fhindex;
- if (ic->ic_state == IEEE80211_S_SCAN &&
- (ic->ic_flags & IEEE80211_F_ASCAN) == 0)
- ieee80211_end_scan(&ic->ic_if);
-}
-
-static void
-ieee80211_recv_prreq(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
- u_int8_t *frm, *efrm, *ssid, *rates;
- u_int8_t rate;
- int allocbs;
-
- if (ic->ic_opmode == IEEE80211_M_STA)
- return;
- if (ic->ic_state != IEEE80211_S_RUN)
- return;
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- /*
- * prreq frame format
- * [tlv] ssid
- * [tlv] supported rates
- */
- ssid = rates = NULL;
- while (frm < efrm) {
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- }
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_prreq, wh, rates,
- IEEE80211_RATE_SIZE);
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_prreq, wh, ssid,
- IEEE80211_NWID_LEN);
- if (ssid[1] != 0 &&
- (ssid[1] != ic->ic_bss.ni_esslen ||
- memcmp(ssid + 2, ic->ic_bss.ni_essid, ic->ic_bss.ni_esslen) != 0)) {
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug) {
- printf("ieee80211_recv_prreq: ssid unmatch ");
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf(" from %s\n", ether_sprintf(wh->i_addr2));
- }
-#endif
- return;
- }
-
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
- if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 1)) == NULL)
- return;
- DPRINTF(("ieee80211_recv_prreq: new req from %s\n",
- ether_sprintf(wh->i_addr2)));
- allocbs = 1;
- } else
- allocbs = 0;
- memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
- ni->ni_nrate = rates[1];
- memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT |
- IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- DPRINTF(("ieee80211_recv_prreq: rate negotiation failed: %s\n",
- ether_sprintf(wh->i_addr2)));
- } else {
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP,
- 0);
- }
- if (allocbs && (ic->ic_opmode == IEEE80211_M_HOSTAP))
- ieee80211_free_node(ic, ni);
-}
-
-static void
-ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
- u_int8_t *frm, *efrm;
- u_int16_t algo, seq, status;
- int allocbs;
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- /*
- * auth frame format
- * [2] algorithm
- * [2] sequence
- * [2] status
- * [tlv*] challenge
- */
- if (frm + 6 > efrm) {
- DPRINTF(("ieee80211_recv_auth: too short from %s\n",
- ether_sprintf(wh->i_addr2)));
- return;
- }
- algo = letoh16(*(u_int16_t *)frm);
- seq = letoh16(*(u_int16_t *)(frm + 2));
- status = letoh16(*(u_int16_t *)(frm + 4));
- if (algo != IEEE80211_AUTH_ALG_OPEN) {
- /* TODO: shared key auth */
- DPRINTF(("ieee80211_recv_auth: unsupported auth %d from %s\n",
- algo, ether_sprintf(wh->i_addr2)));
- return;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_IBSS:
- if (ic->ic_state != IEEE80211_S_RUN || seq != 1)
- return;
- ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
- break;
-
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_MONITOR:
- /* should not come here */
- break;
-
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN || seq != 1)
- return;
- allocbs = 0;
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
- ni = ieee80211_alloc_node(ic, wh->i_addr2, 0);
- if (ni == NULL)
- return;
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss.ni_bssid);
- allocbs = 1;
- }
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2);
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s %s authenticated\n",
- ifp->if_xname,
- (allocbs ? "newly" : "already"),
- ether_sprintf(ni->ni_macaddr));
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH || seq != 2)
- return;
- if (status != 0) {
- printf("%s: authentication failed (reason %d) for %s\n",
- ic->ic_if.if_xname, status,
- ether_sprintf(wh->i_addr3));
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni != NULL)
- ni->ni_fails++;
- return;
- }
- ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
- break;
- }
-}
-
-static void
-ieee80211_recv_asreq(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni = &ic->ic_bss;
- u_int8_t *frm, *efrm, *ssid, *rates;
- u_int16_t capinfo, bintval;
- int reassoc, resp, newassoc;
-
- if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
- (ic->ic_state != IEEE80211_S_RUN))
- return;
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- reassoc = 1;
- resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
- } else {
- reassoc = 0;
- resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- }
- /*
- * asreq frame format
- * [2] capability information
- * [2] listen interval
- * [6*] current AP address (reassoc only)
- * [tlv] ssid
- * [tlv] supported rates
- */
- if (frm + (reassoc ? 10 : 4) > efrm) {
- DPRINTF(("ieee80211_recv_asreq: too short from %s\n",
- ether_sprintf(wh->i_addr2)));
- return;
- }
-
- if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss.ni_bssid)) {
- DPRINTF(("ieee80211_recv_asreq: ignore other bss from %s\n",
- ether_sprintf(wh->i_addr2)));
- return;
- }
- capinfo = letoh16(*(u_int16_t *)frm); frm += 2;
- bintval = letoh16(*(u_int16_t *)frm); frm += 2;
- if (reassoc)
- frm += 6; /* ignore current AP info */
- ssid = rates = NULL;
- while (frm < efrm) {
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- }
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_asreq, wh, rates,
- IEEE80211_RATE_SIZE);
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_asreq, wh, ssid,
- IEEE80211_NWID_LEN);
- if (ssid[1] != ic->ic_bss.ni_esslen ||
- memcmp(ssid + 2, ic->ic_bss.ni_essid, ssid[1]) != 0) {
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug) {
- printf("ieee80211_recv_asreq: ssid unmatch ");
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf(" from %s\n", ether_sprintf(wh->i_addr2));
- }
-#endif
- return;
- }
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni == NULL) {
- DPRINTF(("ieee80211_recv_asreq: not authenticated for %s\n",
- ether_sprintf(wh->i_addr2)));
- if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 1)) == NULL)
- return;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_NOT_AUTHED);
- ieee80211_free_node(ic, ni);
- return;
- }
- if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
- (capinfo & IEEE80211_CAPINFO_PRIVACY) !=
- ((ic->ic_flags & IEEE80211_F_WEPON) ?
- IEEE80211_CAPINFO_PRIVACY : 0)) {
- DPRINTF(("ieee80211_recv_asreq: capability unmatch %x for %s\n",
- capinfo, ether_sprintf(wh->i_addr2)));
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
- ni->ni_associd = 0;
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
- return;
- }
- memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
- ni->ni_nrate = rates[1];
- memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
- ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (ni->ni_nrate == 0) {
- DPRINTF(("ieee80211_recv_asreq: rate unmatch for %s\n",
- ether_sprintf(wh->i_addr2)));
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
- ni->ni_associd = 0;
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
- return;
- }
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- ni->ni_intval = bintval;
- ni->ni_capinfo = capinfo;
- ni->ni_chan = ic->ic_bss.ni_chan;
- ni->ni_fhdwell = ic->ic_bss.ni_fhdwell;
- ni->ni_fhindex = ic->ic_bss.ni_fhindex;
- if (ni->ni_associd == 0) {
- u_int16_t aid;
-
- /*
- * It would be clever to search the bitmap more efficiently,
- * but this will do for now.
- */
- for (aid = 1; aid < ic->ic_max_aid; aid++) {
- if (!IEEE80211_AID_ISSET(aid, ic->ic_aid_bitmap))
- break;
- }
-
- if (ic->ic_bss.ni_associd >= ic->ic_max_aid) {
- IEEE80211_SEND_MGMT(ic, ni, resp,
- IEEE80211_REASON_ASSOC_TOOMANY);
- return;
- } else {
- ni->ni_associd = aid | 0xc000;
- IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
- newassoc = 1;
- }
- } else
- newassoc = 0;
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s %s associated at aid %d\n",
- ifp->if_xname, (newassoc ? "newly" : "already"),
- ether_sprintf(ni->ni_macaddr),
- (ni->ni_associd & ~0xc000));
-}
-
-static void
-ieee80211_recv_asresp(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni = &ic->ic_bss;
- u_int8_t *frm, *efrm, *rates;
- int status;
-
- if (ic->ic_opmode != IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_ASSOC)
- return;
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- /*
- * asresp frame format
- * [2] capability information
- * [2] status
- * [2] association ID
- * [tlv] supported rates
- */
- if (frm + 6 > efrm) {
- DPRINTF(("ieee80211_recv_asresp: too short from %s\n",
- ether_sprintf(wh->i_addr2)));
- return;
- }
-
- ni->ni_capinfo = letoh16(*(u_int16_t *)frm);
- frm += 2;
-
- status = letoh16(*(u_int16_t *)frm);
- frm += 2;
- if (status != 0) {
- printf("%s: association failed (reason %d) for %s\n",
- ifp->if_xname, status, ether_sprintf(wh->i_addr3));
- ni = ieee80211_find_node(ic, wh->i_addr2);
- if (ni != NULL)
- ni->ni_fails++;
- return;
- }
- ni->ni_associd = letoh16(*(u_int16_t *)frm);
- frm += 2;
- rates = frm;
-
- IEEE80211_VERIFY_ELEMENT(ieee80211_recv_asresp, wh, rates,
- IEEE80211_RATE_SIZE);
- memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
- ni->ni_nrate = rates[1];
- memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
- ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (ni->ni_nrate == 0)
- return;
- ieee80211_new_state(ifp, IEEE80211_S_RUN,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
-}
-
-static void
-ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
- u_int8_t *frm, *efrm;
- u_int16_t reason;
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- /*
- * disassoc frame format
- * [2] reason
- */
- if (frm + 2 > efrm) {
- DPRINTF(("ieee80211_recv_disassoc: too short from %s\n",
- ether_sprintf(wh->i_addr2)));
- return;
- }
- reason = letoh16(*(u_int16_t *)frm);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
- break;
- case IEEE80211_M_HOSTAP:
- if ((ni = ieee80211_find_node(ic, wh->i_addr2)) != NULL) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s disassociated"
- " by peer (reason %d)\n", ifp->if_xname,
- ether_sprintf(ni->ni_macaddr), reason);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
- ni->ni_associd = 0;
- }
- break;
- default:
- break;
- }
-}
-
-static void
-ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
- u_int8_t *frm, *efrm;
- u_int16_t reason;
-
- wh = mtod(m0, struct ieee80211_frame *);
- frm = (u_int8_t *)&wh[1];
- efrm = mtod(m0, u_int8_t *) + m0->m_len;
- /*
- * dauth frame format
- * [2] reason
- */
- if (frm + 2 > efrm) {
- DPRINTF(("ieee80211_recv_deauth: too short from %s\n",
- ether_sprintf(wh->i_addr2)));
- return;
- }
- reason = letoh16(*(u_int16_t *)frm);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
- break;
- case IEEE80211_M_HOSTAP:
- if ((ni = ieee80211_find_node(ic, wh->i_addr2)) != NULL) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s deauthenticated"
- " by peer (reason %d)\n", ifp->if_xname,
- ether_sprintf(ni->ni_macaddr), reason);
- ieee80211_free_node(ic, ni);
- }
- break;
- default:
- break;
- }
-}
-
-
-static void
-ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi,
- u_int32_t rstamp)
-{
- struct ifnet *ifp = &ic->ic_if;
- struct ieee80211_frame *wh;
- struct ieee80211_node *ni;
- struct mbuf *m;
- u_int16_t aid;
-
- if (ic->ic_set_tim == NULL) /* No powersaving functionality */
- return;
-
- wh = mtod(m0, struct ieee80211_frame *);
-
- if ((ni = ieee80211_find_node(ic, wh->i_addr2)) == NULL) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s sent bogus power save poll\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2));
- return;
- }
-
- memcpy(&aid, wh->i_dur, sizeof(wh->i_dur));
- if ((aid & 0xc000) != 0xc000) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s sent bogus aid %x\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2), aid);
- return;
- }
-
- if (aid != ni->ni_associd) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s aid %x doesn't match pspoll "
- "aid %x\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2),
- ni->ni_associd, aid);
- return;
- }
-
- /* Okay, take the first queued packet and put it out... */
-
- IF_DEQUEUE(&ni->ni_savedq, m);
- if (m == NULL) {
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: station %s sent pspoll, "
- "but no packets are saved\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2));
- return;
- }
- wh = mtod(m, struct ieee80211_frame *);
-
- /*
- * If this is the last packet, turn off the TIM fields.
- * If there are more packets, set the more packets bit.
- */
-
- if (IF_IS_EMPTY(&ni->ni_savedq)) {
- if (ic->ic_set_tim)
- ic->ic_set_tim(ic, ni->ni_associd, 0);
- } else {
- wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
- }
-
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: enqueued power saving packet for station %s\n",
- ifp->if_xname, ether_sprintf(ni->ni_macaddr));
-
- IF_ENQUEUE(&ic->ic_pwrsaveq, m);
- (*ifp->if_start)(ifp);
-}
-
-
-int
-ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_node *ni = &ic->ic_bss;
- u_int8_t old_bssid[IEEE80211_ADDR_LEN];
- int i, error, ostate;
-#ifdef IEEE80211_DEBUG
- static const char *stname[] =
- { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" };
-#endif
-
- ostate = ic->ic_state;
- DPRINTF(("ieee80211_new_state: %s -> %s\n",
- stname[ostate], stname[nstate]));
- if (ic->ic_newstate) {
- error = (*ic->ic_newstate)(ic->ic_softc, nstate);
- if (error == EINPROGRESS)
- return (0);
- if (error != 0)
- return (error);
- }
-
- /* state transition */
- ic->ic_state = nstate;
- switch (nstate) {
- case IEEE80211_S_INIT:
- switch (ostate) {
- case IEEE80211_S_INIT:
- break;
- case IEEE80211_S_RUN:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- break;
- case IEEE80211_M_HOSTAP:
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if (ni->ni_associd == 0)
- continue;
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- }
- break;
- default:
- break;
- }
- /* FALLTHRU */
- case IEEE80211_S_ASSOC:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_LEAVE);
- break;
- case IEEE80211_M_HOSTAP:
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_LEAVE);
- }
- break;
- default:
- break;
- }
- /* FALLTHRU */
- case IEEE80211_S_AUTH:
- case IEEE80211_S_SCAN:
- ic->ic_mgt_timer = 0;
- IF_PURGE(&ic->ic_mgtq);
- IF_PURGE(&ic->ic_pwrsaveq);
- if (ic->ic_wep_ctx != NULL) {
- free(ic->ic_wep_ctx, M_DEVBUF);
- ic->ic_wep_ctx = NULL;
- }
- ieee80211_free_allnodes(ic);
- break;
- }
- break;
- case IEEE80211_S_SCAN:
- ic->ic_flags &= ~IEEE80211_F_SIBSS;
- ni = &ic->ic_bss;
- /* initialize bss for probe request */
- IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr);
- IEEE80211_ADDR_COPY(old_bssid, ic->ic_bss.ni_bssid);
- IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr);
- ni->ni_nrate = 0;
- memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- if (ic->ic_sup_rates[i] != 0)
- ni->ni_rates[ni->ni_nrate++] =
- ic->ic_sup_rates[i];
- }
- ni->ni_associd = 0;
- ni->ni_rstamp = 0;
- switch (ostate) {
- case IEEE80211_S_INIT:
- /* use lowest rate */
- ni->ni_txrate = 0;
- ieee80211_begin_scan(ifp, ni);
- break;
- case IEEE80211_S_SCAN:
- /* scan next */
- if (ic->ic_flags & IEEE80211_F_ASCAN) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
- }
- break;
- case IEEE80211_S_RUN:
- /* beacon miss */
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: no recent beacons from %s;"
- " rescanning\n",
- ifp->if_xname, ether_sprintf(old_bssid));
- ieee80211_free_allnodes(ic);
- /* FALLTHRU */
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- /* timeout restart scan */
- ni = ieee80211_find_node(ic, ic->ic_bss.ni_macaddr);
- if (ni != NULL)
- ni->ni_fails++;
- ieee80211_begin_scan(ifp, &ic->ic_bss);
- break;
- }
- break;
- case IEEE80211_S_AUTH:
- switch (ostate) {
- case IEEE80211_S_INIT:
- DPRINTF(("ieee80211_new_state: invalid transition\n"));
- break;
- case IEEE80211_S_SCAN:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- switch (mgt) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- /* ??? */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- /* ignore and retry scan on timeout */
- break;
- }
- break;
- case IEEE80211_S_RUN:
- switch (mgt) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- ic->ic_state = ostate; /* stay RUN */
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- /* try to reauth */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- }
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- case IEEE80211_S_ASSOC:
- DPRINTF(("ieee80211_new_state: invalid transition\n"));
- break;
- case IEEE80211_S_AUTH:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- break;
- case IEEE80211_S_RUN:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
- break;
- }
- break;
- case IEEE80211_S_RUN:
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_RUN:
- DPRINTF(("ieee80211_new_state: invalid transition\n"));
- break;
- case IEEE80211_S_SCAN: /* adhoc mode */
- case IEEE80211_S_ASSOC: /* infra mode */
- if (ifp->if_flags & IFF_DEBUG) {
- printf("%s: ", ifp->if_xname);
- if (ic->ic_opmode == IEEE80211_M_STA)
- printf("associated ");
- else
- printf("synchronized ");
- printf("with %s ssid ",
- ether_sprintf(ic->ic_bss.ni_bssid));
- ieee80211_print_essid(ic->ic_bss.ni_essid,
- ic->ic_bss.ni_esslen);
- printf(" channel %d\n", ic->ic_bss.ni_chan);
- }
- /* start with highest negotiated rate */
- ic->ic_bss.ni_txrate = ic->ic_bss.ni_nrate - 1;
- ic->ic_mgt_timer = 0;
- (*ifp->if_start)(ifp);
- break;
- }
- break;
- }
- return (0);
-}
-
-struct mbuf *
-ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct mbuf *m, *n, *n0;
- struct ieee80211_frame *wh;
- int i, left, len, moff, noff, kid;
- u_int32_t iv, crc;
- u_int8_t *ivp;
- void *ctx;
- u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
- u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
-
- n0 = NULL;
- if ((ctx = ic->ic_wep_ctx) == NULL) {
- ctx = malloc(RC4STATE, M_DEVBUF, M_NOWAIT);
- if (ctx == NULL)
- goto fail;
- ic->ic_wep_ctx = ctx;
- }
- m = m0;
- left = m->m_pkthdr.len;
- MGET(n, M_DONTWAIT, m->m_type);
- n0 = n;
- if (n == NULL)
- goto fail;
- M_MOVE_PKTHDR(n, m);
- len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
- if (txflag) {
- n->m_pkthdr.len += len;
- } else {
- n->m_pkthdr.len -= len;
- left -= len;
- }
- n->m_len = MHLEN;
- if (n->m_pkthdr.len >= MINCLSIZE) {
- MCLGET(n, M_DONTWAIT);
- if (n->m_flags & M_EXT)
- n->m_len = n->m_ext.ext_size;
- }
- len = sizeof(struct ieee80211_frame);
- memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
- wh = mtod(n, struct ieee80211_frame *);
- left -= len;
- moff = len;
- noff = len;
- if (txflag) {
- kid = ic->ic_wep_txkey;
- wh->i_fc[1] |= IEEE80211_FC1_WEP;
- if (!ic->ic_iv_flag) {
- ic->ic_iv = arc4random();
- ic->ic_iv_flag++;
- } else
- iv = ic->ic_iv + 1;
- /*
- * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
- * (B, 255, N) with 3 <= B < 8
- */
- if (iv >= 0x03ff00 &&
- (iv & 0xf8ff00) == 0x00ff00)
- iv += 0x000100;
- ic->ic_iv = iv + 1;
- /* put iv in little endian to prepare 802.11i */
- ivp = mtod(n, u_int8_t *) + noff;
- for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
- ivp[i] = iv & 0xff;
- iv >>= 8;
- }
- ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */
- noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
- } else {
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- ivp = mtod(m, u_int8_t *) + moff;
- kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
- moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
- }
- memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
- memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
- ic->ic_nw_keys[kid].wk_len);
- rc4_keysetup(ctx, keybuf,
- IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
-
- /* encrypt with calculating CRC */
- crc = ~0;
- while (left > 0) {
- len = m->m_len - moff;
- if (len == 0) {
- m = m->m_next;
- moff = 0;
- continue;
- }
- if (len > n->m_len - noff) {
- len = n->m_len - noff;
- if (len == 0) {
- MGET(n->m_next, M_DONTWAIT, n->m_type);
- if (n->m_next == NULL)
- goto fail;
- n = n->m_next;
- n->m_len = MLEN;
- if (left >= MINCLSIZE) {
- MCLGET(n, M_DONTWAIT);
- if (n->m_flags & M_EXT)
- n->m_len = n->m_ext.ext_size;
- }
- noff = 0;
- continue;
- }
- }
- if (len > left)
- len = left;
- rc4_crypt(ctx, mtod(n, caddr_t) + noff,
- mtod(m, caddr_t) + moff, len);
- if (txflag)
- crc = ieee80211_crc_update(crc,
- mtod(m, u_int8_t *) + moff, len);
- else
- crc = ieee80211_crc_update(crc,
- mtod(n, u_int8_t *) + noff, len);
- left -= len;
- moff += len;
- noff += len;
- }
- crc = ~crc;
- if (txflag) {
- *(u_int32_t *)crcbuf = htole32(crc);
- if (n->m_len >= noff + sizeof(crcbuf))
- n->m_len = noff + sizeof(crcbuf);
- else {
- n->m_len = noff;
- MGET(n->m_next, M_DONTWAIT, n->m_type);
- if (n->m_next == NULL)
- goto fail;
- n = n->m_next;
- n->m_len = sizeof(crcbuf);
- noff = 0;
- }
- rc4_crypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
- sizeof(crcbuf));
- } else {
- n->m_len = noff;
- for (noff = 0; noff < sizeof(crcbuf); noff += len) {
- len = sizeof(crcbuf) - noff;
- if (len > m->m_len - moff)
- len = m->m_len - moff;
- if (len > 0)
- rc4_crypt(ctx, crcbuf + noff,
- mtod(m, caddr_t) + moff, len);
- m = m->m_next;
- moff = 0;
- }
- if (crc != letoh32(*(u_int32_t *)crcbuf)) {
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug) {
- printf("%s: decrypt CRC error\n",
- ifp->if_xname);
- if (ieee80211_debug > 1)
- ieee80211_dump_pkt(n0->m_data,
- n0->m_len, -1, -1);
- }
-#endif
- goto fail;
- }
- }
- m_freem(m0);
- return (n0);
-
- fail:
- m_freem(m0);
- m_freem(n0);
- return (NULL);
-}
-
-/*
- * CRC 32 -- routine from RFC 2083
- */
-
-/* Table of CRCs of all 8-bit messages */
-static u_int32_t ieee80211_crc_table[256];
-
-/* Make the table for a fast CRC. */
-static void
-ieee80211_crc_init(void)
-{
- u_int32_t c;
- int n, k;
-
- for (n = 0; n < 256; n++) {
- c = (u_int32_t)n;
- for (k = 0; k < 8; k++) {
- if (c & 1)
- c = 0xedb88320UL ^ (c >> 1);
- else
- c = c >> 1;
- }
- ieee80211_crc_table[n] = c;
- }
-}
-
-/*
- * Update a running CRC with the bytes buf[0..len-1]--the CRC
- * should be initialized to all 1's, and the transmitted value
- * is the 1's complement of the final running CRC
- */
-
-static u_int32_t
-ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len)
-{
- u_int8_t *endbuf;
-
- for (endbuf = buf + len; buf < endbuf; buf++)
- crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
- return (crc);
-}
-
-int
-ieee80211_media_change(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
- struct ifmedia_entry *ime;
- enum ieee80211_opmode newmode;
- int i, rate, error = 0;
-
- ime = ic->ic_media.ifm_cur;
- if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) {
- i = -1;
- } else {
- rate = ieee80211_media2rate(ime->ifm_media, ic->ic_phytype);
- if (rate == 0)
- return (EINVAL);
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- if ((ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) == rate)
- break;
- }
- if (i == IEEE80211_RATE_SIZE)
- return (EINVAL);
- }
- if (ic->ic_fixed_rate != i) {
- ic->ic_fixed_rate = i;
- error = ENETRESET;
- }
-
- if ((ime->ifm_media & IFM_IEEE80211_ADHOC) &&
- (ime->ifm_media & IFM_FLAG0))
- newmode = IEEE80211_M_AHDEMO;
- else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
- newmode = IEEE80211_M_IBSS;
- else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
- newmode = IEEE80211_M_HOSTAP;
- else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
- newmode = IEEE80211_M_MONITOR;
- else
- newmode = IEEE80211_M_STA;
- if (ic->ic_opmode != newmode) {
- ic->ic_opmode = newmode;
- error = ENETRESET;
- }
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) != 0)
- error = (*ifp->if_init)(ifp);
- else
- error = 0;
- }
- if (error == 0)
- ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
-
- return (error);
-}
-
-void
-ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
-{
- struct ieee80211com *ic = (void *)ifp;
- int rate;
-
- imr->ifm_status = IFM_AVALID;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- rate = ic->ic_bss.ni_rates[ic->ic_bss.ni_txrate] &
- IEEE80211_RATE_VAL;
- else {
- if (ic->ic_fixed_rate == -1)
- rate = 0;
- else
- rate = ic->ic_sup_rates[ic->ic_fixed_rate] &
- IEEE80211_RATE_VAL;
- }
- imr->ifm_active |= ieee80211_rate2media(rate, ic->ic_phytype);
- switch (ic->ic_opmode) {
- case IEEE80211_M_AHDEMO:
- imr->ifm_active |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
- break;
- case IEEE80211_M_HOSTAP:
- imr->ifm_active |= IFM_IEEE80211_HOSTAP;
- break;
- case IEEE80211_M_IBSS:
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- break;
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
- case IEEE80211_M_STA:
- break;
- }
-}
-
-/*
- * convert IEEE80211 rate value to ifmedia subtype.
- * ieee80211 rate is in unit of 0.5Mbps.
- */
-
-int
-ieee80211_rate2media(int rate, enum ieee80211_phytype phytype)
-{
- int mword;
-
- mword = 0;
- switch (phytype) {
- case IEEE80211_T_FH:
- switch (rate & IEEE80211_RATE_VAL) {
- case 0:
- mword = IFM_AUTO;
- break;
- case 2:
- mword = IFM_IEEE80211_FH1;
- break;
- case 4:
- mword = IFM_IEEE80211_FH2;
- break;
- default:
- mword = IFM_NONE;
- break;
- }
- break;
-
- case IEEE80211_T_DS:
- switch (rate & IEEE80211_RATE_VAL) {
- case 0:
- mword = IFM_AUTO;
- break;
- case 2:
- mword = IFM_IEEE80211_DS1;
- break;
- case 4:
- mword = IFM_IEEE80211_DS2;
- break;
- case 11:
- mword = IFM_IEEE80211_DS5;
- break;
- case 22:
- mword = IFM_IEEE80211_DS11;
- break;
- default:
- mword = IFM_NONE;
- break;
- }
- break;
-
- default:
- mword = IFM_MANUAL;
- break;
- }
- return (mword);
-}
-
-int
-ieee80211_media2rate(int mword, enum ieee80211_phytype phytype)
-{
- int rate;
-
- rate = 0;
- switch (phytype) {
- case IEEE80211_T_FH:
- switch (IFM_SUBTYPE(mword)) {
- case IFM_IEEE80211_FH1:
- rate = 2;
- break;
- case IFM_IEEE80211_FH2:
- rate = 4;
- break;
- }
- break;
-
- case IEEE80211_T_DS:
- switch (IFM_SUBTYPE(mword)) {
- case IFM_IEEE80211_DS1:
- rate = 2;
- break;
- case IFM_IEEE80211_DS2:
- rate = 4;
- break;
- case IFM_IEEE80211_DS5:
- rate = 11;
- break;
- case IFM_IEEE80211_DS11:
- rate = 22;
- break;
- }
- break;
-
- default:
- break;
- }
- return (rate);
-}
-
-/*
- * XXX
- * Wireless LAN specific configuration interface, which is compatible
- * with wiconfig(8).
- */
-
-int
-ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct ieee80211com *ic = (void *)ifp;
- int i, j, error;
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
- struct wi_ltv_keys *keys;
- struct wi_apinfo *ap;
- struct ieee80211_node *ni;
-#ifdef WICACHE
- struct wi_sigcache wsc;
-#endif /* WICACHE */
-
- 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:
- strlcpy((char *)&wreq.wi_val[1], hostname,
- sizeof(wreq.wi_val) - 1);
- 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(ic->ic_ibss_chan);
- wreq.wi_len = 1;
- break;
- case WI_RID_CURRENT_CHAN:
- wreq.wi_val[0] = htole16(ic->ic_bss.ni_chan);
- wreq.wi_len = 1;
- break;
- case WI_RID_COMMS_QUALITY:
- wreq.wi_val[0] = 0; /* quality */
- wreq.wi_val[1] = htole16(ic->ic_bss.ni_rssi); /* signal */
- 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 == -1)
- wreq.wi_val[0] = 0; /* auto */
- else
- wreq.wi_val[0] = htole16(
- (ic->ic_sup_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[ic->ic_bss.ni_txrate] &
- IEEE80211_RATE_VAL) / 2);
- wreq.wi_len = 1;
- break;
- case WI_RID_RTS_THRESH:
- wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: RTS */
- 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(1); /* enabled ... not supported */
- 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((ic->ic_flags & IEEE80211_F_HASWEP) ? 1 : 0);
- 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_WEPON) ? 1 : 0);
- wreq.wi_len = 1;
- break;
- case WI_RID_TX_CRYPT_KEY:
- wreq.wi_val[0] = htole16(ic->ic_wep_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(curproc, 0);
- 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_len);
- memcpy(keys->wi_keys[i].wi_keydat,
- ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
- }
- wreq.wi_len = sizeof(*keys) / 2;
- break;
- case WI_RID_MAX_DATALEN:
- wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */
- wreq.wi_len = 1;
- break;
- case WI_RID_IFACE_STATS:
- /* XXX: should be implemented in lower drivers */
- break;
- case WI_RID_READ_APS:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
- for (i = 0; i < IEEE80211_PSCAN_WAIT; i++) {
- tsleep((caddr_t)ic, PWAIT | PCATCH, "i80211",
- hz);
- if (ic->ic_state != IEEE80211_S_SCAN ||
- (ic->ic_flags & IEEE80211_F_SCANAP) == 0 ||
- (ic->ic_flags & IEEE80211_F_ASCAN) == 0)
- break;
- }
- ic->ic_flags &= ~IEEE80211_F_SCANAP;
- memcpy(ic->ic_chan_active, ic->ic_chan_avail,
- sizeof(ic->ic_chan_active));
- }
- i = 0;
- ap = (void *)((char *)wreq.wi_val + sizeof(i));
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
- break;
- memset(ap, 0, sizeof(*ap));
- 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 = ni->ni_chan;
- ap->signal = ni->ni_rssi;
- ap->capinfo = ni->ni_capinfo;
- ap->interval = ni->ni_intval;
- for (j = 0; j < ni->ni_nrate; j++) {
- if (ni->ni_rates[j] & IEEE80211_RATE_BASIC) {
- ap->rate = (ni->ni_rates[j] &
- IEEE80211_RATE_VAL) * 5; /* XXX */
- }
- }
- i++;
- ap++;
- }
- memcpy(wreq.wi_val, &i, sizeof(i));
- wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
- break;
-#ifdef WICACHE
- case WI_RID_READ_CACHE:
- i = 0;
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if (i == MAXWICACHE)
- break;
- IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
- memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
- wsc.signal = ni->ni_rssi;
- wsc.noise = 0;
- wsc.quality = 0;
- memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
- &wsc, sizeof(wsc));
- i++;
- }
- wreq.wi_len = sizeof(wsc) * i / 2;
- break;
-#endif /* WICACHE */
- case WI_RID_SCAN_APS:
- error = EINVAL;
- break;
- default:
- error = EINVAL;
- break;
- }
- if (error == 0) {
- wreq.wi_len++;
- error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
- }
- return (error);
-}
-
-int
-ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct ieee80211com *ic = (void *)ifp;
- int i, j, len, error;
- 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);
- if (wreq.wi_len-- < 1)
- return (EINVAL);
- 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:
- len = letoh16(wreq.wi_val[0]);
- if (wreq.wi_len < (1 + len + 1) / 2)
- return (EINVAL);
- if (len > IEEE80211_NWID_LEN)
- return (EINVAL);
- ic->ic_des_esslen = len;
- memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
- memcpy(ic->ic_des_essid, &wreq.wi_val[1], len);
- error = ENETRESET;
- break;
- case WI_RID_CURRENT_BSSID:
- return (EPERM);
- case WI_RID_OWN_CHNL:
- if (wreq.wi_len != 1)
- return (EINVAL);
- i = letoh16(wreq.wi_val[0]);
- if (i < 0 ||
- i > IEEE80211_CHAN_MAX ||
- isclr(ic->ic_chan_active, i))
- return (EINVAL);
- ic->ic_ibss_chan = i;
- if (ic->ic_flags & IEEE80211_F_SIBSS)
- error = ENETRESET;
- break;
- case WI_RID_CURRENT_CHAN:
- return (EPERM);
- case WI_RID_COMMS_QUALITY:
- return (EPERM);
- case WI_RID_PROMISC:
- if (wreq.wi_len != 1)
- 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 (wreq.wi_len != 1)
- return (EINVAL);
- switch (letoh16(wreq.wi_val[0])) {
- case IEEE80211_M_STA:
- break;
- case IEEE80211_M_IBSS:
- if (!(ic->ic_flags & IEEE80211_F_HASIBSS))
- return (EINVAL);
- break;
- case IEEE80211_M_AHDEMO:
- if (ic->ic_phytype != IEEE80211_T_DS ||
- !(ic->ic_flags & IEEE80211_F_HASAHDEMO))
- return (EINVAL);
- break;
- case IEEE80211_M_HOSTAP:
- if (!(ic->ic_flags & IEEE80211_F_HASHOSTAP))
- return (EINVAL);
- break;
- default:
- return (EINVAL);
- }
- if (letoh16(wreq.wi_val[0]) != ic->ic_opmode) {
- ic->ic_opmode = letoh16(wreq.wi_val[0]);
- error = ENETRESET;
- }
- break;
- case WI_RID_MAC_NODE:
- if (wreq.wi_len != IEEE80211_ADDR_LEN / 2)
- return (EINVAL);
- IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val);
- /* if_init will copy lladdr into ic_myaddr */
- error = ENETRESET;
- break;
- case WI_RID_TX_RATE:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (wreq.wi_val[0] == 0) {
- /* auto */
- ic->ic_fixed_rate = -1;
- break;
- }
- for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
- if (letoh16(wreq.wi_val[0]) ==
- (ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) / 2)
- break;
- }
- if (i == IEEE80211_RATE_SIZE)
- return (EINVAL);
- ic->ic_fixed_rate = i;
- error = ENETRESET;
- break;
- case WI_RID_CUR_TX_RATE:
- return (EPERM);
- case WI_RID_RTS_THRESH:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (letoh16(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
- return (EINVAL); /* TODO: RTS */
- break;
- case WI_RID_CREATE_IBSS:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (wreq.wi_val[0] != 0) {
- if ((ic->ic_flags & IEEE80211_F_HASIBSS) == 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 = ENETRESET;
- }
- } 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 = ENETRESET;
- }
- }
- }
- break;
- case WI_RID_MICROWAVE_OVEN:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (wreq.wi_val[0] != 0)
- return (EINVAL); /* not supported */
- break;
- case WI_RID_ROAMING_MODE:
- if (wreq.wi_len != 1)
- return EINVAL;
- if (letoh16(wreq.wi_val[0]) != 1)
- return (EINVAL); /* not supported */
- break;
- case WI_RID_SYSTEM_SCALE:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (letoh16(wreq.wi_val[0]) != 1)
- return (EINVAL); /* not supported */
- break;
- case WI_RID_PM_ENABLED:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (wreq.wi_val[0] != 0) {
- if ((ic->ic_flags & IEEE80211_F_HASPMGT) == 0)
- return (EINVAL);
- if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
- }
- } else {
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
- ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
- }
- }
- break;
- case WI_RID_MAX_SLEEP:
- if (wreq.wi_len != 1)
- return (EINVAL);
- ic->ic_lintval = letoh16(wreq.wi_val[0]);
- if (ic->ic_flags & IEEE80211_F_PMGTON)
- error = ENETRESET;
- break;
- case WI_RID_CUR_BEACON_INT:
- return (EPERM);
- case WI_RID_WEP_AVAIL:
- return (EPERM);
- case WI_RID_CNFAUTHMODE:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (letoh16(wreq.wi_val[0]) != 1)
- return (EINVAL); /* TODO: shared key auth */
- break;
- case WI_RID_ENCRYPTION:
- if (wreq.wi_len != 1)
- return (EINVAL);
- if (wreq.wi_val[0] != 0) {
- if ((ic->ic_flags & IEEE80211_F_HASWEP) == 0)
- return (EINVAL);
- if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
- ic->ic_flags |= IEEE80211_F_WEPON;
- error = ENETRESET;
- }
- } else {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- ic->ic_flags &= ~IEEE80211_F_WEPON;
- error = ENETRESET;
- }
- }
- break;
- case WI_RID_TX_CRYPT_KEY:
- if (wreq.wi_len != 1)
- return (EINVAL);
- i = letoh16(wreq.wi_val[0]);
- if (i >= IEEE80211_WEP_NKID)
- return (EINVAL);
- ic->ic_wep_txkey = i;
- break;
- case WI_RID_DEFLT_CRYPT_KEYS:
- if (wreq.wi_len != sizeof(struct wi_ltv_keys) / 2)
- return (EINVAL);
- keys = (struct wi_ltv_keys *)&wreq;
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- len = letoh16(keys->wi_keys[i].wi_keylen);
- if (len != 0 && len < IEEE80211_WEP_KEYLEN)
- return (EINVAL);
- if (len > sizeof(ic->ic_nw_keys[i].wk_key))
- return (EINVAL);
- }
- memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- len = letoh16(keys->wi_keys[i].wi_keylen);
- ic->ic_nw_keys[i].wk_len = len;
- memcpy(ic->ic_nw_keys[i].wk_key,
- keys->wi_keys[i].wi_keydat, len);
- }
- error = ENETRESET;
- break;
- case WI_RID_MAX_DATALEN:
- if (wreq.wi_len != 1)
- return (EINVAL);
- len = letoh16(wreq.wi_val[0]);
- if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
- return (EINVAL);
- if (len != IEEE80211_MAX_LEN)
- return (EINVAL); /* TODO: fragment */
- break;
- case WI_RID_IFACE_STATS:
- error = EPERM;
- break;
- case WI_RID_SCAN_APS:
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- break;
- wreq.wi_len -= 2; /* 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 / 16 >= wreq.wi_len)
- break;
- if (isclr((u_int8_t *)wreq.wi_val, j))
- continue;
- if (isclr(ic->ic_chan_avail, i)) {
- if (wreq.wi_type == WI_RID_CHANNEL_LIST)
- error = EPERM;
- else
- continue;
- }
- setbit(chanlist, i);
- }
- if (error == EPERM && ic->ic_chancheck != NULL)
- error = (*ic->ic_chancheck)(ic->ic_softc, chanlist);
- if (error)
- return (error);
- memcpy(ic->ic_chan_active, chanlist,
- sizeof(ic->ic_chan_active));
- if (isclr(chanlist, ic->ic_ibss_chan)) {
- for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
- if (isset(chanlist, i)) {
- ic->ic_ibss_chan = i;
- break;
- }
- }
- if (isclr(chanlist, ic->ic_bss.ni_chan))
- ic->ic_bss.ni_chan = ic->ic_ibss_chan;
- if (wreq.wi_type == WI_RID_CHANNEL_LIST)
- error = ENETRESET;
- else {
- ic->ic_flags |= IEEE80211_F_SCANAP;
- error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
- }
- break;
- default:
- error = EINVAL;
- break;
- }
- return (error);
-}
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
new file mode 100644
index 00000000000..df3887bcbb5
--- /dev/null
+++ b/sys/net80211/ieee80211.c
@@ -0,0 +1,1051 @@
+/* $OpenBSD: ieee80211.c,v 1.1 2004/06/22 22:53:51 millert Exp $ */
+/* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.11 2004/04/02 20:19:20 sam Exp $");
+#elif defined (__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $");
+#endif
+
+/*
+ * IEEE 802.11 generic handler
+ */
+
+#ifdef __NetBSD__
+#include "opt_inet.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#ifdef __FreeBSD__
+#include <machine/atomic.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#if defined( __FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+#include <net/if_llc.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+
+#ifdef IEEE80211_DEBUG
+int ieee80211_debug = 0;
+#ifdef __NetBSD__
+static int ieee80211_debug_nodenum;
+#endif /* __NetBSD__ */
+
+#ifdef __FreeBSD__
+SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
+ 0, "IEEE 802.11 media debugging printfs");
+#endif
+#endif
+
+int ieee80211_inact_max = IEEE80211_INACT_MAX;
+#ifdef __NetBSD__
+static int ieee80211_inact_max_nodenum;
+#endif
+
+static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+ enum ieee80211_phymode);
+
+#if 0
+static const char *ieee80211_phymode_name[] = {
+ "auto", /* IEEE80211_MODE_AUTO */
+ "11a", /* IEEE80211_MODE_11A */
+ "11b", /* IEEE80211_MODE_11B */
+ "11g", /* IEEE80211_MODE_11G */
+ "FH", /* IEEE80211_MODE_FH */
+ "turbo", /* IEEE80211_MODE_TURBO */
+};
+#endif
+
+void
+ieee80211_ifattach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_channel *c;
+ int i;
+
+#if defined(__OpenBSD__)
+ memcpy(((struct arpcom *)ifp)->ac_enaddr, ic->ic_myaddr,
+ ETHER_ADDR_LEN);
+ ether_ifattach(ifp);
+#else
+ ether_ifattach(ifp, ic->ic_myaddr);
+#endif
+#if NBPFILTER > 0
+ BPF_ATTACH(ifp, DLT_IEEE802_11,
+ sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
+#endif
+ ieee80211_crypto_attach(ifp);
+
+ /*
+ * 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;
+ }
+ }
+ /* 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 */
+
+ (void) ieee80211_setmode(ic, ic->ic_curmode);
+
+ if (ic->ic_lintval == 0)
+ ic->ic_lintval = 100; /* default sleep */
+ ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */
+
+ ieee80211_node_attach(ifp);
+ ieee80211_proto_attach(ifp);
+}
+
+void
+ieee80211_ifdetach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ ieee80211_proto_detach(ifp);
+ ieee80211_crypto_detach(ifp);
+ ieee80211_node_detach(ifp);
+#ifdef __FreeBSD__
+ ifmedia_removeall(&ic->ic_media);
+#else
+ ifmedia_delete_instance(&ic->ic_media, IFM_INST_ANY);
+#endif
+#if NBPFILTER > 0
+ BPF_DETACH(ifp);
+#endif
+ ether_ifdetach(ifp);
+}
+
+/*
+ * Convert MHz frequency to IEEE channel number.
+ */
+u_int
+ieee80211_mhz2ieee(u_int freq, u_int flags)
+{
+ if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return (freq - 2407) / 5;
+ else
+ return 15 + ((freq - 2512) / 20);
+ } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */
+ 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);
+ return (freq - 5000) / 5;
+ }
+}
+
+/*
+ * Convert channel to IEEE channel number.
+ */
+u_int
+ieee80211_chan2ieee(struct ieee80211com *ic, 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_if, "invalid channel freq %u flags %x\n",
+ c->ic_freq, c->ic_flags);
+ return 0; /* XXX */
+ } else {
+ if_printf(&ic->ic_if, "invalid channel (NULL)\n");
+ return 0; /* XXX */
+ }
+}
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+u_int
+ieee80211_ieee2mhz(u_int chan, u_int flags)
+{
+ if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */
+ if (chan == 14)
+ return 2484;
+ if (chan < 14)
+ return 2407 + chan*5;
+ else
+ return 2512 + ((chan-15)*20);
+ } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */
+ return 5000 + (chan*5);
+ } else { /* either, guess */
+ if (chan == 14)
+ return 2484;
+ if (chan < 14) /* 0-13 */
+ return 2407 + chan*5;
+ if (chan < 27) /* 15-26 */
+ return 2512 + ((chan-15)*20);
+ return 5000 + (chan*5);
+ }
+}
+
+/*
+ * 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.
+ */
+void
+ieee80211_media_init(struct ifnet *ifp,
+ 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 ieee80211com *ic = (void *)ifp;
+ struct ifmediareq imr;
+ int i, j, mode, rate, maxrate, mword, mopt, r;
+ 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(ifp);
+
+ /*
+ * Fill in media characteristics.
+ */
+ ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
+ maxrate = 0;
+ 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,
+ };
+ if ((ic->ic_modecaps & (1<<mode)) == 0)
+ 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);
+ if (mode == IEEE80211_MODE_AUTO)
+ continue;
+ 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);
+ 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);
+ /*
+ * Add rate to the collection of all rates.
+ */
+ r = rate & IEEE80211_RATE_VAL;
+ for (j = 0; j < allrates.rs_nrates; j++)
+ if (allrates.rs_rates[j] == r)
+ break;
+ if (j == allrates.rs_nrates) {
+ /* unique, add to the set */
+ allrates.rs_rates[j] = r;
+ allrates.rs_nrates++;
+ }
+ rate = (rate & IEEE80211_RATE_VAL) / 2;
+ if (rate > maxrate)
+ maxrate = rate;
+ }
+ }
+ for (i = 0; i < allrates.rs_nrates; i++) {
+ mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
+ 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);
+ }
+ ieee80211_media_status(ifp, &imr);
+ ifmedia_set(&ic->ic_media, imr.ifm_active);
+
+ if (maxrate)
+ ifp->if_baudrate = IF_Mbps(maxrate);
+
+ if (ic->ic_max_aid == 0)
+ ic->ic_max_aid = IEEE80211_MAX_AID;
+
+#undef ADD
+}
+
+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
+}
+
+/*
+ * Handle a media change request.
+ */
+int
+ieee80211_media_change(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ifmedia_entry *ime;
+ enum ieee80211_opmode newopmode;
+ enum ieee80211_phymode newphymode;
+ int i, j, newrate, error = 0;
+
+ ime = ic->ic_media.ifm_cur;
+ /*
+ * First, identify the phy mode.
+ */
+ switch (IFM_MODE(ime->ifm_media)) {
+ case IFM_IEEE80211_11A:
+ newphymode = IEEE80211_MODE_11A;
+ break;
+ case IFM_IEEE80211_11B:
+ newphymode = IEEE80211_MODE_11B;
+ break;
+ case IFM_IEEE80211_11G:
+ newphymode = IEEE80211_MODE_11G;
+ break;
+ case IFM_IEEE80211_FH:
+ newphymode = IEEE80211_MODE_FH;
+ break;
+ case IFM_AUTO:
+ newphymode = IEEE80211_MODE_AUTO;
+ break;
+ default:
+ return EINVAL;
+ }
+ /*
+ * Turbo mode is an ``option''. Eventually it
+ * needs to be applied to 11g too.
+ */
+ if (ime->ifm_media & IFM_IEEE80211_TURBO) {
+ if (newphymode != IEEE80211_MODE_11A)
+ return EINVAL;
+ newphymode = IEEE80211_MODE_TURBO;
+ }
+ /*
+ * Validate requested mode is available.
+ */
+ if ((ic->ic_modecaps & (1<<newphymode)) == 0)
+ return EINVAL;
+
+ /*
+ * Next, the fixed/variable rate.
+ */
+ i = -1;
+ if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
+ /*
+ * Convert media subtype to rate.
+ */
+ newrate = ieee80211_media2rate(ime->ifm_media);
+ if (newrate == 0)
+ 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 */
+
+ /*
+ * Deduce new operating mode but don't install it just yet.
+ */
+ if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
+ (IFM_IEEE80211_ADHOC|IFM_FLAG0))
+ newopmode = IEEE80211_M_AHDEMO;
+ else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
+ newopmode = IEEE80211_M_HOSTAP;
+ else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
+ newopmode = IEEE80211_M_IBSS;
+ else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
+ newopmode = IEEE80211_M_MONITOR;
+ else
+ 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;
+ error = ENETRESET;
+ }
+
+ /*
+ * Committed to changes, install the rate setting.
+ */
+ if (ic->ic_fixed_rate != i) {
+ ic->ic_fixed_rate = i; /* set fixed tx rate */
+ error = ENETRESET;
+ }
+
+ /*
+ * Handle operating mode change.
+ */
+ if (ic->ic_opmode != newopmode) {
+ ic->ic_opmode = newopmode;
+ switch (newopmode) {
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_STA:
+ case IEEE80211_M_MONITOR:
+ ic->ic_flags &= ~IEEE80211_F_IBSSON;
+ break;
+ case IEEE80211_M_IBSS:
+ ic->ic_flags |= IEEE80211_F_IBSSON;
+#ifdef notdef
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ ieee80211_set11gbasicrates(
+ &ic->ic_sup_rates[newphymode],
+ IEEE80211_MODE_11B);
+#endif
+ break;
+ }
+ error = ENETRESET;
+ }
+#ifdef notdef
+ if (error == 0)
+ ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
+#endif
+ return error;
+}
+
+void
+ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni = NULL;
+
+ imr->ifm_status = IFM_AVALID;
+ imr->ifm_active = IFM_IEEE80211;
+ if (ic->ic_state == IEEE80211_S_RUN)
+ imr->ifm_status |= IFM_ACTIVE;
+ imr->ifm_active |= IFM_AUTO;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ ni = ic->ic_bss;
+ /* calculate rate subtype */
+ imr->ifm_active |= ieee80211_rate2media(ic,
+ ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
+ 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:
+ imr->ifm_active |= IFM_IEEE80211_11A
+ | IFM_IEEE80211_TURBO;
+ break;
+ }
+}
+
+void
+ieee80211_watchdog(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
+ ieee80211_timeout_nodes(ic);
+
+ if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
+ ifp->if_timer = 1;
+}
+
+/*
+ * Mark the basic rates for the 11g rate table based on the
+ * operating mode. For real 11g we mark all the 11b rates
+ * and 6, 12, and 24 OFDM. For 11b compatibility we mark only
+ * 11b rates. There's also a pseudo 11a-mode used to mark only
+ * the basic OFDM rates.
+ */
+static void
+ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+{
+ static const struct ieee80211_rateset basic[] = {
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */
+ { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */
+ { 0 }, /* IEEE80211_MODE_FH */
+ { 0 }, /* IEEE80211_MODE_TURBO */
+ };
+ int i, j;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+ for (j = 0; j < basic[mode].rs_nrates; j++)
+ if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
+ rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
+ break;
+ }
+ }
+}
+
+/*
+ * Set the current phy mode and recalculate the active channel
+ * set based on the available channels for this mode. Also
+ * select a new default/current channel if the current one is
+ * inappropriate for this mode.
+ */
+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 */
+ };
+ struct ieee80211_channel *c;
+ u_int modeflags;
+ int i;
+
+ /* validate new mode */
+ if ((ic->ic_modecaps & (1<<mode)) == 0) {
+ IEEE80211_DPRINTF(("%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.
+ */
+ IASSERT(mode < N(chanflags), ("Unexpected mode %u", mode));
+ modeflags = chanflags[mode];
+ for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+ c = &ic->ic_channels[i];
+ if (mode == IEEE80211_MODE_AUTO) {
+ /* ignore turbo channels for autoselect */
+ if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
+ break;
+ } else {
+ if ((c->ic_flags & modeflags) == modeflags)
+ break;
+ }
+ }
+ if (i > IEEE80211_CHAN_MAX) {
+ IEEE80211_DPRINTF(("%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 (mode == IEEE80211_MODE_AUTO) {
+ /* take anything but pure turbo channels */
+ if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0)
+ 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;
+ }
+ IASSERT(ic->ic_ibss_chan != NULL &&
+ isset(ic->ic_chan_active,
+ ieee80211_chan2ieee(ic, ic->ic_ibss_chan)),
+ ("Bad IBSS channel %u\n",
+ ieee80211_chan2ieee(ic, ic->ic_ibss_chan)));
+ }
+
+ /*
+ * Set/reset state flags that influence beacon contents, etc.
+ *
+ * XXX what if we have stations already associated???
+ * XXX probably not right for autoselect?
+ */
+ if (ic->ic_caps & IEEE80211_C_SHPREAMBLE)
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ if (mode == IEEE80211_MODE_11G) {
+ if (ic->ic_caps & IEEE80211_C_SHSLOT)
+ ic->ic_flags |= IEEE80211_F_SHSLOT;
+ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
+ IEEE80211_MODE_11G);
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+ }
+
+ ic->ic_curmode = mode;
+ return 0;
+#undef N
+}
+
+/*
+ * Return the phy mode for with the specified channel so the
+ * caller can select a rate set. This is problematic and the
+ * work here assumes how things work elsewhere in this code.
+ *
+ * XXX never returns turbo modes -dcy
+ */
+enum ieee80211_phymode
+ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+ /*
+ * NB: this assumes the channel would not be supplied to us
+ * unless it was already compatible with the current mode.
+ */
+ if (ic->ic_curmode != IEEE80211_MODE_AUTO ||
+ chan == IEEE80211_CHAN_ANYC)
+ return ic->ic_curmode;
+ /*
+ * In autoselect mode; deduce a mode based on the channel
+ * characteristics. We assume that turbo-only channels
+ * are not considered when the channel set is constructed.
+ */
+ if (IEEE80211_IS_CHAN_5GHZ(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))
+ return IEEE80211_MODE_11G;
+ else
+ return IEEE80211_MODE_11B;
+}
+
+/*
+ * convert IEEE80211 rate value to ifmedia subtype.
+ * ieee80211 rate is in unit of 0.5Mbps.
+ */
+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[] = {
+ { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 },
+ { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 },
+ { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 },
+ { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 },
+ { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 },
+ { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 },
+ { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 },
+ { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 },
+ { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 },
+ { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 },
+ { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 },
+ { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 },
+ { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 },
+ { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 },
+ { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 },
+ { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 },
+ { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 },
+ { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 },
+ { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 },
+ { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 },
+ { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 },
+ { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 },
+ { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 },
+ { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 },
+ { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 },
+ { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 },
+ { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 },
+ /* NB: OFDM72 doesn't realy exist so we don't handle it */
+ };
+ u_int mask, i;
+
+ mask = rate & IEEE80211_RATE_VAL;
+ switch (mode) {
+ case IEEE80211_MODE_11A:
+ case IEEE80211_MODE_TURBO:
+ mask |= IFM_IEEE80211_11A;
+ break;
+ case IEEE80211_MODE_11B:
+ mask |= IFM_IEEE80211_11B;
+ break;
+ case IEEE80211_MODE_FH:
+ mask |= IFM_IEEE80211_FH;
+ break;
+ 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;
+ }
+ /* NB: hack, 11g matches both 11b+11a rates */
+ /* fall thru... */
+ case IEEE80211_MODE_11G:
+ mask |= IFM_IEEE80211_11G;
+ break;
+ }
+ for (i = 0; i < N(rates); i++)
+ if (rates[i].m == mask)
+ return rates[i].r;
+ return IFM_AUTO;
+#undef N
+}
+
+int
+ieee80211_media2rate(int mword)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+ static const struct {
+ int subtype;
+ int rate;
+ } ieeerates[] = {
+ { IFM_AUTO, -1 },
+ { IFM_MANUAL, 0 },
+ { IFM_NONE, 0 },
+ { IFM_IEEE80211_FH1, 2 },
+ { IFM_IEEE80211_FH2, 4 },
+ { IFM_IEEE80211_DS1, 2 },
+ { IFM_IEEE80211_DS2, 4 },
+ { IFM_IEEE80211_DS5, 11 },
+ { IFM_IEEE80211_DS11, 22 },
+ { IFM_IEEE80211_DS22, 44 },
+ { IFM_IEEE80211_OFDM6, 12 },
+ { IFM_IEEE80211_OFDM9, 18 },
+ { IFM_IEEE80211_OFDM12, 24 },
+ { IFM_IEEE80211_OFDM18, 36 },
+ { IFM_IEEE80211_OFDM24, 48 },
+ { IFM_IEEE80211_OFDM36, 72 },
+ { IFM_IEEE80211_OFDM48, 96 },
+ { IFM_IEEE80211_OFDM54, 108 },
+ { IFM_IEEE80211_OFDM72, 144 },
+ };
+ for (i = 0; i < N(ieeerates); i++) {
+ if (ieeerates[i].subtype == IFM_SUBTYPE(mword))
+ return ieeerates[i].rate;
+ }
+ return 0;
+#undef N
+}
+
+#ifdef __NetBSD__
+/* TBD factor with sysctl_ath_verify. */
+static int
+sysctl_ieee80211_verify(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int*)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return (error);
+
+ IEEE80211_DPRINTF(("%s: t = %d, nodenum = %d, rnodenum = %d\n",
+ __func__, t, node.sysctl_num, rnode->sysctl_num));
+
+ if (node.sysctl_num == ieee80211_inact_max_nodenum) {
+ if (t < 1)
+ return (EINVAL);
+ t = roundup(t, IEEE80211_INACT_WAIT) / IEEE80211_INACT_WAIT;
+#ifdef IEEE80211_DEBUG
+ } else if (node.sysctl_num == ieee80211_debug_nodenum) {
+ if (t < 0 || t > 2)
+ return (EINVAL);
+#endif /* IEEE80211_DEBUG */
+ } else
+ return (EINVAL);
+
+ *(int*)rnode->sysctl_data = t;
+
+ return (0);
+}
+
+/*
+ * Setup sysctl(3) MIB, net.ieee80211.*
+ *
+ * TBD condition CTLFLAG_PERMANENT on being an LKM or not
+ */
+SYSCTL_SETUP(sysctl_ieee80211, "sysctl ieee80211 subtree setup")
+{
+ int rc;
+ struct sysctlnode *cnode, *rnode;
+
+ if ((rc = sysctl_createv(clog, 0, NULL, &rnode,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "net", NULL,
+ NULL, 0, NULL, 0, CTL_NET, CTL_EOL)) != 0)
+ goto err;
+
+ if ((rc = sysctl_createv(clog, 0, &rnode, &rnode,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "link",
+ "link-layer statistics and controls",
+ NULL, 0, NULL, 0, PF_LINK, CTL_EOL)) != 0)
+ goto err;
+
+ if ((rc = sysctl_createv(clog, 0, &rnode, &rnode,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211",
+ "IEEE 802.11 WLAN statistics and controls",
+ NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+ goto err;
+
+#ifdef IEEE80211_DEBUG
+
+ /* control debugging printfs */
+ if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
+ "debug", SYSCTL_DESCR("Enable IEEE 802.11 debugging output"),
+ sysctl_ieee80211_verify, 0, &ieee80211_debug, 0,
+ CTL_CREATE, CTL_EOL)) != 0)
+ goto err;
+
+ ieee80211_debug_nodenum = cnode->sysctl_num;
+
+#endif /* IEEE80211_DEBUG */
+
+ /* control inactivity timer */
+ if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
+ "maxinact", SYSCTL_DESCR("Station inactivity timeout"),
+ sysctl_ieee80211_verify, 0, &ieee80211_inact_max,
+ 0, CTL_CREATE, CTL_EOL)) != 0)
+ goto err;
+
+ ieee80211_inact_max_nodenum = cnode->sysctl_num;
+
+ return;
+err:
+ printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
+}
+#endif /* __NetBSD__ */
+
+#ifdef __FreeBSD__
+/*
+ * Module glue.
+ *
+ * NB: the module name is "wlan" for compatibility with NetBSD.
+ */
+
+static int
+ieee80211_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ if (bootverbose)
+ printf("wlan: <802.11 Link Layer>\n");
+ return 0;
+ case MOD_UNLOAD:
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t ieee80211_mod = {
+ "wlan",
+ ieee80211_modevent,
+ 0
+};
+DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan, 1);
+MODULE_DEPEND(wlan, rc4, 1, 1, 1);
+MODULE_DEPEND(wlan, ether, 1, 1, 1);
+#endif
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
new file mode 100644
index 00000000000..f845845d2b6
--- /dev/null
+++ b/sys/net80211/ieee80211.h
@@ -0,0 +1,548 @@
+/* $OpenBSD: ieee80211.h,v 1.1 2004/06/22 22:53:51 millert Exp $ */
+/* $NetBSD: ieee80211.h,v 1.6 2004/04/30 23:51:53 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.5 2004/04/05 17:47:40 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_H_
+#define _NET80211_IEEE80211_H_
+
+/*
+ * 802.11 protocol definitions.
+ */
+
+#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */
+/* is 802.11 address multicast/broadcast? */
+#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01)
+
+/* 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;
+} __attribute__((__packed__));
+
+#define IEEE80211_PLCP_SFD 0xF3A0
+#define IEEE80211_PLCP_SERVICE 0x00
+
+/*
+ * 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];
+ /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+ /* see below */
+} __attribute__((__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];
+ /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+ /* see below */
+} __attribute__((__packed__));
+
+struct ieee80211_qoscntl {
+ u_int8_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];
+} __attribute__((__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];
+} __attribute__((__packed__));
+
+/*
+ * 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;
+} __attribute__((__packed__));
+#define MNF_SETUP_REQ 0
+#define MNF_SETUP_RESP 1
+#define MNF_TEARDOWN 2
+
+/*
+ * 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];
+} __attribute__((__packed__));
+
+#define IEEE80211_FC0_VERSION_MASK 0x03
+#define IEEE80211_FC0_VERSION_SHIFT 0
+#define IEEE80211_FC0_VERSION_0 0x00
+#define IEEE80211_FC0_TYPE_MASK 0x0c
+#define IEEE80211_FC0_TYPE_SHIFT 2
+#define IEEE80211_FC0_TYPE_MGT 0x00
+#define IEEE80211_FC0_TYPE_CTL 0x04
+#define IEEE80211_FC0_TYPE_DATA 0x08
+
+#define IEEE80211_FC0_SUBTYPE_MASK 0xf0
+#define IEEE80211_FC0_SUBTYPE_SHIFT 4
+/* for TYPE_MGT */
+#define IEEE80211_FC0_SUBTYPE_ASSOC_REQ 0x00
+#define IEEE80211_FC0_SUBTYPE_ASSOC_RESP 0x10
+#define IEEE80211_FC0_SUBTYPE_REASSOC_REQ 0x20
+#define IEEE80211_FC0_SUBTYPE_REASSOC_RESP 0x30
+#define IEEE80211_FC0_SUBTYPE_PROBE_REQ 0x40
+#define IEEE80211_FC0_SUBTYPE_PROBE_RESP 0x50
+#define IEEE80211_FC0_SUBTYPE_BEACON 0x80
+#define IEEE80211_FC0_SUBTYPE_ATIM 0x90
+#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0
+#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0
+#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0
+/* for TYPE_CTL */
+#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0
+#define IEEE80211_FC0_SUBTYPE_RTS 0xb0
+#define IEEE80211_FC0_SUBTYPE_CTS 0xc0
+#define IEEE80211_FC0_SUBTYPE_ACK 0xd0
+#define IEEE80211_FC0_SUBTYPE_CF_END 0xe0
+#define IEEE80211_FC0_SUBTYPE_CF_END_ACK 0xf0
+/* for TYPE_DATA (bit combination) */
+#define IEEE80211_FC0_SUBTYPE_DATA 0x00
+#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10
+#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20
+#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30
+#define IEEE80211_FC0_SUBTYPE_NODATA 0x40
+#define IEEE80211_FC0_SUBTYPE_CFACK 0x50
+#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60
+#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70
+#define IEEE80211_FC0_SUBTYPE_QOS 0x80
+
+#define IEEE80211_FC1_DIR_MASK 0x03
+#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */
+#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */
+#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */
+#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */
+
+#define IEEE80211_FC1_MORE_FRAG 0x04
+#define IEEE80211_FC1_RETRY 0x08
+#define IEEE80211_FC1_PWR_MGT 0x10
+#define IEEE80211_FC1_MORE_DATA 0x20
+#define IEEE80211_FC1_WEP 0x40
+#define IEEE80211_FC1_ORDER 0x80
+
+#define IEEE80211_SEQ_FRAG_MASK 0x000f
+#define IEEE80211_SEQ_FRAG_SHIFT 0
+#define IEEE80211_SEQ_SEQ_MASK 0xfff0
+#define IEEE80211_SEQ_SEQ_SHIFT 4
+
+#define IEEE80211_NWID_LEN 32
+
+#define IEEE80211_QOS_TXOP 0x00ff
+/* bit 8 is reserved */
+#define IEEE80211_QOS_ACKPOLICY 0x0600
+#define IEEE80211_QOS_ESOP 0x0800
+#define IEEE80211_QOS_TID 0xf000
+
+/*
+ * 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];
+ /* FCS */
+} __attribute__((__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];
+ /* FCS */
+} __attribute__((__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];
+ /* FCS */
+} __attribute__((__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];
+ /* FCS */
+} __attribute__((__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];
+ /* FCS */
+} __attribute__((__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];
+ /* FCS */
+} __attribute__((__packed__));
+
+/*
+ * BEACON management packets
+ *
+ * octet timestamp[8]
+ * octet beacon interval[2]
+ * octet capability information[2]
+ * information element
+ * octet elemid
+ * octet length
+ * octet information[length]
+ */
+
+typedef uint8_t *ieee80211_mgt_beacon_t;
+
+#define IEEE80211_BEACON_INTERVAL(beacon) \
+ ((beacon)[8] | ((beacon)[9] << 8))
+#define IEEE80211_BEACON_CAPABILITY(beacon) \
+ ((beacon)[10] | ((beacon)[11] << 8))
+
+#define IEEE80211_CAPINFO_ESS 0x0001
+#define IEEE80211_CAPINFO_IBSS 0x0002
+#define IEEE80211_CAPINFO_CF_POLLABLE 0x0004
+#define IEEE80211_CAPINFO_CF_POLLREQ 0x0008
+#define IEEE80211_CAPINFO_PRIVACY 0x0010
+#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_SHORT_SLOTTIME 0x0400
+#define IEEE80211_CAPINFO_RSN 0x0800
+/* bit 12 is reserved */
+#define IEEE80211_CAPINFO_DSSSOFDM 0x2000
+/* bits 14-15 are reserved */
+
+/*
+ * 802.11i/WPA information element (maximally sized).
+ */
+struct ieee80211_ie_wpa {
+ 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 */
+} __attribute__((__packed__));
+
+/*
+ * Management information elements
+ */
+struct ieee80211_information {
+ char ssid[IEEE80211_NWID_LEN+1];
+ struct rates {
+ u_int8_t *p;
+ } rates;
+ struct fh {
+ u_int16_t dwell;
+ u_int8_t set;
+ u_int8_t pattern;
+ u_int8_t index;
+ } fh;
+ struct ds {
+ u_int8_t channel;
+ } ds;
+ struct cf {
+ u_int8_t count;
+ u_int8_t period;
+ u_int8_t maxdur[2];
+ u_int8_t dur[2];
+ } cf;
+ struct tim {
+ u_int8_t count;
+ u_int8_t period;
+ u_int8_t bitctl;
+ /* u_int8_t pvt[251]; The driver needs to use this. */
+ } tim;
+ struct ibss {
+ u_int16_t atim;
+ } ibss;
+ struct challenge {
+ u_int8_t *p;
+ u_int8_t len;
+ } challenge;
+ struct erp {
+ u_int8_t flags;
+ } erp;
+ struct country {
+ u_int8_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;
+ } band[4]; /* up to 4 sub bands */
+ } country;
+ struct ath {
+ u_int8_t flags;
+ } ath;
+ struct ieee80211_ie_wpa wpa;
+};
+
+enum {
+ IEEE80211_ELEMID_SSID = 0,
+ IEEE80211_ELEMID_RATES = 1,
+ IEEE80211_ELEMID_FHPARMS = 2,
+ IEEE80211_ELEMID_DSPARMS = 3,
+ IEEE80211_ELEMID_CFPARMS = 4,
+ IEEE80211_ELEMID_TIM = 5,
+ IEEE80211_ELEMID_IBSSPARMS = 6,
+ IEEE80211_ELEMID_COUNTRY = 7,
+ IEEE80211_ELEMID_CHALLENGE = 16,
+ /* 17-31 reserved for challenge text extension */
+ IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_TPC = 150,
+ IEEE80211_ELEMID_CCKM = 156,
+ IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
+};
+
+#define IEEE80211_CHALLENGE_LEN 128
+
+#define IEEE80211_RATE_BASIC 0x80
+#define IEEE80211_RATE_VAL 0x7f
+
+/* EPR information element flags */
+#define IEEE80211_ERP_NON_ERP_PRESENT 0x01
+#define IEEE80211_ERP_USE_PROTECTION 0x02
+#define IEEE80211_ERP_BARKER_MODE 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_TYPE 0x01
+#define ATH_OUI_VERSION 0x01
+
+#define WPA_OUI 0xf25000
+#define WPA_OUI_TYPE 0x01
+#define WPA_OUI_VERSION 1 /* current supported version */
+
+#define WPA_CSE_NULL 0x00
+#define WPA_CSE_WEP40 0x01
+#define WPA_CSE_TKIP 0x02
+#define WPA_CSE_WRAP 0x03 /* WPA2/802.11i */
+#define WPA_CSE_CCMP 0x04
+#define WPA_CSE_WEP104 0x05
+
+#define WPA_ASE_NONE 0x00
+#define WPA_ASE_8021X_UNSPEC 0x01
+#define WPA_ASE_8021X_PSK 0x02
+
+/*
+ * AUTH management packets
+ *
+ * octet algo[2]
+ * octet seq[2]
+ * octet status[2]
+ * octet chal.id
+ * octet chal.length
+ * octet chal.text[253]
+ */
+
+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))
+
+#define IEEE80211_AUTH_ALG_OPEN 0x0000
+#define IEEE80211_AUTH_ALG_SHARED 0x0001
+#define IEEE80211_AUTH_ALG_LEAP 0x0080
+
+enum {
+ IEEE80211_AUTH_OPEN_REQUEST = 1,
+ IEEE80211_AUTH_OPEN_RESPONSE = 2,
+};
+
+enum {
+ IEEE80211_AUTH_SHARED_REQUEST = 1,
+ IEEE80211_AUTH_SHARED_CHALLENGE = 2,
+ IEEE80211_AUTH_SHARED_RESPONSE = 3,
+ IEEE80211_AUTH_SHARED_PASS = 4,
+};
+
+/*
+ * Reason codes
+ *
+ * Unlisted codes are reserved
+ */
+
+enum {
+ IEEE80211_REASON_UNSPECIFIED = 1,
+ IEEE80211_REASON_AUTH_EXPIRE = 2,
+ IEEE80211_REASON_AUTH_LEAVE = 3,
+ IEEE80211_REASON_ASSOC_EXPIRE = 4,
+ IEEE80211_REASON_ASSOC_TOOMANY = 5,
+ IEEE80211_REASON_NOT_AUTHED = 6,
+ 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_STATUS_SUCCESS = 0,
+ IEEE80211_STATUS_UNSPECIFIED = 1,
+ IEEE80211_STATUS_CAPINFO = 10,
+ IEEE80211_STATUS_NOT_ASSOCED = 11,
+ IEEE80211_STATUS_OTHER = 12,
+ IEEE80211_STATUS_ALG = 13,
+ IEEE80211_STATUS_SEQUENCE = 14,
+ IEEE80211_STATUS_CHALLENGE = 15,
+ 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,
+};
+
+#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
+#define IEEE80211_WEP_IVLEN 3 /* 24bit */
+#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
+#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
+#define IEEE80211_WEP_NKID 4 /* number of key ids */
+
+#define IEEE80211_CRC_LEN 4
+
+/*
+ * Maximum acceptable MTU is:
+ * IEEE80211_MAX_LEN - WEP overhead - CRC -
+ * QoS overhead - RSN/WPA overhead
+ * Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default
+ * mtu is Ethernet-compatible; it's set by ether_ifattach.
+ */
+#define IEEE80211_MTU_MAX 2290
+#define IEEE80211_MTU_MIN 32
+
+#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \
+ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+#define IEEE80211_MIN_LEN \
+ (sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN)
+
+#define IEEE80211_MAX_AID 2007
+
+#define IEEE80211_AID_SET(b, w) \
+ ((w)[((b) & ~0xc000) / 32] |= (1 << (((b) & ~0xc000) % 32)))
+#define IEEE80211_AID_CLR(b, w) \
+ ((w)[((b) & ~0xc000) / 32] &= ~(1 << (((b) & ~0xc000) % 32)))
+#define IEEE80211_AID_ISSET(b, w) \
+ ((w)[((b) & ~0xc000) / 32] & (1 << (((b) & ~0xc000) % 32)))
+
+/*
+ * RTS frame length parameters. The default is specified in
+ * the 802.11 spec. The max may be wrong for jumbo frames.
+ */
+#define IEEE80211_RTS_DEFAULT 512
+#define IEEE80211_RTS_MIN 1
+#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN
+
+enum {
+ IEEE80211_AUTH_NONE = 0,
+ IEEE80211_AUTH_OPEN = 1,
+ IEEE80211_AUTH_SHARED = 2,
+};
+
+#endif /* _NET80211_IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211_compat.c b/sys/net80211/ieee80211_compat.c
new file mode 100644
index 00000000000..c171cdab40d
--- /dev/null
+++ b/sys/net80211/ieee80211_compat.c
@@ -0,0 +1,54 @@
+/* $OpenBSD: ieee80211_compat.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_compat.c,v 1.3 2003/09/23 15:57:25 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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/socket.h>
+#include <sys/systm.h>
+#include <machine/stdarg.h>
+#include <net/if.h>
+#include <net80211/ieee80211_compat.h>
+
+#ifdef __NetBSD__
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_compat.c,v 1.3 2003/09/23 15:57:25 dyoung Exp $");
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+void
+if_printf(struct ifnet *ifp, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ printf("%s: ", ifp->if_xname);
+ vprintf(fmt, ap);
+
+ va_end(ap);
+ return;
+}
+#endif
diff --git a/sys/net80211/ieee80211_compat.h b/sys/net80211/ieee80211_compat.h
new file mode 100644
index 00000000000..b217c261ceb
--- /dev/null
+++ b/sys/net80211/ieee80211_compat.h
@@ -0,0 +1,64 @@
+/* $OpenBSD: ieee80211_compat.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_compat.h,v 1.5 2004/01/13 23:37:30 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+#ifndef _NET80211_IEEE80211_COMPAT_H_
+#define _NET80211_IEEE80211_COMPAT_H_
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define IASSERT(cond, complaint) if (!(cond)) panic complaint
+#else
+#define IASSERT(cond, complaint) KASSERT(cond, complaint)
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+void if_printf(struct ifnet *, const char *, ...)
+ __attribute__((__format__(__printf__,2,3)));
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define ieee80211_node_critsec_decl(v) int v
+#define ieee80211_node_critsec_begin(ic, v) do { v = splnet(); } while (0)
+#define ieee80211_node_critsec_end(ic, v) splx(v)
+#else
+#define ieee80211_node_critsec_decl(v) /* empty */
+#define ieee80211_node_critsec_begin(ic, v) mtx_lock(&(ic)->ic_nodelock)
+#define ieee80211_node_critsec_end(ic, v) mtx_unlock(&ic->ic_nodelock)
+#endif
+
+#if defined(__OpenBSD__)
+#define BPF_ATTACH(ifp_x,dlt_x,dlf_x,bpf_x) \
+ bpfattach(bpf_x,ifp_x,dlt_x,dlf_x)
+#else
+#define BPF_ATTACH(ifp_x,dlt_x,dlf_x,bpf_x) \
+ bpfattach2(ifp_x,dlt_x,dlf_x,bpf_x)
+#endif
+#define BPF_DETACH(ifp_x) bpfdetach(ifp_x)
+#define BPF_MTAP(bpf_x,mbf_x) bpf_mtap(bpf_x,mbf_x)
+
+#endif /* _NET80211_IEEE80211_COMPAT_H_ */
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
new file mode 100644
index 00000000000..055caf247a6
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto.c
@@ -0,0 +1,364 @@
+/* $OpenBSD: ieee80211_crypto.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_crypto.c,v 1.5 2003/12/14 09:56:53 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $");
+#elif defined(__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto.c,v 1.5 2003/12/14 09:56:53 dyoung Exp $");
+#endif
+
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#ifdef __FreeBSD__
+#include <machine/atomic.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+#include <net/if_llc.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+
+#if defined(__FreeBSD__)
+#include <crypto/rc4/rc4.h>
+#define arc4_ctxlen() sizeof (struct rc4_state)
+#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l)
+#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l)
+#elif defined(__OpenBSD__)
+#include <dev/rndvar.h>
+#include <crypto/arc4.h>
+#define arc4_ctxlen() sizeof (struct rc4_ctx)
+#define arc4_setkey(_c,_k,_l) rc4_keysetup(_c,_k,_l)
+#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l)
+#else
+#include <crypto/arc4/arc4.h>
+#endif
+
+static void ieee80211_crc_init(void);
+static u_int32_t ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len);
+
+void
+ieee80211_crypto_attach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ /*
+ * Setup crypto support.
+ */
+ ieee80211_crc_init();
+ ic->ic_iv = arc4random();
+}
+
+void
+ieee80211_crypto_detach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ if (ic->ic_wep_ctx != NULL) {
+ free(ic->ic_wep_ctx, M_DEVBUF);
+ ic->ic_wep_ctx = NULL;
+ }
+}
+
+struct mbuf *
+ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct mbuf *m, *n, *n0;
+ struct ieee80211_frame *wh;
+ int i, left, len, moff, noff, kid;
+ u_int32_t iv, crc;
+ u_int8_t *ivp;
+ void *ctx;
+ u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+ u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
+
+ n0 = NULL;
+ if ((ctx = ic->ic_wep_ctx) == NULL) {
+ ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT);
+ if (ctx == NULL) {
+ ic->ic_stats.is_crypto_nomem++;
+ goto fail;
+ }
+ ic->ic_wep_ctx = ctx;
+ }
+ m = m0;
+ left = m->m_pkthdr.len;
+ MGET(n, M_DONTWAIT, m->m_type);
+ n0 = n;
+ if (n == NULL) {
+ if (txflag)
+ ic->ic_stats.is_tx_nombuf++;
+ else
+ ic->ic_stats.is_rx_nombuf++;
+ goto fail;
+ }
+#if defined(__FreeBSD__)
+ M_MOVE_PKTHDR(n, m);
+#elif defined(__OpenBSD__)
+ M_DUP_PKTHDR(n, m);
+#else
+ M_COPY_PKTHDR(n, m);
+#endif
+ len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
+ if (txflag) {
+ n->m_pkthdr.len += len;
+ } else {
+ n->m_pkthdr.len -= len;
+ left -= len;
+ }
+ n->m_len = MHLEN;
+ if (n->m_pkthdr.len >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ len = sizeof(struct ieee80211_frame);
+ memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
+ wh = mtod(n, struct ieee80211_frame *);
+ left -= len;
+ moff = len;
+ noff = len;
+ if (txflag) {
+ kid = ic->ic_wep_txkey;
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ iv = ic->ic_iv;
+ /*
+ * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
+ * (B, 255, N) with 3 <= B < 8
+ */
+ if (iv >= 0x03ff00 &&
+ (iv & 0xf8ff00) == 0x00ff00)
+ iv += 0x000100;
+ ic->ic_iv = iv + 1;
+ /* put iv in little endian to prepare 802.11i */
+ ivp = mtod(n, u_int8_t *) + noff;
+ for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
+ ivp[i] = iv & 0xff;
+ iv >>= 8;
+ }
+ ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */
+ noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ } else {
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ ivp = mtod(m, u_int8_t *) + moff;
+ kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
+ moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ }
+ memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
+ memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
+ ic->ic_nw_keys[kid].wk_len);
+ arc4_setkey(ctx, keybuf,
+ IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
+
+ /* encrypt with calculating CRC */
+ crc = ~0;
+ while (left > 0) {
+ len = m->m_len - moff;
+ if (len == 0) {
+ m = m->m_next;
+ moff = 0;
+ continue;
+ }
+ if (len > n->m_len - noff) {
+ len = n->m_len - noff;
+ if (len == 0) {
+ MGET(n->m_next, M_DONTWAIT, n->m_type);
+ if (n->m_next == NULL) {
+ if (txflag)
+ ic->ic_stats.is_tx_nombuf++;
+ else
+ ic->ic_stats.is_rx_nombuf++;
+ goto fail;
+ }
+ n = n->m_next;
+ n->m_len = MLEN;
+ if (left >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ noff = 0;
+ continue;
+ }
+ }
+ if (len > left)
+ len = left;
+ arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
+ mtod(m, caddr_t) + moff, len);
+ if (txflag)
+ crc = ieee80211_crc_update(crc,
+ mtod(m, u_int8_t *) + moff, len);
+ else
+ crc = ieee80211_crc_update(crc,
+ mtod(n, u_int8_t *) + noff, len);
+ left -= len;
+ moff += len;
+ noff += len;
+ }
+ crc = ~crc;
+ if (txflag) {
+ *(u_int32_t *)crcbuf = htole32(crc);
+ if (n->m_len >= noff + sizeof(crcbuf))
+ n->m_len = noff + sizeof(crcbuf);
+ else {
+ n->m_len = noff;
+ MGET(n->m_next, M_DONTWAIT, n->m_type);
+ if (n->m_next == NULL) {
+ ic->ic_stats.is_tx_nombuf++;
+ goto fail;
+ }
+ n = n->m_next;
+ n->m_len = sizeof(crcbuf);
+ noff = 0;
+ }
+ arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
+ sizeof(crcbuf));
+ } else {
+ n->m_len = noff;
+ for (noff = 0; noff < sizeof(crcbuf); noff += len) {
+ len = sizeof(crcbuf) - noff;
+ if (len > m->m_len - moff)
+ len = m->m_len - moff;
+ if (len > 0)
+ arc4_encrypt(ctx, crcbuf + noff,
+ mtod(m, caddr_t) + moff, len);
+ m = m->m_next;
+ moff = 0;
+ }
+ if (crc != letoh32(*(u_int32_t *)crcbuf)) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ if_printf(ifp, "decrypt CRC error\n");
+ if (ieee80211_debug > 1)
+ ieee80211_dump_pkt(n0->m_data,
+ n0->m_len, -1, -1);
+ }
+#endif
+ ic->ic_stats.is_rx_decryptcrc++;
+ goto fail;
+ }
+ }
+ m_freem(m0);
+ return n0;
+
+ fail:
+ m_freem(m0);
+ m_freem(n0);
+ return NULL;
+}
+
+/*
+ * CRC 32 -- routine from RFC 2083
+ */
+
+/* Table of CRCs of all 8-bit messages */
+static u_int32_t ieee80211_crc_table[256];
+
+/* Make the table for a fast CRC. */
+static void
+ieee80211_crc_init(void)
+{
+ u_int32_t c;
+ int n, k;
+
+ for (n = 0; n < 256; n++) {
+ c = (u_int32_t)n;
+ for (k = 0; k < 8; k++) {
+ if (c & 1)
+ c = 0xedb88320UL ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ ieee80211_crc_table[n] = c;
+ }
+}
+
+/*
+ * Update a running CRC with the bytes buf[0..len-1]--the CRC
+ * should be initialized to all 1's, and the transmitted value
+ * is the 1's complement of the final running CRC
+ */
+
+static u_int32_t
+ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len)
+{
+ u_int8_t *endbuf;
+
+ for (endbuf = buf + len; buf < endbuf; buf++)
+ crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+ return crc;
+}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
new file mode 100644
index 00000000000..553072ddd54
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto.h
@@ -0,0 +1,53 @@
+/* $OpenBSD: ieee80211_crypto.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_CRYPTO_H_
+#define _NET80211_IEEE80211_CRYPTO_H_
+
+/*
+ * 802.11 protocol crypto-related definitions.
+ */
+#define IEEE80211_KEYBUF_SIZE 16
+
+struct ieee80211_wepkey {
+ int wk_len;
+ u_int8_t wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+extern void ieee80211_crypto_attach(struct ifnet *);
+extern void ieee80211_crypto_detach(struct ifnet *);
+extern struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int);
+#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
new file mode 100644
index 00000000000..5ddfa6ed618
--- /dev/null
+++ b/sys/net80211/ieee80211_input.c
@@ -0,0 +1,1607 @@
+/* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.20 2004/04/02 23:35:24 sam Exp $");
+#elif defined(__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $");
+#endif
+
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#ifdef __FreeBSD__
+#include <machine/atomic.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+#include <net/if_llc.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+
+#ifdef __OpenBSD__
+#include <dev/rndvar.h>
+#endif
+
+static void ieee80211_recv_pspoll(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to ic_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+void
+ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
+ int rssi, u_int32_t rstamp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_frame *wh;
+ struct ether_header *eh;
+ struct mbuf *m1;
+ int error, len;
+ u_int8_t dir, type, subtype;
+ u_int8_t *bssid;
+ u_int16_t rxseq;
+#if !defined(__OpenBSD__)
+ ALTQ_DECL(struct altq_pktattr pktattr;)
+#endif
+
+ IASSERT(ni != NULL, ("null node"));
+
+ /* 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;
+ }
+
+ /*
+ * In monitor mode, send everything directly to bpf.
+ * Also do not process frames w/o i_addr2 any further.
+ * XXX may want to include the CRC
+ */
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ m->m_pkthdr.len < sizeof(struct ieee80211_frame_min))
+ goto out;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "receive packet with wrong version: %x\n",
+ wh->i_fc[0]);
+ ic->ic_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ /*
+ * NB: We are not yet prepared to handle control frames,
+ * but permitting drivers to send them to us allows
+ * them to go through bpf tapping at the 802.11 layer.
+ */
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ /* XXX statistic */
+ IEEE80211_DPRINTF2(("%s: frame too short, len %u\n",
+ __func__, m->m_pkthdr.len));
+ ic->ic_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (ic->ic_state != IEEE80211_S_SCAN) {
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
+ /* not interested in */
+ IEEE80211_DPRINTF2(("%s: discard frame from "
+ "bss %s\n", __func__,
+ ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ if (dir == IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr3;
+ else
+ bssid = wh->i_addr1;
+ if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
+#ifdef __OpenBSD__
+ !IEEE80211_ADDR_EQ(bssid, etherbroadcastaddr) &&
+#else
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr) &&
+#endif
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_DATA) {
+ /* not interested in */
+ IEEE80211_DPRINTF2(("%s: discard frame from "
+ "bss %s\n", __func__,
+ ether_sprintf(bssid)));
+ ic->ic_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ break;
+ case IEEE80211_M_MONITOR:
+ goto out;
+ default:
+ /* XXX catch bad values */
+ break;
+ }
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ rxseq = ni->ni_rxseq;
+ ni->ni_rxseq =
+ letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+ /* TODO: fragment */
+ if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ rxseq == ni->ni_rxseq) {
+ /* duplicate, silently discarded */
+ ic->ic_stats.is_rx_dup++; /* XXX per-station stat */
+ goto out;
+ }
+ ni->ni_inact = 0;
+ }
+
+ if (ic->ic_set_tim != NULL &&
+ (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT)
+ && ni->ni_pwrsave == 0) {
+ /* turn on power save mode */
+
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: power save mode on for %s\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
+
+ ni->ni_pwrsave = IEEE80211_PS_SLEEP;
+ }
+ if (ic->ic_set_tim != NULL &&
+ (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0 &&
+ ni->ni_pwrsave != 0) {
+ /* turn off power save mode, dequeue stored packets */
+
+ ni->ni_pwrsave = 0;
+ if (ic->ic_set_tim)
+ ic->ic_set_tim(ic, ni->ni_associd, 0);
+
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: power save mode off for %s\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
+
+ while (!IF_IS_EMPTY(&ni->ni_savedq)) {
+ struct mbuf *m;
+ IF_DEQUEUE(&ni->ni_savedq, m);
+ IF_ENQUEUE(&ic->ic_pwrsaveq, m);
+ (*ifp->if_start)(ifp);
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ if (dir != IEEE80211_FC1_DIR_FROMDS) {
+ ic->ic_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
+ /*
+ * In IEEE802.11 network, multicast packet
+ * sent from me is broadcasted from AP.
+ * It should be silently discarded for
+ * SIMPLEX interface.
+ */
+ ic->ic_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ ic->ic_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ break;
+ case IEEE80211_M_HOSTAP:
+ if (dir != IEEE80211_FC1_DIR_TODS) {
+ ic->ic_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* check if source STA is associated */
+ if (ni == ic->ic_bss) {
+ IEEE80211_DPRINTF(("%s: data from unknown src "
+ "%s\n", __func__,
+ ether_sprintf(wh->i_addr2)));
+ /* NB: caller deals with reference */
+ ni = ieee80211_dup_bss(ic, wh->i_addr2);
+ if (ni != NULL) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_AUTHED);
+ ieee80211_free_node(ic, ni);
+ }
+ ic->ic_stats.is_rx_notassoc++;
+ goto err;
+ }
+ if (ni->ni_associd == 0) {
+ IEEE80211_DPRINTF(("ieee80211_input: "
+ "data from unassoc src %s\n",
+ ether_sprintf(wh->i_addr2)));
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_NOT_ASSOCED);
+ ieee80211_unref_node(&ni);
+ ic->ic_stats.is_rx_notassoc++;
+ goto err;
+ }
+ break;
+ case IEEE80211_M_MONITOR:
+ break;
+ }
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (ic->ic_flags & IEEE80211_F_WEPON) {
+ m = ieee80211_wep_crypt(ifp, m, 0);
+ if (m == NULL) {
+ ic->ic_stats.is_rx_wepfail++;
+ goto err;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ } else {
+ ic->ic_stats.is_rx_nowep++;
+ goto out;
+ }
+ }
+#if NBPFILTER > 0
+ /* copy to listener after decrypt */
+ if (ic->ic_rawbpf)
+ BPF_MTAP(ic->ic_rawbpf, m);
+#endif
+ m = ieee80211_decap(ifp, m);
+ if (m == NULL) {
+ IEEE80211_DPRINTF(("ieee80211_input: "
+ "decapsulation error for src %s\n",
+ ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_decap++;
+ goto err;
+ }
+ ifp->if_ipackets++;
+
+ /* perform as a bridge within the AP */
+ m1 = NULL;
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ eh = mtod(m, struct ether_header *);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
+ if (m1 == NULL)
+ ifp->if_oerrors++;
+ else
+ m1->m_flags |= M_MCAST;
+ } else {
+ ni = ieee80211_find_node(ic, eh->ether_dhost);
+ if (ni != NULL) {
+ if (ni->ni_associd != 0) {
+ m1 = m;
+ m = NULL;
+ }
+ ieee80211_free_node(ic, ni);
+ }
+ }
+ if (m1 != NULL) {
+#if !defined(__OpenBSD__)
+#ifdef ALTQ
+ if (ALTQ_IS_ENABLED(&ifp->if_snd))
+ altq_etherclassify(&ifp->if_snd, m1,
+ &pktattr);
+#endif
+#endif
+ len = m1->m_pkthdr.len;
+ IFQ_ENQUEUE(&ifp->if_snd, m1, NULL, error);
+ if (error)
+ ifp->if_oerrors++;
+ else {
+ if (m != NULL)
+ ifp->if_omcasts++;
+ ifp->if_obytes += len;
+ }
+ }
+ }
+ if (m != NULL) {
+#if NBPFILTER > 0
+ /*
+ * If we forward packet into transmitter of the AP,
+ * we don't need to duplicate for DLT_EN10MB.
+ */
+ if (ifp->if_bpf && m1 == NULL)
+ BPF_MTAP(ifp->if_bpf, m);
+#endif
+#if defined(__OpenBSD__)
+ ether_input_mbuf(ifp, m);
+#else
+ (*ifp->if_input)(ifp, m);
+#endif
+ }
+ return;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ ic->ic_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ ic->ic_stats.is_rx_ahdemo_mgt++;
+ goto out;
+ }
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+ /* drop frames without interest */
+ if (ic->ic_state == IEEE80211_S_SCAN) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
+ subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ ic->ic_stats.is_rx_mgtdiscard++;
+ goto out;
+ }
+ } else {
+ if (ic->ic_opmode != IEEE80211_M_IBSS &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ ic->ic_stats.is_rx_mgtdiscard++;
+ goto out;
+ }
+ }
+
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* avoid to print too many frames */
+ int doprint = 0;
+
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ doprint = 1;
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ doprint = 1;
+ break;
+ default:
+ doprint = 1;
+ break;
+ }
+#ifdef IEEE80211_DEBUG
+ doprint += ieee80211_debug;
+#endif
+ if (doprint)
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype
+ >> IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf)
+ BPF_MTAP(ic->ic_rawbpf, m);
+#endif
+ (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
+ m_freem(m);
+ return;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ ic->ic_stats.is_rx_ctl++;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ goto out;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if (subtype == IEEE80211_FC0_SUBTYPE_PS_POLL) {
+ /* XXX statistic */
+ /* Dump out a single packet from the host */
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: got power save probe from %s\n",
+ ifp->if_xname,
+ ether_sprintf(wh->i_addr2));
+ ieee80211_recv_pspoll(ic, m, rssi, rstamp);
+ }
+ goto out;
+
+ default:
+ IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type));
+ /* should not come here */
+ break;
+ }
+ err:
+ ifp->if_ierrors++;
+ out:
+ if (m != NULL) {
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf)
+ BPF_MTAP(ic->ic_rawbpf, m);
+#endif
+ m_freem(m);
+ }
+}
+
+struct mbuf *
+ieee80211_decap(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ether_header *eh;
+ struct ieee80211_frame wh;
+ struct llc *llc;
+
+ if (m->m_len < sizeof(wh) + sizeof(*llc)) {
+ m = m_pullup(m, sizeof(wh) + sizeof(*llc));
+ if (m == NULL)
+ return NULL;
+ }
+ memcpy(&wh, mtod(m, caddr_t), sizeof(wh));
+ llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh));
+ if (llc->llc_dsap == LLC_SNAP_LSAP && 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) {
+ m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh));
+ llc = NULL;
+ } else {
+ m_adj(m, sizeof(wh) - sizeof(*eh));
+ }
+ eh = mtod(m, struct ether_header *);
+ switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
+ IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
+ IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
+ IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
+ break;
+ case IEEE80211_FC1_DIR_DSTODS:
+ /* not yet supported */
+ IEEE80211_DPRINTF(("%s: DS to DS\n", __func__));
+ m_freem(m);
+ return NULL;
+ }
+#ifdef ALIGNED_POINTER
+ if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
+ struct mbuf *n, *n0, **np;
+ caddr_t newdata;
+ int off, pktlen;
+
+ n0 = NULL;
+ np = &n0;
+ off = 0;
+ pktlen = m->m_pkthdr.len;
+ while (pktlen > off) {
+ if (n0 == NULL) {
+ MGETHDR(n, M_DONTWAIT, MT_DATA);
+ if (n == NULL) {
+ m_freem(m);
+ return NULL;
+ }
+#if defined(__FreeBSD__)
+ M_MOVE_PKTHDR(n, m);
+#elif defined(__OpenBSD__)
+ M_DUP_PKTHDR(n, m);
+#else
+ M_COPY_PKTHDR(n, m);
+#endif
+ n->m_len = MHLEN;
+ } else {
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n == NULL) {
+ m_freem(m);
+ m_freem(n0);
+ return NULL;
+ }
+ n->m_len = MLEN;
+ }
+ if (pktlen - off >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ if (n0 == NULL) {
+ newdata =
+ (caddr_t)ALIGN(n->m_data + sizeof(*eh)) -
+ sizeof(*eh);
+ n->m_len -= newdata - n->m_data;
+ n->m_data = newdata;
+ }
+ if (n->m_len > pktlen - off)
+ n->m_len = pktlen - off;
+ m_copydata(m, off, n->m_len, mtod(n, caddr_t));
+ off += n->m_len;
+ *np = n;
+ np = &n->m_next;
+ }
+ m_freem(m);
+ m = n0;
+ }
+#endif /* ALIGNED_POINTER */
+ if (llc != NULL) {
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
+ }
+ return m;
+}
+
+/*
+ * Install received rate set information in the node's state block.
+ */
+static int
+ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t *rates, u_int8_t *xrates, int flags)
+{
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ memset(rs, 0, sizeof(*rs));
+ rs->rs_nrates = rates[1];
+ memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
+ if (xrates != NULL) {
+ u_int8_t nxrates;
+ /*
+ * Tack on 11g extended supported rate element.
+ */
+ nxrates = xrates[1];
+ if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
+ nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
+ IEEE80211_DPRINTF(("%s: extended rate set too large;"
+ " only using %u of %u rates\n",
+ __func__, nxrates, xrates[1]));
+ ic->ic_stats.is_rx_rstoobig++;
+ }
+ memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
+ rs->rs_nrates += nxrates;
+ }
+ return ieee80211_fix_rate(ic, ni, flags);
+}
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
+ if ((__elem) == NULL) { \
+ IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n", \
+ __func__, ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT])); \
+ ic->ic_stats.is_rx_elem_missing++; \
+ return; \
+ } \
+ if ((__elem)[1] > (__maxlen)) { \
+ IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s " \
+ "frame from %s\n", __func__, (__elem)[1], \
+ ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT], \
+ ether_sprintf(wh->i_addr2))); \
+ ic->ic_stats.is_rx_elem_toobig++; \
+ return; \
+ } \
+} while (0)
+
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
+ if ((_len) < (_minlen)) { \
+ IEEE80211_DPRINTF(("%s: %s frame too short from %s\n", \
+ __func__, \
+ ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT], \
+ ether_sprintf(wh->i_addr2))); \
+ ic->ic_stats.is_rx_elem_toosmall++; \
+ return; \
+ } \
+} while (0)
+
+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 ifnet *ifp = &ic->ic_if;
+ int allocbs;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_IBSS:
+ if (ic->ic_state != IEEE80211_S_RUN ||
+ seq != IEEE80211_AUTH_OPEN_REQUEST) {
+ IEEE80211_DPRINTF(("%s: discard auth from %s; "
+ "state %u, seq %u\n", __func__,
+ ether_sprintf(wh->i_addr2),
+ ic->ic_state, seq));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+
+ case IEEE80211_M_AHDEMO:
+ /* should not come here */
+ break;
+
+ case IEEE80211_M_HOSTAP:
+ if (ic->ic_state != IEEE80211_S_RUN ||
+ seq != IEEE80211_AUTH_OPEN_REQUEST) {
+ IEEE80211_DPRINTF(("%s: discard auth from %s; "
+ "state %u, seq %u\n", __func__,
+ ether_sprintf(wh->i_addr2),
+ ic->ic_state, seq));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (ni == ic->ic_bss) {
+ ni = ieee80211_alloc_node(ic, wh->i_addr2);
+ if (ni == NULL) {
+ ic->ic_stats.is_rx_nodealloc++;
+ return;
+ }
+ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ ni->ni_chan = ic->ic_bss->ni_chan;
+ allocbs = 1;
+ } else
+ allocbs = 0;
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s %s authenticated (open)\n",
+ ether_sprintf(ni->ni_macaddr),
+ (allocbs ? "newly" : "already"));
+ break;
+ case IEEE80211_M_STA:
+ if (ic->ic_state != IEEE80211_S_AUTH ||
+ seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+ ic->ic_stats.is_rx_bad_auth++;
+ IEEE80211_DPRINTF(("%s: discard auth from %s; "
+ "state %u, seq %u\n", __func__,
+ ether_sprintf(wh->i_addr2),
+ ic->ic_state, seq));
+ return;
+ }
+ if (status != 0) {
+ if_printf(&ic->ic_if,
+ "open authentication failed (reason %d) for %s\n",
+ status,
+ ether_sprintf(wh->i_addr3));
+ if (ni != ic->ic_bss)
+ ni->ni_fails++;
+ ic->ic_stats.is_rx_auth_fail++;
+ return;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_M_MONITOR:
+ break;
+ }
+}
+
+/* TBD send appropriate responses on error? */
+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)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ u_int8_t *challenge = NULL;
+ int allocbs, i;
+
+ if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
+ IEEE80211_DPRINTF(("%s: WEP is off\n", __func__));
+ return;
+ }
+
+ if (frm + 1 < efrm) {
+ if (frm[1] + 2 > efrm - frm) {
+ IEEE80211_DPRINTF(("elt %d %d bytes too long\n",
+ frm[0], (frm[1] + 2) - (int)(efrm - frm)));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DPRINTF(("%s: no challenge sent\n",
+ __func__));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DPRINTF(("%s: bad challenge len %d\n",
+ __func__, challenge[1]));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ default:
+ break;
+ }
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_IBSS:
+ IEEE80211_DPRINTF(("%s: unexpected operating mode\n",
+ __func__));
+ return;
+ case IEEE80211_M_HOSTAP:
+ if (ic->ic_state != IEEE80211_S_RUN) {
+ IEEE80211_DPRINTF(("%s: not running\n", __func__));
+ return;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_REQUEST:
+ if (ni == ic->ic_bss) {
+ ni = ieee80211_alloc_node(ic, wh->i_addr2);
+ if (ni == NULL) {
+ ic->ic_stats.is_rx_nodealloc++;
+ return;
+ }
+ IEEE80211_ADDR_COPY(ni->ni_bssid,
+ ic->ic_bss->ni_bssid);
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ ni->ni_chan = ic->ic_bss->ni_chan;
+ allocbs = 1;
+ } else
+ allocbs = 0;
+ if (ni->ni_challenge == NULL)
+ ni->ni_challenge = (u_int32_t*)malloc(
+ IEEE80211_CHALLENGE_LEN, M_DEVBUF,
+ M_NOWAIT);
+ if (ni->ni_challenge == NULL) {
+ IEEE80211_DPRINTF(("challenge alloc failed\n"));
+ /* XXX statistic */
+ return;
+ }
+ for (i = IEEE80211_CHALLENGE_LEN / sizeof(u_int32_t);
+ --i >= 0; )
+ ni->ni_challenge[i] = arc4random();
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s shared key "
+ "%sauthentication\n",
+ ether_sprintf(ni->ni_macaddr),
+ allocbs ? "" : "re");
+ break;
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (ni == ic->ic_bss) {
+ IEEE80211_DPRINTF(("%s: unknown STA\n",
+ __func__));
+ return;
+ }
+ if (ni->ni_challenge == NULL) {
+ IEEE80211_DPRINTF((
+ "%s: no challenge recorded\n", __func__));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (memcmp(ni->ni_challenge, &challenge[2],
+ challenge[1]) != 0) {
+ IEEE80211_DPRINTF(("%s: challenge mismatch\n",
+ __func__));
+ ic->ic_stats.is_rx_auth_fail++;
+ return;
+ }
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s authenticated "
+ "(shared key)\n",
+ ether_sprintf(ni->ni_macaddr));
+ break;
+ default:
+ IEEE80211_DPRINTF(("%s: bad seq %d from %s\n",
+ __func__, seq, ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ break;
+
+ case IEEE80211_M_STA:
+ if (ic->ic_state != IEEE80211_S_AUTH)
+ return;
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_PASS:
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_DEVBUF);
+ ni->ni_challenge = NULL;
+ }
+ if (status != 0) {
+ if_printf(&ic->ic_if,
+ "%s: shared authentication failed "
+ "(reason %d) for %s\n",
+ __func__, status,
+ ether_sprintf(wh->i_addr3));
+ if (ni != ic->ic_bss)
+ ni->ni_fails++;
+ ic->ic_stats.is_rx_auth_fail++;
+ return;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ if (ni->ni_challenge == NULL)
+ ni->ni_challenge = (u_int32_t*)malloc(
+ challenge[1], M_DEVBUF, M_NOWAIT);
+ if (ni->ni_challenge == NULL) {
+ IEEE80211_DPRINTF((
+ "%s: challenge alloc failed\n", __func__));
+ /* XXX statistic */
+ return;
+ }
+ memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ break;
+ default:
+ IEEE80211_DPRINTF(("%s: bad seq %d from %s\n",
+ __func__, seq, ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ break;
+ }
+}
+
+void
+ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
+ struct ieee80211_node *ni,
+ int subtype, int rssi, u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ u_int8_t *frm, *efrm;
+ u_int8_t *ssid, *rates, *xrates;
+ int reassoc, resp, newassoc, allocbs;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ u_int8_t *tstamp, *bintval, *capinfo, *country;
+ u_int8_t chan, bchan, fhindex, erp;
+ u_int16_t fhdwell;
+ int isprobe;
+
+ if (ic->ic_opmode != IEEE80211_M_IBSS &&
+ ic->ic_state != IEEE80211_S_SCAN) {
+ /* XXX: may be useful for background scan */
+ return;
+ }
+ isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP);
+
+ /*
+ * beacon/probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] capability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] country information
+ * [tlv] parameter set (FH/DS)
+ * [tlv] erp information
+ * [tlv] extended supported rates
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+ tstamp = frm; frm += 8;
+ bintval = frm; frm += 2;
+ capinfo = frm; frm += 2;
+ ssid = rates = xrates = country = NULL;
+ bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ chan = bchan;
+ fhdwell = 0;
+ fhindex = 0;
+ erp = 0;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_COUNTRY:
+ country = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ fhdwell = (frm[3] << 8) | frm[2];
+ chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+ fhindex = frm[6];
+ }
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
+ /*
+ * XXX hack this since depending on phytype
+ * is problematic for multi-mode devices.
+ */
+ if (ic->ic_phytype != IEEE80211_T_FH)
+ chan = frm[2];
+ break;
+ case IEEE80211_ELEMID_TIM:
+ break;
+ case IEEE80211_ELEMID_IBSSPARMS:
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_ERP:
+ if (frm[1] != 1) {
+ IEEE80211_DPRINTF(("%s: invalid ERP "
+ "element; length %u, expecting "
+ "1\n", __func__, frm[1]));
+ ic->ic_stats.is_rx_elem_toobig++;
+ break;
+ }
+ erp = frm[2];
+ break;
+ default:
+ IEEE80211_DPRINTF2(("%s: element id %u/len %u "
+ "ignored\n", __func__, *frm, frm[1]));
+ ic->ic_stats.is_rx_elem_unknown++;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+ if (
+#if IEEE80211_CHAN_MAX < 255
+ chan > IEEE80211_CHAN_MAX ||
+#endif
+ isclr(ic->ic_chan_active, chan)) {
+ IEEE80211_DPRINTF(("%s: ignore %s with invalid channel "
+ "%u\n", __func__,
+ isprobe ? "probe response" : "beacon",
+ chan));
+ ic->ic_stats.is_rx_badchan++;
+ return;
+ }
+ if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) {
+ /*
+ * Frame was received on a channel different from the
+ * one indicated in the DS params element id;
+ * silently discard it.
+ *
+ * NB: this can happen due to signal leakage.
+ * But we should take it for FH phy because
+ * the rssi value should be correct even for
+ * different hop pattern in FH.
+ */
+ IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked "
+ "for channel %u\n", __func__,
+ isprobe ? "probe response" : "beacon",
+ bchan, chan));
+ ic->ic_stats.is_rx_chanmismatch++;
+ return;
+ }
+
+ /*
+ * Use mac and channel for lookup so we collect all
+ * potential AP's when scanning. Otherwise we may
+ * see the same AP on multiple channels and will only
+ * record the last one. We could filter APs here based
+ * on rssi, etc. but leave that to the end of the scan
+ * so we can keep the selection criteria in one spot.
+ * This may result in a bloat of the scanned AP list but
+ * it shouldn't be too much.
+ */
+ ni = ieee80211_lookup_node(ic, wh->i_addr2,
+ &ic->ic_channels[chan]);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug &&
+ (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {
+ printf("%s: %s%s on chan %u (bss chan %u) ",
+ __func__, (ni == NULL ? "new " : ""),
+ isprobe ? "probe response" : "beacon",
+ chan, bchan);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf(" from %s\n", ether_sprintf(wh->i_addr2));
+ printf("%s: caps 0x%x bintval %u erp 0x%x\n",
+ __func__, letoh16(*(u_int16_t *)capinfo),
+ letoh16(*(u_int16_t *)bintval), erp);
+ if (country) {
+ int i;
+ printf("%s: country info", __func__);
+ for (i = 0; i < country[1]; i++)
+ printf(" %02x", country[i+2]);
+ printf("\n");
+ }
+ }
+#endif
+ if (ni == NULL) {
+ ni = ieee80211_alloc_node(ic, wh->i_addr2);
+ if (ni == NULL)
+ return;
+ ni->ni_esslen = ssid[1];
+ memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
+ memcpy(ni->ni_essid, ssid + 2, ssid[1]);
+ allocbs = 1;
+ } else if (ssid[1] != 0 && (isprobe || ni->ni_esslen == 0)) {
+ /*
+ * Update ESSID at probe response to adopt hidden AP by
+ * Lucent/Cisco, which announces null ESSID in beacon.
+ */
+ ni->ni_esslen = ssid[1];
+ memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
+ memcpy(ni->ni_essid, ssid + 2, ssid[1]);
+ allocbs = 0;
+ } else
+ allocbs = 0;
+ IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
+ ni->ni_intval = letoh16(*(u_int16_t *)bintval);
+ ni->ni_capinfo = letoh16(*(u_int16_t *)capinfo);
+ /* XXX validate channel # */
+ ni->ni_chan = &ic->ic_channels[chan];
+ ni->ni_fhdwell = fhdwell;
+ ni->ni_fhindex = fhindex;
+ ni->ni_erp = erp;
+ /* NB: must be after ni_chan is setup */
+ ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
+ /*
+ * When scanning we record results (nodes) with a zero
+ * refcnt. Otherwise we want to hold the reference for
+ * ibss neighbors so the nodes don't get released prematurely.
+ * Anything else can be discarded (XXX and should be handled
+ * above so we don't do so much work).
+ */
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ ieee80211_unref_node(&ni); /* NB: do not free */
+ else if (ic->ic_opmode == IEEE80211_M_IBSS &&
+ allocbs && isprobe) {
+ /*
+ * Fake an association so the driver can setup it's
+ * private state. The rate set has been setup above;
+ * there is no handshake as in ap/station operation.
+ */
+ if (ic->ic_newassoc)
+ (*ic->ic_newassoc)(ic, ni, 1);
+ /* NB: hold reference */
+ } else {
+ /* XXX optimize to avoid work done above */
+ ieee80211_free_node(ic, ni);
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ: {
+ u_int8_t rate;
+
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ return;
+ if (ic->ic_state != IEEE80211_S_RUN)
+ return;
+
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+ if (ssid[1] != 0 &&
+ (ssid[1] != ic->ic_bss->ni_esslen ||
+ memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ printf("%s: ssid mismatch ", __func__);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf(" from %s\n", ether_sprintf(wh->i_addr2));
+ }
+#endif
+ ic->ic_stats.is_rx_ssidmismatch++;
+ return;
+ }
+
+ if (ni == ic->ic_bss) {
+ ni = ieee80211_dup_bss(ic, wh->i_addr2);
+ if (ni == NULL)
+ return;
+ IEEE80211_DPRINTF(("%s: new req from %s\n",
+ __func__, ether_sprintf(wh->i_addr2)));
+ allocbs = 1;
+ } else
+ allocbs = 0;
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ rate = ieee80211_setup_rates(ic, ni, rates, xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
+ | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ IEEE80211_DPRINTF(("%s: rate negotiation failed: %s\n",
+ __func__,ether_sprintf(wh->i_addr2)));
+ } else {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
+ }
+ if (allocbs)
+ ieee80211_free_node(ic, ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ u_int16_t algo, seq, status;
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
+ algo = letoh16(*(u_int16_t *)frm);
+ seq = letoh16(*(u_int16_t *)(frm + 2));
+ status = letoh16(*(u_int16_t *)(frm + 4));
+ IEEE80211_DPRINTF(("%s: auth %d seq %d from %s\n",
+ __func__, algo, seq, ether_sprintf(wh->i_addr2)));
+
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
+ rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,
+ status);
+ else {
+ IEEE80211_DPRINTF(("%s: unsupported authentication "
+ "algorithm %d from %s\n",
+ __func__, algo, ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_auth_unsupported++;
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
+ u_int16_t capinfo, bintval;
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
+ (ic->ic_state != IEEE80211_S_RUN))
+ return;
+
+ if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ reassoc = 1;
+ resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+ } else {
+ reassoc = 0;
+ resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
+ }
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
+ IEEE80211_DPRINTF(("%s: ignore other bss from %s\n",
+ __func__, ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_assoc_bss++;
+ return;
+ }
+ capinfo = letoh16(*(u_int16_t *)frm); frm += 2;
+ bintval = letoh16(*(u_int16_t *)frm); frm += 2;
+ if (reassoc)
+ frm += 6; /* ignore current AP info */
+ ssid = rates = xrates = NULL;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+ if (ssid[1] != ic->ic_bss->ni_esslen ||
+ memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ printf("%s: ssid mismatch ", __func__);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf(" from %s\n", ether_sprintf(wh->i_addr2));
+ }
+#endif
+ ic->ic_stats.is_rx_ssidmismatch++;
+ return;
+ }
+ if (ni == ic->ic_bss) {
+ IEEE80211_DPRINTF(("%s: not authenticated for %s\n",
+ __func__, ether_sprintf(wh->i_addr2)));
+ ni = ieee80211_dup_bss(ic, wh->i_addr2);
+ if (ni != NULL) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED);
+ ieee80211_free_node(ic, ni);
+ }
+ ic->ic_stats.is_rx_assoc_notauth++;
+ return;
+ }
+ /* discard challenge after association */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_DEVBUF);
+ ni->ni_challenge = NULL;
+ }
+ /* XXX per-node cipher suite */
+ /* XXX some stations use the privacy bit for handling APs
+ that suport both encrypted and unencrypted traffic */
+ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
+ (capinfo & IEEE80211_CAPINFO_PRIVACY) !=
+ ((ic->ic_flags & IEEE80211_F_WEPON) ?
+ IEEE80211_CAPINFO_PRIVACY : 0)) {
+ IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n",
+ __func__, capinfo, ether_sprintf(wh->i_addr2)));
+ IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ ni->ni_associd = 0;
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_STATUS_CAPINFO);
+ ic->ic_stats.is_rx_assoc_capmismatch++;
+ return;
+ }
+ ieee80211_setup_rates(ic, ni, rates, xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ni->ni_rates.rs_nrates == 0) {
+ IEEE80211_DPRINTF(("%s: rate mismatch for %s\n",
+ __func__, ether_sprintf(wh->i_addr2)));
+ IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ ni->ni_associd = 0;
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_STATUS_BASIC_RATE);
+ ic->ic_stats.is_rx_assoc_norate++;
+ return;
+ }
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ ni->ni_intval = bintval;
+ ni->ni_capinfo = capinfo;
+ ni->ni_chan = ic->ic_bss->ni_chan;
+ ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
+ ni->ni_fhindex = ic->ic_bss->ni_fhindex;
+ if (ni->ni_associd == 0) {
+ u_int16_t aid;
+
+ /*
+ * It would be clever to search the bitmap
+ * more efficiently, but this will do for now.
+ */
+ for (aid = 1; aid < ic->ic_max_aid; aid++) {
+ if (!IEEE80211_AID_ISSET(aid,
+ ic->ic_aid_bitmap))
+ break;
+ }
+
+ if (ic->ic_bss->ni_associd >= ic->ic_max_aid) {
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_REASON_ASSOC_TOOMANY);
+ return;
+ } else {
+ ni->ni_associd = aid | 0xc000;
+ IEEE80211_AID_SET(ni->ni_associd,
+ ic->ic_aid_bitmap);
+ newassoc = 1;
+ }
+ } else
+ newassoc = 0;
+ /* XXX for 11g must turn off short slot time if long
+ slot time sta associates */
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s %s associated at aid %d\n",
+ (newassoc ? "newly" : "already"),
+ ether_sprintf(ni->ni_macaddr),
+ ni->ni_associd & ~0xc000);
+ /* give driver a chance to setup state like ni_txrate */
+ if (ic->ic_newassoc)
+ (*ic->ic_newassoc)(ic, ni, newassoc);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
+ u_int16_t status;
+
+ if (ic->ic_opmode != IEEE80211_M_STA ||
+ ic->ic_state != IEEE80211_S_ASSOC)
+ return;
+
+ /*
+ * asresp frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
+ ni = ic->ic_bss;
+ ni->ni_capinfo = letoh16(*(u_int16_t *)frm);
+ frm += 2;
+
+ status = letoh16(*(u_int16_t *)frm);
+ frm += 2;
+ if (status != 0) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp,
+ "association failed (reason %d) for %s\n",
+ status, ether_sprintf(wh->i_addr3));
+ if (ni != ic->ic_bss)
+ ni->ni_fails++;
+ ic->ic_stats.is_rx_auth_fail++;
+ return;
+ }
+ ni->ni_associd = letoh16(*(u_int16_t *)frm);
+ frm += 2;
+
+ rates = xrates = NULL;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
+ ieee80211_setup_rates(ic, ni, rates, xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ni->ni_rates.rs_nrates != 0)
+ ieee80211_new_state(ic, IEEE80211_S_RUN,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH: {
+ u_int16_t reason;
+ /*
+ * deauth frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
+ reason = letoh16(*(u_int16_t *)frm);
+ ic->ic_stats.is_rx_deauth++;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_M_HOSTAP:
+ if (ni != ic->ic_bss) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s deauthenticated"
+ " by peer (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ /* node will be free'd on return */
+ ieee80211_unref_node(&ni);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ u_int16_t reason;
+ /*
+ * disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
+ reason = letoh16(*(u_int16_t *)frm);
+ ic->ic_stats.is_rx_disassoc++;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_M_HOSTAP:
+ if (ni != ic->ic_bss) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s disassociated"
+ " by peer (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ IEEE80211_AID_CLR(ni->ni_associd,
+ ic->ic_aid_bitmap);
+ ni->ni_associd = 0;
+ /* XXX node reclaimed how? */
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not "
+ "handled\n", __func__, subtype));
+ ic->ic_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+
+static void
+ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ u_int16_t aid;
+
+ if (ic->ic_set_tim == NULL) /* No powersaving functionality */
+ return;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ if ((ni = ieee80211_find_node(ic, wh->i_addr2)) == NULL) {
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: station %s sent bogus power save poll\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
+ return;
+ }
+
+ memcpy(&aid, wh->i_dur, sizeof(wh->i_dur));
+ if ((aid & 0xc000) != 0xc000) {
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: station %s sent bogus aid %x\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2), aid);
+ return;
+ }
+
+ if (aid != ni->ni_associd) {
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: station %s aid %x doesn't match pspoll "
+ "aid %x\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2),
+ ni->ni_associd, aid);
+ return;
+ }
+
+ /* Okay, take the first queued packet and put it out... */
+
+ IF_DEQUEUE(&ni->ni_savedq, m);
+ if (m == NULL) {
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: station %s sent pspoll, "
+ "but no packets are saved\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
+ return;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /*
+ * If this is the last packet, turn off the TIM fields.
+ * If there are more packets, set the more packets bit.
+ */
+
+ if (IF_IS_EMPTY(&ni->ni_savedq)) {
+ if (ic->ic_set_tim)
+ ic->ic_set_tim(ic, ni->ni_associd, 0);
+ } else {
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ }
+
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: enqueued power saving packet for station %s\n",
+ ifp->if_xname, ether_sprintf(ni->ni_macaddr));
+
+ IF_ENQUEUE(&ic->ic_pwrsaveq, m);
+ (*ifp->if_start)(ifp);
+}
+#undef IEEE80211_VERIFY_LENGTH
+#undef IEEE80211_VERIFY_ELEMENT
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
new file mode 100644
index 00000000000..44ab3e16fe1
--- /dev/null
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -0,0 +1,1384 @@
+/* $OpenBSD: ieee80211_ioctl.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.13 2004/03/30 22:57:57 sam Exp $");
+#elif defined(__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $");
+#endif
+
+/*
+ * IEEE 802.11 ioctl support (FreeBSD-specific)
+ */
+
+#ifdef __NetBSD__
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#endif /* __FreeBSD__ */
+#ifndef __OpenBSD__
+#include <netinet/if_inarp.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#if defined(__FreeBSD__)
+#include <dev/wi/if_wavelan_ieee.h>
+#elif defined(__OpenBSD__)
+#include <dev/ic/if_wi_ieee.h>
+#else
+#include <dev/ic/wi_ieee.h>
+#endif
+
+/*
+ * XXX
+ * Wireless LAN specific configuration interface, which is compatible
+ * with wicontrol(8).
+ */
+
+int
+ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ int i, j, error;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct wi_req wreq;
+ struct wi_ltv_keys *keys;
+ struct wi_apinfo *ap;
+ struct ieee80211_node *ni;
+ struct ieee80211_rateset *rs;
+#ifdef WICACHE
+ struct wi_sigcache wsc;
+#endif /* WICACHE */
+#if 0 /* TBD */
+ struct wi_scan_p2_hdr *p2;
+ struct wi_scan_res *res;
+#endif
+
+ error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
+ if (error)
+ return error;
+ wreq.wi_len = 0;
+ switch (wreq.wi_type) {
+ case WI_RID_SERIALNO:
+ case WI_RID_STA_IDENTITY:
+ case WI_RID_CARD_ID:
+ case WI_RID_PROCFRAME:
+ case WI_RID_PRISM2:
+ case WI_RID_SYMBOL_DIVERSITY:
+ case WI_RID_ENH_SECURITY:
+ case WI_RID_AUTH_STATION:
+ /* nothing appropriate */
+ break;
+ case WI_RID_NODENAME:
+ strlcpy((char *)&wreq.wi_val[1], hostname,
+ sizeof(wreq.wi_val) - sizeof(wreq.wi_val[0]));
+ 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_bss->ni_chan));
+ 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->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 == -1)
+ 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_FRAG_THRESH:
+ wreq.wi_val[0] = htole16(ic->ic_fragthreshold);
+ 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(1); /* enabled ... not supported */
+ 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((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
+ 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_WEPON) ? 1 : 0);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_TX_CRYPT_KEY:
+ wreq.wi_val[0] = htole16(ic->ic_wep_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(curproc, 0);
+ 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_len);
+ memcpy(keys->wi_keys[i].wi_keydat,
+ ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
+ }
+ wreq.wi_len = sizeof(*keys) / 2;
+ break;
+ case WI_RID_MAX_DATALEN:
+ wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_DBM_ADJUST:
+ /* not supported, we just pass rssi value from driver. */
+ break;
+ case WI_RID_IFACE_STATS:
+ /* XXX: should be implemented in lower drivers */
+ break;
+ case WI_RID_READ_APS:
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ /*
+ * Don't return results until active scan completes.
+ */
+ if (ic->ic_state == IEEE80211_S_SCAN &&
+ (ic->ic_flags & IEEE80211_F_ASCAN)) {
+ error = EINPROGRESS;
+ break;
+ }
+ }
+ i = 0;
+ ap = (void *)((char *)wreq.wi_val + sizeof(i));
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
+ break;
+ memset(ap, 0, sizeof(*ap));
+ 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)(ic, 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 */
+ }
+ }
+ i++;
+ ap++;
+ }
+ memcpy(wreq.wi_val, &i, sizeof(i));
+ wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
+ break;
+#if 0
+ case WI_RID_PRISM2:
+ wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */
+ wreq.wi_len = sizeof(u_int16_t) / 2;
+ break;
+ case WI_RID_SCAN_RES: /* compatibility interface */
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_state == IEEE80211_S_SCAN &&
+ (ic->ic_flags & IEEE80211_F_ASCAN)) {
+ error = EINPROGRESS;
+ break;
+ }
+ /* NB: we use the Prism2 format so we can return rate info */
+ p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
+ res = (void *)&p2[1];
+ i = 0;
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
+ break;
+ res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
+ res->wi_noise = 0;
+ res->wi_signal = (*ic->ic_node_getrssi)(ic, 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;
+ res++, i++;
+ }
+ p2->wi_rsvd = 0;
+ p2->wi_reason = i;
+ wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
+ break;
+#endif /* 0 */
+#ifdef WICACHE
+ case WI_RID_READ_CACHE:
+ i = 0;
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
+ break;
+ IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
+ memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
+ wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
+ wsc.noise = 0;
+ wsc.quality = 0;
+ memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
+ &wsc, sizeof(wsc));
+ i++;
+ }
+ wreq.wi_len = sizeof(wsc) * i / 2;
+ break;
+#endif /* WICACHE */
+ case WI_RID_SCAN_APS:
+ error = EINVAL;
+ 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)
+{
+ u_char *chanlist = ic->ic_chan_active;
+ int i;
+
+ 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 (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
+ isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ /*
+ * 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.
+ */
+ return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
+}
+
+int
+ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ieee80211com *ic = (void *)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 (letoh16(wreq.wi_val[0]) * 2 > len ||
+ letoh16(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 = letoh16(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 = letoh16(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_flags & IEEE80211_F_SIBSS)
+ 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 (letoh16(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 (letoh16(wreq.wi_val[0]) != ic->ic_opmode) {
+ ic->ic_opmode = letoh16(wreq.wi_val[0]);
+ error = ENETRESET;
+ }
+ 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 = -1;
+ break;
+ }
+ rate = 2 * letoh16(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 = ENETRESET;
+ break;
+ case WI_RID_CUR_TX_RATE:
+ return EPERM;
+ case WI_RID_FRAG_THRESH:
+ if (len != 2)
+ return EINVAL;
+ ic->ic_fragthreshold = letoh16(wreq.wi_val[0]);
+ error = ENETRESET;
+ break;
+ case WI_RID_RTS_THRESH:
+ if (len != 2)
+ return EINVAL;
+ ic->ic_rtsthreshold = letoh16(wreq.wi_val[0]);
+ error = ENETRESET;
+ 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 = ENETRESET;
+ }
+ } 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 = ENETRESET;
+ }
+ }
+ }
+ 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;
+ if (letoh16(wreq.wi_val[0]) != 1)
+ return EINVAL; /* not supported */
+ break;
+ case WI_RID_SYSTEM_SCALE:
+ if (len != 2)
+ return EINVAL;
+ if (letoh16(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 = ENETRESET;
+ }
+ } else {
+ if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ ic->ic_flags &= ~IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ }
+ break;
+ case WI_RID_MAX_SLEEP:
+ if (len != 2)
+ return EINVAL;
+ ic->ic_lintval = letoh16(wreq.wi_val[0]);
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
+ error = ENETRESET;
+ 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;
+ if (letoh16(wreq.wi_val[0]) != 1)
+ return EINVAL; /* TODO: shared key auth */
+ 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_WEPON) == 0) {
+ ic->ic_flags |= IEEE80211_F_WEPON;
+ error = ENETRESET;
+ }
+ } else {
+ if (ic->ic_flags & IEEE80211_F_WEPON) {
+ ic->ic_flags &= ~IEEE80211_F_WEPON;
+ error = ENETRESET;
+ }
+ }
+ break;
+ case WI_RID_TX_CRYPT_KEY:
+ if (len != 2)
+ return EINVAL;
+ i = letoh16(wreq.wi_val[0]);
+ if (i >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ ic->ic_wep_txkey = i;
+ 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 = letoh16(keys->wi_keys[i].wi_keylen);
+ if (len != 0 && len < IEEE80211_WEP_KEYLEN)
+ return EINVAL;
+ if (len > sizeof(ic->ic_nw_keys[i].wk_key))
+ return EINVAL;
+ }
+ memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ len = letoh16(keys->wi_keys[i].wi_keylen);
+ ic->ic_nw_keys[i].wk_len = len;
+ memcpy(ic->ic_nw_keys[i].wk_key,
+ keys->wi_keys[i].wi_keydat, len);
+ }
+ error = ENETRESET;
+ break;
+ case WI_RID_MAX_DATALEN:
+ if (len != 2)
+ return EINVAL;
+ len = letoh16(wreq.wi_val[0]);
+ if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
+ return EINVAL;
+ if (len != IEEE80211_MAX_LEN)
+ return EINVAL; /* TODO: fragment */
+ ic->ic_fragthreshold = len;
+ error = ENETRESET;
+ 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);
+ 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);
+ }
+ memcpy(ic->ic_chan_active, chanlist,
+ sizeof(ic->ic_chan_active));
+ error = ieee80211_setupscan(ic);
+ 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;
+ }
+ return error;
+}
+
+#ifdef __FreeBSD__
+int
+ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ int error = 0;
+ u_int kid, len;
+ struct ieee80211req *ireq;
+ struct ifreq *ifr;
+ u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+ char tmpssid[IEEE80211_NWID_LEN];
+ struct ieee80211_channel *chan;
+ struct ifaddr *ifa; /* XXX */
+
+ switch (cmd) {
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, (struct ifreq *) data,
+ &ic->ic_media, cmd);
+ break;
+ case SIOCG80211:
+ ireq = (struct ieee80211req *) data;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_SSID:
+ 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);
+ break;
+ default:
+ ireq->i_len = ic->ic_bss->ni_esslen;
+ memcpy(tmpssid, ic->ic_bss->ni_essid,
+ ireq->i_len);
+ break;
+ }
+ error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_NUMSSIDS:
+ ireq->i_val = 1;
+ break;
+ case IEEE80211_IOC_WEP:
+ if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
+ ireq->i_val = IEEE80211_WEP_NOSUP;
+ } else {
+ if (ic->ic_flags & IEEE80211_F_WEPON) {
+ ireq->i_val =
+ IEEE80211_WEP_MIXED;
+ } else {
+ ireq->i_val =
+ IEEE80211_WEP_OFF;
+ }
+ }
+ break;
+ case IEEE80211_IOC_WEPKEY:
+ if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
+ error = EINVAL;
+ break;
+ }
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID) {
+ error = EINVAL;
+ break;
+ }
+ len = (u_int) ic->ic_nw_keys[kid].wk_len;
+ /* NB: only root can read WEP keys */
+ if (suser(curthread) == 0) {
+ bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+ } else {
+ bzero(tmpkey, len);
+ }
+ ireq->i_len = len;
+ error = copyout(tmpkey, ireq->i_data, len);
+ break;
+ case IEEE80211_IOC_NUMWEPKEYS:
+ if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
+ error = EINVAL;
+ else
+ ireq->i_val = IEEE80211_WEP_NKID;
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
+ error = EINVAL;
+ else
+ ireq->i_val = ic->ic_wep_txkey;
+ break;
+ case IEEE80211_IOC_AUTHMODE:
+ ireq->i_val = IEEE80211_AUTH_OPEN;
+ break;
+ case IEEE80211_IOC_CHANNEL:
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ chan = ic->ic_des_chan;
+ else
+ chan = ic->ic_ibss_chan;
+ break;
+ default:
+ chan = ic->ic_bss->ni_chan;
+ break;
+ }
+ ireq->i_val = ieee80211_chan2ieee(ic, chan);
+ break;
+ case IEEE80211_IOC_POWERSAVE:
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
+ ireq->i_val = IEEE80211_POWERSAVE_ON;
+ else
+ ireq->i_val = IEEE80211_POWERSAVE_OFF;
+ break;
+ case IEEE80211_IOC_POWERSAVESLEEP:
+ ireq->i_val = ic->ic_lintval;
+ break;
+ case IEEE80211_IOC_RTSTHRESHOLD:
+ ireq->i_val = ic->ic_rtsthreshold;
+ break;
+ case IEEE80211_IOC_PROTMODE:
+ ireq->i_val = ic->ic_protmode;
+ break;
+ case IEEE80211_IOC_TXPOWER:
+ if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+ error = EINVAL;
+ else
+ ireq->i_val = ic->ic_txpower;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case SIOCS80211:
+ error = suser(curproc, 0);
+ if (error)
+ break;
+ ireq = (struct ieee80211req *) data;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_SSID:
+ if (ireq->i_val != 0 ||
+ ireq->i_len > IEEE80211_NWID_LEN) {
+ error = EINVAL;
+ break;
+ }
+ 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;
+ break;
+ case IEEE80211_IOC_WEP:
+ /*
+ * These cards only support one mode so
+ * we just turn wep on if what ever is
+ * passed in is not OFF.
+ */
+ if (ireq->i_val == IEEE80211_WEP_OFF) {
+ ic->ic_flags &= ~IEEE80211_F_WEPON;
+ } else {
+ ic->ic_flags |= IEEE80211_F_WEPON;
+ }
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_WEPKEY:
+ if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
+ error = EINVAL;
+ break;
+ }
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID) {
+ error = EINVAL;
+ break;
+ }
+ if (ireq->i_len > sizeof(tmpkey)) {
+ error = EINVAL;
+ break;
+ }
+ memset(tmpkey, 0, sizeof(tmpkey));
+ error = copyin(ireq->i_data, tmpkey, ireq->i_len);
+ if (error)
+ break;
+ memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
+ sizeof(tmpkey));
+ ic->ic_nw_keys[kid].wk_len = ireq->i_len;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID) {
+ error = EINVAL;
+ break;
+ }
+ ic->ic_wep_txkey = kid;
+ error = ENETRESET;
+ break;
+#if 0
+ case IEEE80211_IOC_AUTHMODE:
+ sc->wi_authmode = ireq->i_val;
+ break;
+#endif
+ 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)) {
+ error = EINVAL;
+ break;
+ } 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 (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bss->ni_chan != ic->ic_des_chan)
+ error = ENETRESET;
+ } else {
+ if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
+ error = ENETRESET;
+ }
+ break;
+ }
+ break;
+ case IEEE80211_IOC_POWERSAVE:
+ switch (ireq->i_val) {
+ case IEEE80211_POWERSAVE_OFF:
+ if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ ic->ic_flags &= ~IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ break;
+ case IEEE80211_POWERSAVE_ON:
+ if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
+ error = EINVAL;
+ else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+ ic->ic_flags |= IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case IEEE80211_IOC_POWERSAVESLEEP:
+ if (ireq->i_val < 0) {
+ error = EINVAL;
+ break;
+ }
+ ic->ic_lintval = ireq->i_val;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_RTSTHRESHOLD:
+ if (!(IEEE80211_RTS_MIN < ireq->i_val &&
+ ireq->i_val <= IEEE80211_RTS_MAX + 1)) {
+ error = EINVAL;
+ break;
+ }
+ ic->ic_rtsthreshold = ireq->i_val;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_PROTMODE:
+ if (ireq->i_val > IEEE80211_PROT_RTSCTS) {
+ error = EINVAL;
+ break;
+ }
+ ic->ic_protmode = ireq->i_val;
+ /* NB: if not operating in 11g this can wait */
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_TXPOWER:
+ if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) {
+ error = EINVAL;
+ break;
+ }
+ if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
+ ireq->i_val < IEEE80211_TXPOWER_MAX)) {
+ error = EINVAL;
+ break;
+ }
+ ic->ic_txpower = ireq->i_val;
+ error = ENETRESET;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case SIOCGIFGENERIC:
+ error = ieee80211_cfgget(ifp, cmd, data);
+ break;
+ case SIOCSIFGENERIC:
+ error = suser(curproc, 0);
+ if (error)
+ break;
+ error = ieee80211_cfgset(ifp, cmd, data);
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+ return error;
+}
+
+#else /* !__FreeBSD__ */
+
+int
+ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int i, error = 0;
+ struct ieee80211_nwid nwid;
+ struct ieee80211_nwkey *nwkey;
+ struct ieee80211_power *power;
+ struct ieee80211_bssid *bssid;
+ struct ieee80211chanreq *chanreq;
+ struct ieee80211_channel *chan;
+ struct ieee80211_wepkey keys[IEEE80211_WEP_NKID];
+ static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+#ifdef __OpenBSD__
+ error = ether_ioctl(ifp, &ic->ic_ac, cmd, data);
+#else
+ error = ether_ioctl(ifp, cmd, data);
+#endif
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCS80211NWID:
+ if ((error = copyin(ifr->ifr_data, &nwid, sizeof(nwid))) != 0)
+ break;
+ if (nwid.i_len > IEEE80211_NWID_LEN) {
+ error = EINVAL;
+ break;
+ }
+ memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
+ ic->ic_des_esslen = nwid.i_len;
+ memcpy(ic->ic_des_essid, nwid.i_nwid, nwid.i_len);
+ error = ENETRESET;
+ break;
+ case SIOCG80211NWID:
+ memset(&nwid, 0, sizeof(nwid));
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ nwid.i_len = ic->ic_des_esslen;
+ memcpy(nwid.i_nwid, ic->ic_des_essid, nwid.i_len);
+ break;
+ default:
+ nwid.i_len = ic->ic_bss->ni_esslen;
+ memcpy(nwid.i_nwid, ic->ic_bss->ni_essid, nwid.i_len);
+ break;
+ }
+ error = copyout(&nwid, ifr->ifr_data, sizeof(nwid));
+ break;
+ case SIOCS80211NWKEY:
+ nwkey = (struct ieee80211_nwkey *)data;
+ if ((ic->ic_caps & IEEE80211_C_WEP) == 0 &&
+ nwkey->i_wepon != IEEE80211_NWKEY_OPEN) {
+ error = EINVAL;
+ break;
+ }
+ /* check and copy keys */
+ memset(keys, 0, sizeof(keys));
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ keys[i].wk_len = nwkey->i_key[i].i_keylen;
+ if ((keys[i].wk_len > 0 &&
+ keys[i].wk_len < IEEE80211_WEP_KEYLEN) ||
+ keys[i].wk_len > sizeof(keys[i].wk_key)) {
+ error = EINVAL;
+ break;
+ }
+ if (keys[i].wk_len <= 0)
+ continue;
+ if ((error = copyin(nwkey->i_key[i].i_keydat,
+ keys[i].wk_key, keys[i].wk_len)) != 0)
+ break;
+ }
+ if (error)
+ break;
+ i = nwkey->i_defkid - 1;
+ if (i < 0 || i >= IEEE80211_WEP_NKID ||
+ keys[i].wk_len == 0 ||
+ (keys[i].wk_len == -1 && ic->ic_nw_keys[i].wk_len == 0)) {
+ if (nwkey->i_wepon != IEEE80211_NWKEY_OPEN) {
+ error = EINVAL;
+ break;
+ }
+ } else
+ ic->ic_wep_txkey = i;
+ /* save the key */
+ if (nwkey->i_wepon == IEEE80211_NWKEY_OPEN)
+ ic->ic_flags &= ~IEEE80211_F_WEPON;
+ else
+ ic->ic_flags |= IEEE80211_F_WEPON;
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ if (keys[i].wk_len < 0)
+ continue;
+ ic->ic_nw_keys[i].wk_len = keys[i].wk_len;
+ memcpy(ic->ic_nw_keys[i].wk_key, keys[i].wk_key,
+ sizeof(keys[i].wk_key));
+ }
+ error = ENETRESET;
+ break;
+ case SIOCG80211NWKEY:
+ nwkey = (struct ieee80211_nwkey *)data;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ nwkey->i_wepon = IEEE80211_NWKEY_WEP;
+ else
+ nwkey->i_wepon = IEEE80211_NWKEY_OPEN;
+ nwkey->i_defkid = ic->ic_wep_txkey + 1;
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ if (nwkey->i_key[i].i_keydat == NULL)
+ continue;
+ /* do not show any keys to non-root user */
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ nwkey->i_key[i].i_keylen = ic->ic_nw_keys[i].wk_len;
+ if ((error = copyout(ic->ic_nw_keys[i].wk_key,
+ nwkey->i_key[i].i_keydat,
+ ic->ic_nw_keys[i].wk_len)) != 0)
+ break;
+ }
+ break;
+ case SIOCS80211POWER:
+ power = (struct ieee80211_power *)data;
+ ic->ic_lintval = power->i_maxsleep;
+ if (power->i_enabled != 0) {
+ if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
+ error = EINVAL;
+ else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+ ic->ic_flags |= IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ } else {
+ if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ ic->ic_flags &= ~IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ }
+ break;
+ case SIOCG80211POWER:
+ power = (struct ieee80211_power *)data;
+ power->i_enabled = (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0;
+ power->i_maxsleep = ic->ic_lintval;
+ break;
+ case SIOCS80211BSSID:
+ bssid = (struct ieee80211_bssid *)data;
+ if (IEEE80211_ADDR_EQ(bssid->i_bssid, empty_macaddr))
+ ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ else {
+ ic->ic_flags |= IEEE80211_F_DESBSSID;
+ IEEE80211_ADDR_COPY(ic->ic_des_bssid, bssid->i_bssid);
+ }
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ break;
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ error = ENETRESET;
+ break;
+ default:
+ if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(ic->ic_des_bssid,
+ ic->ic_bss->ni_bssid))
+ error = ENETRESET;
+ break;
+ }
+ break;
+ case SIOCG80211BSSID:
+ bssid = (struct ieee80211_bssid *)data;
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ IEEE80211_ADDR_COPY(bssid->i_bssid,
+ ic->ic_myaddr);
+ else if (ic->ic_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(bssid->i_bssid,
+ ic->ic_des_bssid);
+ else
+ memset(bssid->i_bssid, 0, IEEE80211_ADDR_LEN);
+ break;
+ default:
+ IEEE80211_ADDR_COPY(bssid->i_bssid,
+ ic->ic_bss->ni_bssid);
+ break;
+ }
+ break;
+ case SIOCS80211CHANNEL:
+ chanreq = (struct ieee80211chanreq *)data;
+ if (chanreq->i_channel == IEEE80211_CHAN_ANY)
+ ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+ else if (chanreq->i_channel > IEEE80211_CHAN_MAX ||
+ isclr(ic->ic_chan_active, chanreq->i_channel)) {
+ error = EINVAL;
+ break;
+ } else
+ ic->ic_ibss_chan = ic->ic_des_chan =
+ &ic->ic_channels[chanreq->i_channel];
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ error = ENETRESET;
+ break;
+ default:
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bss->ni_chan != ic->ic_des_chan)
+ error = ENETRESET;
+ } else {
+ if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
+ error = ENETRESET;
+ }
+ break;
+ }
+ break;
+ case SIOCG80211CHANNEL:
+ chanreq = (struct ieee80211chanreq *)data;
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ chan = ic->ic_des_chan;
+ else
+ chan = ic->ic_ibss_chan;
+ break;
+ default:
+ chan = ic->ic_bss->ni_chan;
+ break;
+ }
+ chanreq->i_channel = ieee80211_chan2ieee(ic, chan);
+ break;
+ case SIOCGIFGENERIC:
+ error = ieee80211_cfgget(ifp, cmd, data);
+ break;
+ case SIOCSIFGENERIC:
+ error = suser(curproc, 0);
+ if (error)
+ break;
+ error = ieee80211_cfgset(ifp, cmd, data);
+ break;
+ case SIOCG80211STATS:
+ ifr = (struct ifreq *)data;
+ copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
+ break;
+ case SIOCSIFMTU:
+ ifr = (struct ifreq *)data;
+ if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
+ ifr->ifr_mtu <= IEEE80211_MTU_MAX))
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+#endif /* !__FreeBSD__ */
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
new file mode 100644
index 00000000000..7716947b961
--- /dev/null
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -0,0 +1,211 @@
+/* $OpenBSD: ieee80211_ioctl.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $OpenBSD: ieee80211_ioctl.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.5 2004/03/30 22:57:57 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_IOCTL_H_
+#define _NET80211_IEEE80211_IOCTL_H_
+
+/*
+ * IEEE 802.11 ioctls.
+ */
+
+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_nowep; /* rx w/ wep but wep !config */
+ 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_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_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_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_nombuf; /* rx failed for lack of mbuf */
+ 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_tx_nombuf; /* tx failed for lack of mbuf */
+ 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_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 */
+};
+
+#ifdef __FreeBSD__
+/*
+ * FreeBSD-style ioctls.
+ */
+/* 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 */
+ 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 IEEE80211_IOC_SSID 1
+#define IEEE80211_IOC_NUMSSIDS 2
+#define IEEE80211_IOC_WEP 3
+#define IEEE80211_WEP_NOSUP -1
+#define IEEE80211_WEP_OFF 0
+#define IEEE80211_WEP_ON 1
+#define IEEE80211_WEP_MIXED 2
+#define IEEE80211_IOC_WEPKEY 4
+#define IEEE80211_IOC_NUMWEPKEYS 5
+#define IEEE80211_IOC_WEPTXKEY 6
+#define IEEE80211_IOC_AUTHMODE 7
+#define IEEE80211_IOC_STATIONNAME 8
+#define IEEE80211_IOC_CHANNEL 9
+#define IEEE80211_IOC_POWERSAVE 10
+#define IEEE80211_POWERSAVE_NOSUP -1
+#define IEEE80211_POWERSAVE_OFF 0
+#define IEEE80211_POWERSAVE_CAM 1
+#define IEEE80211_POWERSAVE_PSP 2
+#define IEEE80211_POWERSAVE_PSP_CAM 3
+#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM
+#define IEEE80211_IOC_POWERSAVESLEEP 11
+#define IEEE80211_IOC_RTSTHRESHOLD 12
+#define IEEE80211_IOC_PROTMODE 13
+#define IEEE80211_PROTMODE_OFF 0
+#define IEEE80211_PROTMODE_CTS 1
+#define IEEE80211_PROTMODE_RTSCTS 2
+#define IEEE80211_IOC_TXPOWER 14
+
+#ifndef IEEE80211_CHAN_ANY
+#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
+#endif
+
+#define SIOCG80211STATS _IOWR('i', 236, struct ifreq)
+#endif /* __FreeBSD__ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+/* nwid is pointed at by ifr.ifr_data */
+struct ieee80211_nwid {
+ u_int8_t i_len;
+ u_int8_t i_nwid[IEEE80211_NWID_LEN];
+};
+
+#define SIOCS80211NWID _IOWR('i', 230, struct ifreq)
+#define SIOCG80211NWID _IOWR('i', 231, struct ifreq)
+
+/* the first member must be matched with struct ifreq */
+struct ieee80211_nwkey {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ int i_wepon; /* wep enabled flag */
+ int i_defkid; /* default encrypt key id */
+ struct {
+ int i_keylen;
+ u_int8_t *i_keydat;
+ } i_key[IEEE80211_WEP_NKID];
+};
+#define SIOCS80211NWKEY _IOW('i', 232, struct ieee80211_nwkey)
+#define SIOCG80211NWKEY _IOWR('i', 233, struct ieee80211_nwkey)
+/* i_wepon */
+#define IEEE80211_NWKEY_OPEN 0 /* No privacy */
+#define IEEE80211_NWKEY_WEP 1 /* WEP enabled */
+#define IEEE80211_NWKEY_EAP 2 /* EAP enabled */
+#define IEEE80211_NWKEY_PERSIST 0x100 /* designate persist keyset */
+
+/* power management parameters */
+struct ieee80211_power {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ int i_enabled; /* 1 == on, 0 == off */
+ int i_maxsleep; /* max sleep in ms */
+};
+#define SIOCS80211POWER _IOW('i', 234, struct ieee80211_power)
+#define SIOCG80211POWER _IOWR('i', 235, struct ieee80211_power)
+
+struct ieee80211_auth {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ int i_authtype;
+};
+
+#define IEEE80211_AUTH_NONE 0
+#define IEEE80211_AUTH_OPEN 1
+#define IEEE80211_AUTH_SHARED 2
+
+#define SIOCS80211AUTH _IOW('i', 236, struct ieee80211_auth)
+#define SIOCG80211AUTH _IOWR('i', 237, struct ieee80211_auth)
+
+struct ieee80211chanreq {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ u_int16_t i_channel;
+};
+
+#ifndef IEEE80211_CHAN_ANY
+#define IEEE80211_CHAN_ANY 0xffff
+#endif
+
+#define SIOCS80211CHANNEL _IOW('i', 238, struct ieee80211chanreq)
+#define SIOCG80211CHANNEL _IOWR('i', 239, struct ieee80211chanreq)
+
+struct ieee80211_bssid {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ u_int8_t i_bssid[IEEE80211_ADDR_LEN];
+};
+
+#define SIOCS80211BSSID _IOW('i', 240, struct ieee80211_bssid)
+#define SIOCG80211BSSID _IOWR('i', 241, struct ieee80211_bssid)
+
+#define SIOCG80211STATS _IOWR('i', 242, struct ifreq)
+#endif
+
+#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
new file mode 100644
index 00000000000..f143467a576
--- /dev/null
+++ b/sys/net80211/ieee80211_node.c
@@ -0,0 +1,877 @@
+/* $OpenBSD $ */
+/* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.22 2004/04/05 04:15:55 sam Exp $");
+#elif defined(__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $");
+#endif
+
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#ifdef __FreeBSD__
+#include <machine/atomic.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+#include <net/if_llc.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+
+#ifdef __OpenBSD__
+#include <dev/rndvar.h>
+#endif
+
+static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
+static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
+static void ieee80211_node_copy(struct ieee80211com *,
+ struct ieee80211_node *, const struct ieee80211_node *);
+static u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
+ struct ieee80211_node *);
+
+static void ieee80211_setup_node(struct ieee80211com *ic,
+ struct ieee80211_node *ni, u_int8_t *macaddr);
+static void _ieee80211_free_node(struct ieee80211com *,
+ struct ieee80211_node *);
+
+#ifdef __NetBSD__
+MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+#else
+#define M_80211_NODE M_DEVBUF
+#endif
+
+void
+ieee80211_node_attach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+#ifdef __FreeBSD__
+ /* XXX need unit */
+ IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname);
+#endif
+ TAILQ_INIT(&ic->ic_node);
+ ic->ic_node_alloc = ieee80211_node_alloc;
+ ic->ic_node_free = ieee80211_node_free;
+ ic->ic_node_copy = ieee80211_node_copy;
+ ic->ic_node_getrssi = ieee80211_node_getrssi;
+ ic->ic_scangen = 1;
+}
+
+void
+ieee80211_node_lateattach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni;
+
+ ni = (*ic->ic_node_alloc)(ic);
+ IASSERT(ni != NULL, ("unable to setup inital BSS node"));
+ ni->ni_chan = IEEE80211_CHAN_ANYC;
+ ic->ic_bss = ni;
+ ic->ic_txpower = IEEE80211_TXPOWER_MAX;
+}
+
+void
+ieee80211_node_detach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ if (ic->ic_bss != NULL)
+ (*ic->ic_node_free)(ic, ic->ic_bss);
+ ieee80211_free_allnodes(ic);
+#ifdef __FreeBSD__
+ IEEE80211_NODE_LOCK_DESTROY(ic);
+#endif
+}
+
+/*
+ * AP scanning support.
+ */
+
+/*
+ * Initialize the active channel set based on the set
+ * of available channels and the current PHY mode.
+ */
+static void
+ieee80211_reset_scan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ memcpy(ic->ic_chan_scan, ic->ic_chan_active,
+ sizeof(ic->ic_chan_active));
+ /* NB: hack, setup so next_scan starts with the first channel */
+ if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
+ ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX];
+}
+
+/*
+ * Begin an active scan.
+ */
+void
+ieee80211_begin_scan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ /*
+ * 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++;
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "begin %s scan\n",
+ (ic->ic_flags & IEEE80211_F_ASCAN) ?
+ "active" : "passive");
+ /*
+ * Clear scan state and flush any previously seen
+ * AP's. Note that the latter assumes we don't act
+ * as both an AP and a station, otherwise we'll
+ * potentially flush state of stations associated
+ * with us.
+ */
+ ieee80211_reset_scan(ifp);
+ ieee80211_free_allnodes(ic);
+
+ /* Scan the next channel. */
+ ieee80211_next_scan(ifp);
+}
+
+/*
+ * Switch to the next channel marked for scanning.
+ */
+void
+ieee80211_next_scan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_channel *chan;
+
+ chan = ic->ic_bss->ni_chan;
+ for (;;) {
+ if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
+ chan = &ic->ic_channels[0];
+ if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
+ /*
+ * Honor channels marked passive-only
+ * during an active scan.
+ */
+ if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 ||
+ (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
+ break;
+ }
+ if (chan == ic->ic_bss->ni_chan) {
+ ieee80211_end_scan(ifp);
+ return;
+ }
+ }
+ clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
+ IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
+ ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
+ ieee80211_chan2ieee(ic, chan)));
+ ic->ic_bss->ni_chan = chan;
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+}
+
+void
+ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
+{
+ struct ieee80211_node *ni;
+ struct ifnet *ifp = &ic->ic_if;
+
+ ni = ic->ic_bss;
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "creating ibss\n");
+ ic->ic_flags |= IEEE80211_F_SIBSS;
+ ni->ni_chan = chan;
+ ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */
+ ni->ni_esslen = ic->ic_des_esslen;
+ memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
+ ni->ni_rssi = 0;
+ ni->ni_rstamp = 0;
+ memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
+ ni->ni_intval = ic->ic_lintval;
+ ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ ni->ni_fhdwell = 200; /* XXX */
+ ni->ni_fhindex = 1;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+}
+
+int
+ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ u_int8_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;
+ } else {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+ fail |= 0x02;
+ }
+ if (ic->ic_flags & IEEE80211_F_WEPON) {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+ fail |= 0x04;
+ } else {
+ /* XXX does this mean privacy is supported or required? */
+ if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+ fail |= 0x04;
+ }
+ rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
+ 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))
+ fail |= 0x10;
+ if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ fail |= 0x20;
+#ifdef IEEE80211_DEBUG
+ if (ic->ic_if.if_flags & IFF_DEBUG) {
+ 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
+ return fail;
+}
+
+/*
+ * Complete a scan of potential channels.
+ */
+void
+ieee80211_end_scan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni, *nextbs, *selbs;
+ int i, fail;
+
+ ic->ic_flags &= ~IEEE80211_F_ASCAN;
+ ni = TAILQ_FIRST(&ic->ic_node);
+
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ /* XXX off stack? */
+ u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)];
+ /*
+ * 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 unnoccupied one. If that fails, pick a random
+ * channel from the active set.
+ */
+ for (; ni != NULL; ni = nextbs) {
+ ieee80211_ref_node(ni);
+ nextbs = TAILQ_NEXT(ni, ni_list);
+ setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
+ ieee80211_free_node(ic, ni);
+ }
+ for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+ if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
+ break;
+ if (i == IEEE80211_CHAN_MAX) {
+ fail = arc4random() & 3; /* random 0-3 */
+ for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+ if (isset(ic->ic_chan_active, i) && fail-- == 0)
+ break;
+ }
+ ieee80211_create_ibss(ic, &ic->ic_channels[i]);
+ return;
+ }
+ if (ni == NULL) {
+ IEEE80211_DPRINTF(("%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;
+ }
+ /*
+ * Reset the list of channels to scan and start again.
+ */
+ ieee80211_reset_scan(ifp);
+ ieee80211_next_scan(ifp);
+ return;
+ }
+ selbs = NULL;
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n");
+ for (; ni != NULL; ni = nextbs) {
+ ieee80211_ref_node(ni);
+ nextbs = TAILQ_NEXT(ni, ni_list);
+ if (ni->ni_fails) {
+ /*
+ * The configuration of the access points may change
+ * during my scan. So delete the entry for the AP
+ * and retry to associate if there is another beacon.
+ */
+ if (ni->ni_fails++ > 2)
+ ieee80211_free_node(ic, ni);
+ continue;
+ }
+ if (ieee80211_match_bss(ic, ni) == 0) {
+ if (selbs == NULL)
+ selbs = ni;
+ else if (ni->ni_rssi > selbs->ni_rssi) {
+ ieee80211_unref_node(&selbs);
+ selbs = ni;
+ } else
+ ieee80211_unref_node(&ni);
+ } else {
+ ieee80211_unref_node(&ni);
+ }
+ }
+ if (selbs == NULL)
+ goto notfound;
+ (*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ic->ic_bss->ni_rates.rs_nrates == 0) {
+ selbs->ni_fails++;
+ ieee80211_unref_node(&selbs);
+ goto notfound;
+ }
+ ieee80211_unref_node(&selbs);
+ /*
+ * Discard scan set; the nodes have a refcnt of zero
+ * and have not asked the driver to setup private
+ * node state. Let them be repopulated on demand either
+ * through transmission (ieee80211_find_txnode) or receipt
+ * of a probe response (to be added).
+ */
+ ieee80211_free_allnodes(ic);
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ } else {
+ ieee80211_unref_node(&selbs);
+ ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+ }
+}
+
+int
+ieee80211_get_rate(struct ieee80211com *ic)
+{
+ u_int8_t (*rates)[IEEE80211_RATE_MAXSIZE];
+ int rate;
+
+ rates = &ic->ic_bss->ni_rates.rs_rates;
+
+ if (ic->ic_fixed_rate != -1)
+ rate = (*rates)[ic->ic_fixed_rate];
+ else if (ic->ic_state == IEEE80211_S_RUN)
+ rate = (*rates)[ic->ic_bss->ni_txrate];
+ else
+ rate = 0;
+
+ return rate & IEEE80211_RATE_VAL;
+}
+
+static struct ieee80211_node *
+ieee80211_node_alloc(struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni;
+ MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node),
+ M_80211_NODE, M_NOWAIT);
+ memset(ni, 0, sizeof(struct ieee80211_node));
+ return ni;
+}
+
+static void
+ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_DEVBUF);
+ ni->ni_challenge = NULL;
+ }
+ FREE(ni, M_80211_NODE);
+}
+
+static void
+ieee80211_node_copy(struct ieee80211com *ic,
+ struct ieee80211_node *dst, const struct ieee80211_node *src)
+{
+ *dst = *src;
+ dst->ni_challenge = NULL;
+}
+
+static u_int8_t
+ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ return ni->ni_rssi;
+}
+
+static void
+ieee80211_setup_node(struct ieee80211com *ic,
+ struct ieee80211_node *ni, u_int8_t *macaddr)
+{
+ int hash;
+ ieee80211_node_critsec_decl(s);
+
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
+ hash = IEEE80211_NODE_HASH(macaddr);
+ ni->ni_refcnt = 1; /* mark referenced */
+ ieee80211_node_critsec_begin(ic, s);
+ TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
+ LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
+ /*
+ * Note we don't enable the inactive timer when acting
+ * as a station. Nodes created in this mode represent
+ * AP's identified while scanning. If we time them out
+ * then several things happen: we can't return the data
+ * to users to show the list of AP's we encountered, and
+ * more importantly, we'll incorrectly deauthenticate
+ * ourself because the inactivity timer will kick us off.
+ */
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ ic->ic_inact_timer = IEEE80211_INACT_WAIT;
+ ieee80211_node_critsec_end(ic, s);
+}
+
+struct ieee80211_node *
+ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+ struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+ if (ni != NULL)
+ ieee80211_setup_node(ic, ni, macaddr);
+ else
+ ic->ic_stats.is_rx_nodealloc++;
+ return ni;
+}
+
+struct ieee80211_node *
+ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+ struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+ if (ni != NULL) {
+ ieee80211_setup_node(ic, ni, macaddr);
+ /*
+ * Inherit from ic_bss.
+ */
+ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+ ni->ni_chan = ic->ic_bss->ni_chan;
+ } else
+ ic->ic_stats.is_rx_nodealloc++;
+ return ni;
+}
+
+static struct ieee80211_node *
+_ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+ struct ieee80211_node *ni;
+ int hash;
+
+#ifdef __FreeBSD__
+ IEEE80211_NODE_LOCK_ASSERT(ic);
+#endif /* __FreeBSD__ */
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+ ieee80211_node_incref(ni); /* mark referenced */
+ return ni;
+ }
+ }
+ return NULL;
+}
+
+struct ieee80211_node *
+ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+ struct ieee80211_node *ni;
+ ieee80211_node_critsec_decl(s);
+
+ ieee80211_node_critsec_begin(ic, s);
+ ni = _ieee80211_find_node(ic, macaddr);
+ ieee80211_node_critsec_end(ic, s);
+ return ni;
+}
+
+/*
+ * Return a reference to the appropriate node for sending
+ * a data frame. This handles node discovery in adhoc networks.
+ */
+struct ieee80211_node *
+ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+ struct ieee80211_node *ni;
+ ieee80211_node_critsec_decl(s);
+
+ /*
+ * The destination address should be in the node table
+ * unless we are operating in station mode or this is a
+ * multicast/broadcast frame.
+ */
+ if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
+ return ic->ic_bss;
+
+ /* XXX can't hold lock across dup_bss 'cuz of recursive locking */
+ ieee80211_node_critsec_begin(ic, s);
+ ni = _ieee80211_find_node(ic, macaddr);
+ ieee80211_node_critsec_end(ic, s);
+ if (ni == NULL &&
+ (ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_AHDEMO)) {
+ /*
+ * Fake up a node; this handles node discovery in
+ * adhoc mode. Note that for the driver's benefit
+ * we we treat this like an association so the driver
+ * has an opportunity to setup it's private state.
+ *
+ * XXX need better way to handle this; issue probe
+ * request so we can deduce rate set, etc.
+ */
+ ni = ieee80211_dup_bss(ic, macaddr);
+ if (ni != NULL) {
+ /* XXX no rate negotiation; just dup */
+ ni->ni_rates = ic->ic_bss->ni_rates;
+ if (ic->ic_newassoc)
+ (*ic->ic_newassoc)(ic, ni, 1);
+ }
+ }
+ return ni;
+}
+
+/*
+ * For some types of packet and for some operating modes, it is
+ * desirable to process a Rx packet using its sender's node-record
+ * instead of the BSS record, when that is possible.
+ *
+ * - AP mode: it is desirable to keep a node-record for every
+ * authenticated/associated station *in the BSS*. For future use,
+ * we also track neighboring APs, since they might belong to the
+ * same ESSID.
+ *
+ * - IBSS mode: it is desirable to keep a node-record for every
+ * station *in the BSS*.
+ *
+ * - monitor mode: it is desirable to keep a node-record for every
+ * sender, regardless of BSS.
+ *
+ * - STA mode: the only available node-record is the BSS record,
+ * ic->ic_bss.
+ *
+ * Of all the 802.11 Control packets, only the node-records for
+ * RTS packets node-record can be looked up.
+ *
+ * Return non-zero if the packet's node-record is kept, zero
+ * otherwise.
+ */
+static __inline int
+ieee80211_needs_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh,
+ u_int8_t **bssid)
+{
+ struct ieee80211_node *bss = ic->ic_bss;
+ int needsnode, rc = 0;
+
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ return 0;
+
+ needsnode = (ic->ic_opmode == IEEE80211_M_MONITOR);
+
+ *bssid = NULL;
+
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_CTL:
+ return (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_RTS;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ *bssid = wh->i_addr3;
+ rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid);
+ break;
+ case IEEE80211_FC0_TYPE_DATA:
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ *bssid = wh->i_addr3;
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_AHDEMO)
+ rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ *bssid = wh->i_addr1;
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid);
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ case IEEE80211_FC1_DIR_DSTODS:
+ *bssid = wh->i_addr2;
+ rc = (ic->ic_opmode == IEEE80211_M_HOSTAP);
+ break;
+ }
+ break;
+ }
+ return needsnode || rc;
+}
+
+struct ieee80211_node *
+ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh)
+{
+ struct ieee80211_node *ni;
+ const static u_int8_t zero[IEEE80211_ADDR_LEN];
+ u_int8_t *bssid;
+ ieee80211_node_critsec_decl(s);
+
+ if (!ieee80211_needs_rxnode(ic, wh, &bssid))
+ return ieee80211_ref_node(ic->ic_bss);
+
+ ieee80211_node_critsec_begin(ic, s);
+ ni = _ieee80211_find_node(ic, wh->i_addr2);
+ ieee80211_node_critsec_end(ic, s);
+
+ if (ni == NULL) {
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ if ((ni = ieee80211_dup_bss(ic, wh->i_addr2)) != NULL)
+ IEEE80211_ADDR_COPY(ni->ni_bssid,
+ (bssid != NULL) ? bssid : zero);
+
+ /* XXX see remarks in ieee80211_find_txnode */
+ if (ni != NULL) {
+ /* XXX no rate negotiation; just dup */
+ ni->ni_rates = ic->ic_bss->ni_rates;
+ if (ic->ic_newassoc)
+ (*ic->ic_newassoc)(ic, ni, 1);
+ }
+ IEEE80211_DPRINTF(("%s: faked-up node %p for %s\n",
+ __func__, ni, ether_sprintf(wh->i_addr2)));
+ }
+ ni = ieee80211_ref_node((ni == NULL) ? ic->ic_bss : ni);
+ }
+ IASSERT(ni != NULL, ("%s: null node", __func__));
+ return ni;
+}
+
+/*
+ * Like find but search based on the channel too.
+ */
+struct ieee80211_node *
+ieee80211_lookup_node(struct ieee80211com *ic,
+ u_int8_t *macaddr, struct ieee80211_channel *chan)
+{
+ struct ieee80211_node *ni;
+ int hash;
+ ieee80211_node_critsec_decl(s);
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ ieee80211_node_critsec_begin(ic, s);
+ LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+ ni->ni_chan == chan) {
+ ieee80211_node_incref(ni);/* mark referenced */
+ break;
+ }
+ }
+ ieee80211_node_critsec_end(ic, s);
+ return ni;
+}
+
+static void
+_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ IASSERT(ni != ic->ic_bss, ("freeing bss node"));
+
+ IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
+ LIST_REMOVE(ni, ni_hash);
+ if (!IF_IS_EMPTY(&ni->ni_savedq)) {
+ IF_PURGE(&ni->ni_savedq);
+ if (ic->ic_set_tim)
+ ic->ic_set_tim(ic, ni->ni_associd, 0);
+ }
+ if (TAILQ_EMPTY(&ic->ic_node))
+ ic->ic_inact_timer = 0;
+ (*ic->ic_node_free)(ic, ni);
+}
+
+void
+ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ ieee80211_node_critsec_decl(s);
+
+ IASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
+
+ if (ieee80211_node_decref(ni) == 0) {
+ ieee80211_node_critsec_begin(ic, s);
+ _ieee80211_free_node(ic, ni);
+ ieee80211_node_critsec_end(ic, s);
+ }
+}
+
+void
+ieee80211_free_allnodes(struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni;
+ ieee80211_node_critsec_decl(s);
+
+ ieee80211_node_critsec_begin(ic, s);
+ while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
+ _ieee80211_free_node(ic, ni);
+ ieee80211_node_critsec_end(ic, s);
+}
+
+/*
+ * Timeout inactive nodes. Note that we cannot hold the node
+ * lock while sending a frame as this would lead to a LOR.
+ * Instead we use a generation number to mark nodes that we've
+ * scanned and drop the lock and restart a scan if we have to
+ * time out a node. Since we are single-threaded by virtue of
+ * controlling the inactivity timer we can be sure this will
+ * process each node only once.
+ */
+void
+ieee80211_timeout_nodes(struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni;
+ ieee80211_node_critsec_decl(s);
+ u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/
+
+restart:
+ ieee80211_node_critsec_begin(ic, s);
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ if (ni->ni_scangen == gen) /* previously handled */
+ continue;
+ ni->ni_scangen = gen;
+ if (++ni->ni_inact > ieee80211_inact_max) {
+ IEEE80211_DPRINTF(("station %s timed out "
+ "due to inactivity (%u secs)\n",
+ ether_sprintf(ni->ni_macaddr),
+ ni->ni_inact));
+ /*
+ * Send a deauthenticate frame.
+ *
+ * Drop the node lock before sending the
+ * deauthentication frame in case the driver takes
+ * a lock, as this will result in a LOR between the
+ * node lock and the driver lock.
+ */
+ ieee80211_node_critsec_end(ic, s);
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_EXPIRE);
+ ieee80211_free_node(ic, ni);
+ ic->ic_stats.is_node_timeout++;
+ goto restart;
+ }
+ }
+ if (!TAILQ_EMPTY(&ic->ic_node))
+ ic->ic_inact_timer = IEEE80211_INACT_WAIT;
+ ieee80211_node_critsec_end(ic, s);
+}
+
+void
+ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg)
+{
+ struct ieee80211_node *ni;
+ ieee80211_node_critsec_decl(s);
+
+ ieee80211_node_critsec_begin(ic, s);
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+ (*f)(arg, ni);
+ ieee80211_node_critsec_end(ic, s);
+}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
new file mode 100644
index 00000000000..e1592c8303e
--- /dev/null
+++ b/sys/net80211/ieee80211_node.h
@@ -0,0 +1,206 @@
+/* $OpenBSD: ieee80211_node.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.10 2004/04/05 22:10:26 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_NODE_H_
+#define _NET80211_IEEE80211_NODE_H_
+
+#define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */
+#define IEEE80211_TRANS_WAIT 5 /* transition wait */
+#define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */
+#define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT)
+
+#define IEEE80211_NODE_HASHSIZE 32
+/* simple hash is enough for variation of macaddr */
+#define IEEE80211_NODE_HASH(addr) \
+ (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE)
+
+#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
+#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];
+};
+
+/*
+ * Node specific information. Note that drivers are expected
+ * to derive from this structure to add device-specific per-node
+ * state. This is done by overriding the ic_node_* methods in
+ * the ieee80211com structure.
+ */
+struct ieee80211_node {
+ TAILQ_ENTRY(ieee80211_node) ni_list;
+ LIST_ENTRY(ieee80211_node) ni_hash;
+ u_int ni_refcnt;
+ u_int ni_scangen; /* gen# for timeout scan */
+
+ /* hardware */
+ u_int32_t ni_rstamp; /* recv timestamp */
+ u_int8_t ni_rssi; /* recv ssi */
+
+ /* header */
+ u_int8_t ni_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t ni_bssid[IEEE80211_ADDR_LEN];
+
+ /* beacon, probe response */
+ u_int8_t ni_tstamp[8]; /* 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];
+ struct ieee80211_rateset ni_rates; /* negotiated rate set */
+ u_int8_t *ni_country; /* country information XXX */
+ struct ieee80211_channel *ni_chan;
+ u_int16_t ni_fhdwell; /* FH only */
+ u_int8_t ni_fhindex; /* FH only */
+ u_int8_t ni_erp; /* 11g only */
+
+#ifdef notyet
+ /* DTIM and contention free period (CFP) */
+ u_int8_t ni_dtimperiod;
+ u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */
+ u_int16_t ni_cfpduremain; /* remaining cfp duration */
+ u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */
+ u_int16_t ni_nextdtim; /* time to next DTIM */
+ u_int16_t ni_timoffset;
+#endif
+
+ /* power saving mode */
+
+ u_int8_t ni_pwrsave;
+ struct ifqueue ni_savedq; /* packets queued for pspoll */
+
+ /* others */
+ u_int16_t ni_associd; /* assoc response */
+ u_int16_t ni_txseq; /* seq to be transmitted */
+ u_int16_t ni_rxseq; /* seq previous received */
+ int ni_fails; /* failure count to associate */
+ int ni_inact; /* inactivity mark count */
+ int ni_txrate; /* index to ni_rates[] */
+ u_int32_t *ni_challenge; /* shared-key challenge */
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define ieee80211_node_incref(ni) \
+ do { \
+ int _s = splnet(); \
+ (ni)->ni_refcnt++; \
+ splx(_s); \
+ } while (0)
+
+static __inline int
+ieee80211_node_decref(struct ieee80211_node *ni)
+{
+ int refcnt, s;
+ s = splnet();
+ refcnt = --ni->ni_refcnt;
+ splx(s);
+ return refcnt;
+}
+
+#else
+#define ieee80211_node_incref(ni) atomic_add_int(&(ni)->ni_refcnt, 1)
+static __inline int
+ieee80211_node_decref(struct ieee80211_node *ni)
+{
+ int orefcnt;
+ do {
+ orefcnt = ni->ni_refcnt;
+ } while (atomic_cmpset_int(&ni->ni_refcnt, orefcnt, orefcnt - 1) == 0);
+ return orefcnt - 1;
+}
+#endif
+
+static __inline struct ieee80211_node *
+ieee80211_ref_node(struct ieee80211_node *ni)
+{
+ ieee80211_node_incref(ni);
+ return ni;
+}
+
+static __inline void
+ieee80211_unref_node(struct ieee80211_node **ni)
+{
+ ieee80211_node_decref(*ni);
+ *ni = NULL; /* guard against use */
+}
+
+#define IEEE80211_NODE_LOCK_INIT(_ic, _name) \
+ mtx_init(&(_ic)->ic_nodelock, _name, "802.11 node table", MTX_DEF)
+#define IEEE80211_NODE_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_nodelock)
+#define IEEE80211_NODE_LOCK(_ic) mtx_lock(&(_ic)->ic_nodelock)
+#define IEEE80211_NODE_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_nodelock)
+#define IEEE80211_NODE_LOCK_ASSERT(_ic) \
+ mtx_assert(&(_ic)->ic_nodelock, MA_OWNED)
+
+struct ieee80211com;
+
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_80211_NODE);
+#endif
+
+extern void ieee80211_node_attach(struct ifnet *);
+extern void ieee80211_node_lateattach(struct ifnet *);
+extern void ieee80211_node_detach(struct ifnet *);
+
+extern void ieee80211_begin_scan(struct ifnet *);
+extern void ieee80211_next_scan(struct ifnet *);
+extern void ieee80211_end_scan(struct ifnet *);
+extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *,
+ u_int8_t *);
+extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *,
+ u_int8_t *);
+extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *,
+ u_int8_t *);
+extern struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *,
+ struct ieee80211_frame *);
+extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
+ u_int8_t *);
+extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *,
+ u_int8_t *macaddr, struct ieee80211_channel *);
+extern void ieee80211_free_node(struct ieee80211com *,
+ struct ieee80211_node *);
+extern void ieee80211_free_allnodes(struct ieee80211com *);
+typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
+extern void ieee80211_iterate_nodes(struct ieee80211com *ic,
+ ieee80211_iter_func *, void *);
+extern void ieee80211_timeout_nodes(struct ieee80211com *);
+
+extern int ieee80211_match_bss(struct ieee80211com *,
+ struct ieee80211_node *);
+extern void ieee80211_create_ibss(struct ieee80211com* ,
+ struct ieee80211_channel *);
+#endif /* _NET80211_IEEE80211_NODE_H_ */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
new file mode 100644
index 00000000000..7a236d349c5
--- /dev/null
+++ b/sys/net80211/ieee80211_output.c
@@ -0,0 +1,671 @@
+/* $OpenBSD: ieee80211_output.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.10 2004/04/02 23:25:39 sam Exp $");
+#elif defined(__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $");
+#endif
+
+#ifdef __NetBSD__
+#include "opt_inet.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#ifdef __FreeBSD__
+#include <machine/atomic.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+#include <net/if_llc.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+
+/*
+ * Send a management frame to the specified node. The node pointer
+ * must have a reference as the pointer will be passed to the driver
+ * and potentially held for a long time. If the frame is successfully
+ * dispatched to the driver, then it is responsible for freeing the
+ * reference (and potentially free'ing up any associated storage).
+ */
+static int
+ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
+ struct mbuf *m, int type)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_frame *wh;
+
+ IASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = 0;
+
+ /*
+ * Yech, hack alert! We want to pass the node down to the
+ * driver's start routine. If we don't do so then the start
+ * routine must immediately look it up again and that can
+ * cause a lock order reversal if, for example, this frame
+ * is being sent because the station is being timedout and
+ * the frame being sent is a DEAUTH message. We could stick
+ * this in an m_tag and tack that on to the mbuf. However
+ * that's rather expensive to do for every frame so instead
+ * we stuff it in the rcvif field since outbound frames do
+ * not (presently) use this.
+ */
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL)
+ return ENOMEM;
+#ifdef __FreeBSD__
+ KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
+#endif
+ m->m_pkthdr.rcvif = (void *)ni;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)&wh->i_dur[0] = 0;
+ *(u_int16_t *)&wh->i_seq[0] =
+ htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseq++;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+
+ if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
+ m->m_flags &= ~M_LINK0;
+ IEEE80211_DPRINTF(("%s: encrypting frame for %s\n", __func__,
+ ether_sprintf(wh->i_addr1)));
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ }
+
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* avoid to print too many frames */
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+#ifdef IEEE80211_DEBUG
+ ieee80211_debug > 1 ||
+#endif
+ (type & IEEE80211_FC0_SUBTYPE_MASK) !=
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ if_printf(ifp, "sending %s to %s on channel %u\n",
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK)
+ >> IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(ni->ni_macaddr),
+ ieee80211_chan2ieee(ic, ni->ni_chan));
+ }
+
+ IF_ENQUEUE(&ic->ic_mgtq, m);
+ ifp->if_timer = 1;
+ (*ifp->if_start)(ifp);
+ return 0;
+}
+
+/*
+ * Encapsulate an outbound data frame. The mbuf chain is updated and
+ * a reference to the destination node is returned. If an error is
+ * encountered NULL is returned and the node reference will also be NULL.
+ *
+ * NB: The caller is responsible for free'ing a returned node reference.
+ * The convention is ic_bss is not reference counted; the caller must
+ * maintain that.
+ */
+struct mbuf *
+ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ether_header eh;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni = NULL;
+ struct llc *llc;
+
+ if (m->m_len < sizeof(struct ether_header)) {
+ m = m_pullup(m, sizeof(struct ether_header));
+ if (m == NULL) {
+ ic->ic_stats.is_tx_nombuf++;
+ goto bad;
+ }
+ }
+ memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+
+ ni = ieee80211_find_txnode(ic, eh.ether_dhost);
+ if (ni == NULL) {
+ IEEE80211_DPRINTF(("%s: no node for dst %s, discard frame\n",
+ __func__, ether_sprintf(eh.ether_dhost)));
+ ic->ic_stats.is_tx_nonode++;
+ goto bad;
+ }
+ ni->ni_inact = 0;
+
+ 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;
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL) {
+ ic->ic_stats.is_tx_nombuf++;
+ goto bad;
+ }
+ 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] = 0;
+ *(u_int16_t *)&wh->i_seq[0] =
+ htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseq++;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+ break;
+ case IEEE80211_M_HOSTAP:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
+ break;
+ case IEEE80211_M_MONITOR:
+ goto bad;
+ }
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ *pni = ni;
+ return m;
+bad:
+ if (m != NULL)
+ m_freem(m);
+ if (ni && ni != ic->ic_bss)
+ ieee80211_free_node(ic, ni);
+ *pni = NULL;
+ return NULL;
+}
+
+/*
+ * Add a supported rates element id to a frame.
+ */
+u_int8_t *
+ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+{
+ int nrates;
+
+ *frm++ = IEEE80211_ELEMID_RATES;
+ nrates = rs->rs_nrates;
+ if (nrates > IEEE80211_RATE_SIZE)
+ nrates = IEEE80211_RATE_SIZE;
+ *frm++ = nrates;
+ memcpy(frm, rs->rs_rates, nrates);
+ return frm + nrates;
+}
+
+/*
+ * Add an extended supported rates element id to a frame.
+ */
+u_int8_t *
+ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
+{
+ /*
+ * Add an extended supported rates element if operating in 11g mode.
+ */
+ if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
+ int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE;
+ *frm++ = IEEE80211_ELEMID_XRATES;
+ *frm++ = nrates;
+ memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates);
+ frm += nrates;
+ }
+ return frm;
+}
+
+/*
+ * 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)
+{
+ *frm++ = IEEE80211_ELEMID_SSID;
+ *frm++ = len;
+ memcpy(frm, ssid, len);
+ return frm + len;
+}
+
+static struct mbuf *
+ieee80211_getmbuf(int flags, int type, u_int pktlen)
+{
+ struct mbuf *m;
+
+ IASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen));
+#ifdef __FreeBSD__
+ if (pktlen <= MHLEN)
+ MGETHDR(m, flags, type);
+ else
+ m = m_getcl(flags, type, M_PKTHDR);
+#else
+ MGETHDR(m, flags, type);
+ if (m != NULL && pktlen > MHLEN)
+ MCLGET(m, flags);
+#endif
+ return m;
+}
+
+/*
+ * Send a management frame. The node is for the destination (or ic_bss
+ * when in station mode). Nodes other than ic_bss have their reference
+ * count bumped to reflect our use for an indeterminant time.
+ */
+int
+ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int arg)
+{
+#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+ struct ifnet *ifp = &ic->ic_if;
+ struct mbuf *m;
+ u_int8_t *frm;
+ enum ieee80211_phymode mode;
+ u_int16_t capinfo;
+ int has_challenge, is_shared_key, ret, timer;
+
+ IASSERT(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.
+ */
+ if (ni != ic->ic_bss)
+ ieee80211_ref_node(ni);
+ timer = 0;
+ switch (type) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ 2 + ic->ic_des_esslen
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+ frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
+ mode = ieee80211_chan2mode(ic, ni->ni_chan);
+ frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
+ frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ timer = IEEE80211_TRANS_WAIT;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ /*
+ * probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (FH/DS)
+ * [tlv] parameter set (IBSS)
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ 8 + 2 + 2 + 2
+ + 2 + ni->ni_esslen
+ + 2 + IEEE80211_RATE_SIZE
+ + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3)
+ + 6
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ memset(frm, 0, 8); /* timestamp should be filled later */
+ frm += 8;
+ *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
+ frm += 2;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo = IEEE80211_CAPINFO_IBSS;
+ else
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ *(u_int16_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, &ic->ic_bss->ni_rates);
+
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ *frm++ = IEEE80211_ELEMID_FHPARMS;
+ *frm++ = 5;
+ *frm++ = ni->ni_fhdwell & 0x00ff;
+ *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
+ *frm++ = IEEE80211_FH_CHANSET(
+ ieee80211_chan2ieee(ic, ni->ni_chan));
+ *frm++ = IEEE80211_FH_CHANPAT(
+ ieee80211_chan2ieee(ic, ni->ni_chan));
+ *frm++ = ni->ni_fhindex;
+ } else {
+ *frm++ = IEEE80211_ELEMID_DSPARMS;
+ *frm++ = 1;
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ } else { /* IEEE80211_M_HOSTAP */
+ /* TODO: TIM */
+ *frm++ = IEEE80211_ELEMID_TIM;
+ *frm++ = 4; /* length */
+ *frm++ = 0; /* DTIM count */
+ *frm++ = 1; /* DTIM period */
+ *frm++ = 0; /* bitmap control */
+ *frm++ = 0; /* Partial Virtual Bitmap (variable length) */
+ }
+ frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+
+ has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
+ arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
+ ni->ni_challenge != NULL);
+
+ is_shared_key = has_challenge || (ni->ni_challenge != NULL &&
+ arg == IEEE80211_AUTH_SHARED_PASS);
+
+ if (has_challenge) {
+ MH_ALIGN(m, 2 * 3 + 2 + IEEE80211_CHALLENGE_LEN);
+ m->m_pkthdr.len = m->m_len =
+ 2 * 3 + 2 + IEEE80211_CHALLENGE_LEN;
+ } else {
+ MH_ALIGN(m, 2 * 3);
+ m->m_pkthdr.len = m->m_len = 2 * 3;
+ }
+ frm = mtod(m, u_int8_t *);
+ ((u_int16_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] = 0; /* status */
+
+ if (has_challenge) {
+ ((u_int16_t *)frm)[3] =
+ htole16((IEEE80211_CHALLENGE_LEN << 8) |
+ IEEE80211_ELEMID_CHALLENGE);
+ memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+ IEEE80211_CHALLENGE_LEN);
+ if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
+ IEEE80211_DPRINTF((
+ "%s: request encrypt frame\n", __func__));
+ m->m_flags |= M_LINK0; /* WEP-encrypt, please */
+ }
+ }
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ timer = IEEE80211_TRANS_WAIT;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s deauthenticate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), arg);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ MH_ALIGN(m, 2);
+ m->m_pkthdr.len = m->m_len = 2;
+ *mtod(m, u_int16_t *) = htole16(arg); /* reason */
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ sizeof(capinfo)
+ + sizeof(u_int16_t)
+ + IEEE80211_ADDR_LEN
+ + 2 + ni->ni_esslen
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ capinfo = 0;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo |= IEEE80211_CAPINFO_IBSS;
+ else /* IEEE80211_M_STA */
+ capinfo |= IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ /*
+ * NB: Some 11a AP's reject the request when
+ * short premable is set.
+ */
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ *(u_int16_t *)frm = htole16(ic->ic_lintval);
+ frm += 2;
+
+ if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
+ frm += IEEE80211_ADDR_LEN;
+ }
+
+ 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);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ timer = IEEE80211_TRANS_WAIT;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+ sizeof(capinfo)
+ + sizeof(u_int16_t)
+ + sizeof(u_int16_t)
+ + 2 + IEEE80211_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ *(u_int16_t *)frm = htole16(arg); /* status */
+ frm += 2;
+
+ if (arg == IEEE80211_STATUS_SUCCESS)
+ *(u_int16_t *)frm = htole16(ni->ni_associd);
+ frm += 2;
+
+ frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s disassociate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), arg);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ MH_ALIGN(m, 2);
+ m->m_pkthdr.len = m->m_len = 2;
+ *mtod(m, u_int16_t *) = htole16(arg); /* reason */
+ break;
+
+ default:
+ IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
+ __func__, type));
+ senderr(EINVAL, is_tx_unknownmgt);
+ /* NOTREACHED */
+ }
+
+ ret = ieee80211_mgmt_output(ifp, ni, m, type);
+ if (ret == 0) {
+ if (timer)
+ ic->ic_mgt_timer = timer;
+ } else {
+bad:
+ if (ni != ic->ic_bss) /* remove ref we added */
+ ieee80211_free_node(ic, ni);
+ }
+ return ret;
+#undef senderr
+}
+
+void
+ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni,
+ struct mbuf *m)
+{
+ /* Store the new packet on our queue, changing the TIM if necessary */
+
+ if (IF_IS_EMPTY(&ni->ni_savedq)) {
+ ic->ic_set_tim(ic, ni->ni_associd, 1);
+ }
+ if (ni->ni_savedq.ifq_len >= IEEE80211_PS_MAX_QUEUE) {
+ IF_DROP(&ni->ni_savedq);
+ m_freem(m);
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: station %s power save queue overflow"
+ " of size %d drops %d\n",
+ ic->ic_if.if_xname,
+ ether_sprintf(ni->ni_macaddr),
+ IEEE80211_PS_MAX_QUEUE,
+ ni->ni_savedq.ifq_drops);
+ } else {
+ /* Similar to ieee80211_mgmt_output, store the node in
+ * the rcvif field.
+ */
+ IF_ENQUEUE(&ni->ni_savedq, m);
+ m->m_pkthdr.rcvif = (void *)ni;
+ }
+}
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
new file mode 100644
index 00000000000..faaee044985
--- /dev/null
+++ b/sys/net80211/ieee80211_proto.c
@@ -0,0 +1,566 @@
+/* $OpenBSD: ieee80211_proto.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__FreeBSD__)
+__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.8 2004/04/02 20:22:25
+ sam Exp $");
+#elif defined(__NetBSD__)
+__KERNEL_RCSID(0, "$NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $");
+#endif
+
+/*
+ * IEEE 802.11 protocol support.
+ */
+
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#ifdef __FreeBSD__
+#include <machine/atomic.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#elif defined(__NetBSD__)
+#include <net/if_ether.h>
+#endif
+#include <net/if_llc.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+const char *ieee80211_mgt_subtype_name[] = {
+ "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
+ "probe_req", "probe_resp", "reserved#6", "reserved#7",
+ "beacon", "atim", "disassoc", "auth",
+ "deauth", "reserved#13", "reserved#14", "reserved#15"
+};
+const char *ieee80211_state_name[IEEE80211_S_MAX] = {
+ "INIT", /* IEEE80211_S_INIT */
+ "SCAN", /* IEEE80211_S_SCAN */
+ "AUTH", /* IEEE80211_S_AUTH */
+ "ASSOC", /* IEEE80211_S_ASSOC */
+ "RUN" /* IEEE80211_S_RUN */
+};
+
+static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
+
+void
+ieee80211_proto_attach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ ifp->if_hdrlen = sizeof(struct ieee80211_frame);
+
+#ifdef notdef
+ ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
+#else
+ ic->ic_rtsthreshold = IEEE80211_RTS_MAX;
+#endif
+ ic->ic_fragthreshold = 2346; /* XXX not used yet */
+ ic->ic_fixed_rate = -1; /* no fixed rate */
+ ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+
+#ifdef __FreeBSD__
+ mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF);
+#endif
+
+ /* protocol state change handler */
+ ic->ic_newstate = ieee80211_newstate;
+
+ /* initialize management frame handlers */
+ ic->ic_recv_mgmt = ieee80211_recv_mgmt;
+ ic->ic_send_mgmt = ieee80211_send_mgmt;
+}
+
+void
+ieee80211_proto_detach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+#ifdef __FreeBSD__
+ IF_DRAIN(&ic->ic_mgtq);
+ mtx_destroy(&ic->ic_mgtq.ifq_mtx);
+#else
+ IF_PURGE(&ic->ic_mgtq);
+ IF_PURGE(&ic->ic_pwrsaveq);
+#endif
+}
+
+void
+ieee80211_print_essid(u_int8_t *essid, int len)
+{
+ int i;
+ u_int8_t *p;
+
+ if (len > IEEE80211_NWID_LEN)
+ len = IEEE80211_NWID_LEN;
+ /* determine printable or not */
+ for (i = 0, p = essid; i < len; i++, p++) {
+ if (*p < ' ' || *p > 0x7e)
+ break;
+ }
+ if (i == len) {
+ printf("\"");
+ for (i = 0, p = essid; i < len; i++, p++)
+ printf("%c", *p);
+ printf("\"");
+ } else {
+ printf("0x");
+ for (i = 0, p = essid; i < len; i++, p++)
+ printf("%02x", *p);
+ }
+}
+
+void
+ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
+{
+ struct ieee80211_frame *wh;
+ int i;
+
+ wh = (struct ieee80211_frame *)buf;
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ printf("NODS %s", ether_sprintf(wh->i_addr2));
+ printf("->%s", ether_sprintf(wh->i_addr1));
+ printf("(%s)", ether_sprintf(wh->i_addr3));
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ printf("TODS %s", ether_sprintf(wh->i_addr2));
+ printf("->%s", ether_sprintf(wh->i_addr3));
+ printf("(%s)", ether_sprintf(wh->i_addr1));
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ printf("FRDS %s", ether_sprintf(wh->i_addr3));
+ printf("->%s", ether_sprintf(wh->i_addr1));
+ printf("(%s)", ether_sprintf(wh->i_addr2));
+ break;
+ case IEEE80211_FC1_DIR_DSTODS:
+ printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
+ printf("->%s", ether_sprintf(wh->i_addr3));
+ printf("(%s", ether_sprintf(wh->i_addr2));
+ printf("->%s)", ether_sprintf(wh->i_addr1));
+ break;
+ }
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_DATA:
+ printf(" data");
+ break;
+ case IEEE80211_FC0_TYPE_MGT:
+ printf(" %s", ieee80211_mgt_subtype_name[
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
+ >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ break;
+ default:
+ printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
+ break;
+ }
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+ printf(" WEP");
+ if (rate >= 0)
+ printf(" %dM", rate / 2);
+ if (rssi >= 0)
+ printf(" +%d", rssi);
+ printf("\n");
+ if (len > 0) {
+ for (i = 0; i < len; i++) {
+ if ((i & 1) == 0)
+ printf(" ");
+ printf("%02x", buf[i]);
+ }
+ printf("\n");
+ }
+}
+
+int
+ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
+{
+#define RV(v) ((v) & IEEE80211_RATE_VAL)
+ int i, j, ignore, error;
+ int okrate, badrate;
+ struct ieee80211_rateset *srs, *nrs;
+ u_int8_t r;
+
+ error = 0;
+ okrate = badrate = 0;
+ srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
+ nrs = &ni->ni_rates;
+ for (i = 0; i < nrs->rs_nrates; ) {
+ ignore = 0;
+ if (flags & IEEE80211_F_DOSORT) {
+ /*
+ * Sort rates.
+ */
+ for (j = i + 1; j < nrs->rs_nrates; j++) {
+ if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) {
+ r = nrs->rs_rates[i];
+ nrs->rs_rates[i] = nrs->rs_rates[j];
+ nrs->rs_rates[j] = r;
+ }
+ }
+ }
+ r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
+ badrate = r;
+ if (flags & IEEE80211_F_DOFRATE) {
+ /*
+ * Apply fixed rate constraint. Note that we do
+ * not apply the constraint to basic rates as
+ * otherwise we may not be able to associate if
+ * the rate set we submit to the AP is invalid
+ * (e.g. fix rate at 36Mb/s which is not a basic
+ * rate for 11a operation).
+ */
+ if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
+ ic->ic_fixed_rate >= 0 &&
+ r != RV(srs->rs_rates[ic->ic_fixed_rate]))
+ ignore++;
+ }
+ if (flags & IEEE80211_F_DONEGO) {
+ /*
+ * Check against supported rates.
+ */
+ for (j = 0; j < srs->rs_nrates; j++) {
+ if (r == RV(srs->rs_rates[j])) {
+ /*
+ * Overwrite with the supported rate
+ * value so any basic rate bit is set.
+ * This insures that response we send
+ * to stations have the necessary basic
+ * rate bit set.
+ */
+ nrs->rs_rates[i] = srs->rs_rates[j];
+ break;
+ }
+ }
+ if (j == srs->rs_nrates) {
+ /*
+ * A rate in the node's rate set is not
+ * supported. If this is a basic rate and we
+ * are operating as an AP then this is an error.
+ * Otherwise we just discard/ignore the rate.
+ * Note that this is important for 11b stations
+ * when they want to associate with an 11g AP.
+ */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ (nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
+ error++;
+ ignore++;
+ }
+ }
+ if (flags & IEEE80211_F_DODEL) {
+ /*
+ * Delete unacceptable rates.
+ */
+ if (ignore) {
+ nrs->rs_nrates--;
+ for (j = i; j < nrs->rs_nrates; j++)
+ nrs->rs_rates[j] = nrs->rs_rates[j + 1];
+ nrs->rs_rates[j] = 0;
+ continue;
+ }
+ }
+ if (!ignore)
+ okrate = nrs->rs_rates[i];
+ i++;
+ }
+ if (okrate == 0 || error != 0)
+ return badrate | IEEE80211_RATE_BASIC;
+ else
+ return RV(okrate);
+#undef RV
+}
+
+static int
+ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+ ieee80211_node_critsec_decl(s);
+
+ ostate = ic->ic_state;
+ IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
+ ic->ic_state = nstate; /* state transition */
+ ni = ic->ic_bss; /* NB: no reference held */
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ break;
+ case IEEE80211_S_RUN:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ break;
+ case IEEE80211_M_HOSTAP:
+ ieee80211_node_critsec_begin(ic, s);
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ if (ni->ni_associd == 0)
+ continue;
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ }
+ ieee80211_node_critsec_end(ic, s);
+ break;
+ default:
+ break;
+ }
+ /* FALLTHRU */
+ case IEEE80211_S_ASSOC:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ break;
+ case IEEE80211_M_HOSTAP:
+ ieee80211_node_critsec_begin(ic, s);
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ }
+ ieee80211_node_critsec_end(ic, s);
+ break;
+ default:
+ break;
+ }
+ /* FALLTHRU */
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_SCAN:
+ ic->ic_mgt_timer = 0;
+#ifdef __FreeBSD__
+ IF_DRAIN(&ic->ic_mgtq);
+#else
+ IF_PURGE(&ic->ic_mgtq);
+ IF_PURGE(&ic->ic_pwrsaveq);
+#endif
+ if (ic->ic_wep_ctx != NULL) {
+ free(ic->ic_wep_ctx, M_DEVBUF);
+ ic->ic_wep_ctx = NULL;
+ }
+ ieee80211_free_allnodes(ic);
+ break;
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ ic->ic_flags &= ~IEEE80211_F_SIBSS;
+ /* initialize bss for probe request */
+#if defined(__OpenBSD__)
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr);
+#else
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
+#endif
+ ni->ni_rates = ic->ic_sup_rates[
+ ieee80211_chan2mode(ic, ni->ni_chan)];
+ ni->ni_associd = 0;
+ ni->ni_rstamp = 0;
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ /*
+ * AP operation and we already have a channel;
+ * bypass the scan and startup immediately.
+ */
+ ieee80211_create_ibss(ic, ic->ic_des_chan);
+ } else {
+ ieee80211_begin_scan(ifp);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ /* scan next */
+ if (ic->ic_flags & IEEE80211_F_ASCAN) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+ }
+ break;
+ case IEEE80211_S_RUN:
+ /* beacon miss */
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* XXX bssid clobbered above */
+ if_printf(ifp, "no recent beacons from %s;"
+ " rescanning\n",
+ ether_sprintf(ic->ic_bss->ni_bssid));
+ }
+ ieee80211_free_allnodes(ic);
+ /* FALLTHRU */
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ /* timeout restart scan */
+ ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
+ if (ni != NULL) {
+ ni->ni_fails++;
+ ieee80211_unref_node(&ni);
+ }
+ ieee80211_begin_scan(ifp);
+ break;
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ IEEE80211_DPRINTF(("%s: invalid transition\n",
+ __func__));
+ break;
+ case IEEE80211_S_SCAN:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (mgt) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ /* ??? */
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ /* ignore and retry scan on timeout */
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (mgt) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ ic->ic_state = ostate; /* stay RUN */
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ /* try to reauth */
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ }
+ break;
+ }
+ break;
+ case IEEE80211_S_ASSOC:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_ASSOC:
+ IEEE80211_DPRINTF(("%s: invalid transition\n",
+ __func__));
+ break;
+ case IEEE80211_S_AUTH:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_RUN:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_RUN:
+ IEEE80211_DPRINTF(("%s: invalid transition\n",
+ __func__));
+ break;
+ case IEEE80211_S_SCAN: /* adhoc/hostap mode */
+ case IEEE80211_S_ASSOC: /* infra mode */
+ IASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
+ ("%s: bogus xmit rate %u setup\n", __func__,
+ ni->ni_txrate));
+ if (ifp->if_flags & IFF_DEBUG) {
+ if_printf(ifp, " ");
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ printf("associated ");
+ else
+ printf("synchronized ");
+ printf("with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(ic->ic_bss->ni_essid,
+ ni->ni_esslen);
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ni->ni_chan),
+ IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
+ }
+ ic->ic_mgt_timer = 0;
+ (*ifp->if_start)(ifp);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
new file mode 100644
index 00000000000..22f88d2994e
--- /dev/null
+++ b/sys/net80211/ieee80211_proto.h
@@ -0,0 +1,83 @@
+/* $OpenBSD: ieee80211_proto.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_PROTO_H_
+#define _NET80211_IEEE80211_PROTO_H_
+
+/*
+ * 802.11 protocol implementation definitions.
+ */
+
+enum ieee80211_state {
+ IEEE80211_S_INIT = 0, /* default state */
+ IEEE80211_S_SCAN = 1, /* scanning */
+ IEEE80211_S_AUTH = 2, /* try to authenticate */
+ IEEE80211_S_ASSOC = 3, /* try to assoc */
+ IEEE80211_S_RUN = 4, /* associated */
+};
+#define IEEE80211_S_MAX (IEEE80211_S_RUN+1)
+
+#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
+ ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
+
+extern const char *ieee80211_mgt_subtype_name[];
+
+extern void ieee80211_proto_attach(struct ifnet *);
+extern void ieee80211_proto_detach(struct ifnet *);
+
+struct ieee80211_node;
+extern void ieee80211_input(struct ifnet *, struct mbuf *,
+ struct ieee80211_node *, int, u_int32_t);
+extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *, int, int, u_int32_t);
+extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
+ int, int);
+extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
+ struct ieee80211_node **);
+extern void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *,
+ struct mbuf *);
+extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);
+extern u_int8_t *ieee80211_add_rates(u_int8_t *frm,
+ const struct ieee80211_rateset *);
+#define ieee80211_new_state(_ic, _nstate, _arg) \
+ (((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
+extern u_int8_t *ieee80211_add_xrates(u_int8_t *frm,
+ const struct ieee80211_rateset *);
+extern void ieee80211_print_essid(u_int8_t *, int);
+extern void ieee80211_dump_pkt(u_int8_t *, int, int, int);
+
+extern const char *ieee80211_state_name[IEEE80211_S_MAX];
+#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_radiotap.h b/sys/net80211/ieee80211_radiotap.h
new file mode 100644
index 00000000000..6e12236cafa
--- /dev/null
+++ b/sys/net80211/ieee80211_radiotap.h
@@ -0,0 +1,213 @@
+/* $OpenBSD: ieee80211_radiotap.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.3 2004/04/05 22:13:21 sam Exp $ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.9 2004/06/06 04:13:28 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
+ * YOUNG 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.
+ */
+#ifndef _NET_IF_IEEE80211RADIOTAP_H_
+#define _NET_IF_IEEE80211RADIOTAP_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.
+ *
+ * I suggest the following 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
+ * should expect in the following, and for that reason I tell the
+ * units and origin of each measurement (where it applies), or else I
+ * use sufficiently weaselly language ("is a monotonically nondecreasing
+ * function of...") that I cannot set false expectations for lawyerly
+ * readers.
+ */
+#ifdef _KERNEL
+#ifndef DLT_IEEE802_11_RADIO
+#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */
+#endif
+#endif /* _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
+
+/* The radio capture header precedes the 802.11 header. */
+struct ieee80211_radiotap_header {
+ u_int8_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
+ * header in bytes, including
+ * it_version, it_pad,
+ * it_len, and data fields.
+ */
+ u_int32_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__));
+
+/* Name Data type Units
+ * ---- --------- -----
+ *
+ * IEEE80211_RADIOTAP_TSFT u_int64_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
+ *
+ * Tx/Rx frequency in MHz, followed by flags (see below).
+ *
+ * IEEE80211_RADIOTAP_FHSS u_int16_t see below
+ *
+ * For frequency-hopping radios, the hop set (first byte)
+ * and pattern (second byte).
+ *
+ * IEEE80211_RADIOTAP_RATE u_int8_t 500kb/s
+ *
+ * Tx/Rx data rate
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from
+ * one milliwatt (dBm)
+ *
+ * RF signal power at the antenna, decibel difference from
+ * one milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from
+ * one milliwatt (dBm)
+ *
+ * RF noise power at the antenna, decibel difference from one
+ * milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL u_int8_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)
+ *
+ * RF noise power at the antenna, decibel difference from an
+ * arbitrary, fixed reference point.
+ *
+ * IEEE80211_RADIOTAP_BARKER_CODE_LOCK u_int16_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
+ *
+ * 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)
+ *
+ * Transmit power expressed as decibel distance from max power
+ * set at factory calibration. 0 is max power. Monotonically
+ * nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from
+ * one milliwatt (dBm)
+ *
+ * Transmit power expressed as dBm (decibels from a 1 milliwatt
+ * reference). This is the absolute power level measured at
+ * the antenna port.
+ *
+ * IEEE80211_RADIOTAP_FLAGS u_int8_t bitmap
+ *
+ * Properties of transmitted and received frames. See flags
+ * defined below.
+ *
+ * IEEE80211_RADIOTAP_ANTENNA u_int8_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.
+ */
+enum ieee80211_radiotap_type {
+ IEEE80211_RADIOTAP_TSFT = 0,
+ IEEE80211_RADIOTAP_FLAGS = 1,
+ IEEE80211_RADIOTAP_RATE = 2,
+ IEEE80211_RADIOTAP_CHANNEL = 3,
+ IEEE80211_RADIOTAP_FHSS = 4,
+ IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+ IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+ IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+ IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+ IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+ IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+ IEEE80211_RADIOTAP_ANTENNA = 11,
+ IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+ IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+ IEEE80211_RADIOTAP_FCS = 14,
+ 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) */
+#endif /* !_KERNEL */
+
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received
+ * during CFP
+ */
+#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received
+ * with short
+ * preamble
+ */
+#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received
+ * with WEP encryption
+ */
+#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received
+ * with fragmentation
+ */
+
+#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */
diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c
new file mode 100644
index 00000000000..6d99cc28fd2
--- /dev/null
+++ b/sys/net80211/ieee80211_rssadapt.c
@@ -0,0 +1,409 @@
+/* $OpenBSD: ieee80211_rssadapt.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_rssadapt.c,v 1.7 2004/05/25 04:33:59 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``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 David
+ * Young 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/param.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#ifdef __FreeBSD__
+#include <net/ethernet.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_compat.h>
+#include <net80211/ieee80211_rssadapt.h>
+
+#ifdef interpolate
+#undef interpolate
+#endif
+#define interpolate(parm, old, new) ((parm##_old * (old) + \
+ (parm##_denom - parm##_old) * (new)) / \
+ parm##_denom)
+
+#ifdef IEEE80211_DEBUG
+static struct timeval lastrateadapt; /* time of last rate adaptation msg */
+static int currssadaptps = 0; /* rate-adaptation msgs this second */
+static int ieee80211_adaptrate = 4; /* rate-adaptation max msgs/sec */
+
+#define RSSADAPT_DO_PRINT() \
+ ((ieee80211_rssadapt_debug > 0) && \
+ ppsratecheck(&lastrateadapt, &currssadaptps, ieee80211_adaptrate))
+#define RSSADAPT_PRINTF(X) \
+ if (RSSADAPT_DO_PRINT()) \
+ printf X
+
+int ieee80211_rssadapt_debug = 0;
+
+#else
+#define RSSADAPT_DO_PRINT() (0)
+#define RSSADAPT_PRINTF(X)
+#endif
+
+static struct ieee80211_rssadapt_expavgctl master_expavgctl = {
+ rc_decay_denom : 16,
+ rc_decay_old : 15,
+ rc_thresh_denom : 8,
+ rc_thresh_old : 4,
+ rc_avgrssi_denom : 8,
+ rc_avgrssi_old : 4
+};
+
+#ifdef __NetBSD__
+#ifdef IEEE80211_DEBUG
+/* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */
+static int
+sysctl_ieee80211_rssadapt_debug(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int*)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return (error);
+
+ IEEE80211_DPRINTF(("%s: t = %d, nodenum = %d, rnodenum = %d\n",
+ __func__, t, node.sysctl_num, rnode->sysctl_num));
+
+ if (t < 0 || t > 2)
+ return (EINVAL);
+ *(int*)rnode->sysctl_data = t;
+
+ return (0);
+}
+#endif /* IEEE80211_DEBUG */
+
+/* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */
+static int
+sysctl_ieee80211_rssadapt_expavgctl(SYSCTLFN_ARGS)
+{
+ struct ieee80211_rssadapt_expavgctl rc;
+ int error;
+ struct sysctlnode node;
+
+ node = *rnode;
+ rc = *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data;
+ node.sysctl_data = &rc;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return (error);
+
+ IEEE80211_DPRINTF(("%s: decay = %d/%d, thresh = %d/%d, "
+ "avgrssi = %d/%d, nodenum = %d, rnodenum = %d\n",
+ __func__, rc.rc_decay_old, rc.rc_decay_denom,
+ rc.rc_thresh_old, rc.rc_thresh_denom,
+ rc.rc_avgrssi_old, rc.rc_avgrssi_denom,
+ node.sysctl_num, rnode->sysctl_num));
+
+ if (rc.rc_decay_old < 0 ||
+ rc.rc_decay_denom < rc.rc_decay_old)
+ return (EINVAL);
+
+ if (rc.rc_thresh_old < 0 ||
+ rc.rc_thresh_denom < rc.rc_thresh_old)
+ return (EINVAL);
+
+ if (rc.rc_avgrssi_old < 0 ||
+ rc.rc_avgrssi_denom < rc.rc_avgrssi_old)
+ return (EINVAL);
+
+ *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data = rc;
+
+ return (0);
+}
+
+/*
+ * Setup sysctl(3) MIB, net.ieee80211.*
+ *
+ * TBD condition CTLFLAG_PERMANENT on being an LKM or not
+ */
+SYSCTL_SETUP(sysctl_ieee80211_rssadapt,
+ "sysctl ieee80211 rssadapt subtree setup")
+{
+ int rc;
+ struct sysctlnode *node;
+
+ if ((rc = sysctl_createv(clog, 0, NULL, &node,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "net", NULL,
+ NULL, 0, NULL, 0, CTL_NET, CTL_EOL)) != 0)
+ goto err;
+
+ if ((rc = sysctl_createv(clog, 0, &node, &node,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "link", NULL,
+ NULL, 0, NULL, 0, PF_LINK, CTL_EOL)) != 0)
+ goto err;
+
+ if ((rc = sysctl_createv(clog, 0, &node, &node,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211", NULL,
+ NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+ goto err;
+
+ if ((rc = sysctl_createv(clog, 0, &node, &node,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "rssadapt",
+ SYSCTL_DESCR("Received Signal Strength adaptation controls"),
+ NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+ goto err;
+
+#ifdef IEEE80211_DEBUG
+ /* control debugging printfs */
+ if ((rc = sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
+ SYSCTL_DESCR("Enable RSS adaptation debugging output"),
+ sysctl_ieee80211_rssadapt_debug, 0, &ieee80211_rssadapt_debug, 0,
+ CTL_CREATE, CTL_EOL)) != 0)
+ goto err;
+#endif /* IEEE80211_DEBUG */
+
+ /* control rate of decay for exponential averages */
+ if ((rc = sysctl_createv(clog, 0, &node, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT,
+ "expavgctl", SYSCTL_DESCR("RSS exponential averaging control"),
+ sysctl_ieee80211_rssadapt_expavgctl, 0,
+ &master_expavgctl, sizeof(master_expavgctl), CTL_CREATE,
+ CTL_EOL)) != 0)
+ goto err;
+
+ return;
+err:
+ printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
+}
+#endif /* __NetBSD__ */
+
+int
+ieee80211_rssadapt_choose(struct ieee80211_rssadapt *ra,
+ struct ieee80211_rateset *rs, struct ieee80211_frame *wh, u_int len,
+ int fixed_rate, const char *dvname, int do_not_adapt)
+{
+ u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
+ int flags = 0, i, rateidx = 0, thridx, top;
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+ flags |= IEEE80211_RATE_BASIC;
+
+ for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
+ i < IEEE80211_RSSADAPT_BKTS;
+ i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
+ thridx = i;
+ if (len <= top)
+ break;
+ }
+
+ thrs = &ra->ra_rate_thresh[thridx];
+
+ if (fixed_rate != -1) {
+ if ((rs->rs_rates[fixed_rate] & flags) == flags) {
+ rateidx = fixed_rate;
+ goto out;
+ }
+ flags |= IEEE80211_RATE_BASIC;
+ i = fixed_rate;
+ } else
+ i = rs->rs_nrates;
+
+ while (--i >= 0) {
+ rateidx = i;
+ if ((rs->rs_rates[i] & flags) != flags)
+ continue;
+ if (do_not_adapt)
+ break;
+ if ((*thrs)[i] < ra->ra_avg_rssi)
+ break;
+ }
+
+out:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_rssadapt_debug && dvname != NULL) {
+ printf("%s: dst %s threshold[%d, %d.%d] %d < %d\n",
+ dvname, ether_sprintf(wh->i_addr1), len,
+ (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) / 2,
+ (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) * 5 % 10,
+ (*thrs)[rateidx], ra->ra_avg_rssi);
+ }
+#endif /* IEEE80211_DEBUG */
+ return rateidx;
+}
+
+void
+ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt *ra)
+{
+ long interval;
+
+ ra->ra_pktrate =
+ (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2;
+ ra->ra_nfail = ra->ra_nok = 0;
+
+ /* a node is eligible for its rate to be raised every 1/10 to 10
+ * seconds, more eligible in proportion to recent packet rates.
+ */
+ interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate));
+ ra->ra_raise_interval.tv_sec = interval / (1000 * 1000);
+ ra->ra_raise_interval.tv_usec = interval % (1000 * 1000);
+}
+
+void
+ieee80211_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni,
+ struct ieee80211_rssadapt *ra, int rssi)
+{
+#ifdef IEEE80211_DEBUG
+ int last_avg_rssi = ra->ra_avg_rssi;
+#endif
+
+ ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi,
+ ra->ra_avg_rssi, (rssi << 8));
+
+ RSSADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n",
+ ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr),
+ rssi, last_avg_rssi, ra->ra_avg_rssi));
+}
+
+/*
+ * Adapt the data rate to suit the conditions. When a transmitted
+ * packet is dropped after IEEE80211_RSSADAPT_RETRY_LIMIT retransmissions,
+ * raise the RSS threshold for transmitting packets of similar length at
+ * the same data rate.
+ */
+void
+ieee80211_rssadapt_lower_rate(struct ieee80211com *ic,
+ struct ieee80211_node *ni, struct ieee80211_rssadapt *ra,
+ struct ieee80211_rssdesc *id)
+{
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+ u_int16_t last_thr;
+ u_int i, thridx, top;
+
+ ra->ra_nfail++;
+
+ if (id->id_rateidx >= rs->rs_nrates) {
+ RSSADAPT_PRINTF(("ieee80211_rssadapt_lower_rate: "
+ "%s rate #%d > #%d out of bounds\n",
+ ether_sprintf(ni->ni_macaddr), id->id_rateidx,
+ rs->rs_nrates - 1));
+ return;
+ }
+
+ for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
+ i < IEEE80211_RSSADAPT_BKTS;
+ i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
+ thridx = i;
+ if (id->id_len <= top)
+ break;
+ }
+
+ last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx];
+ ra->ra_rate_thresh[thridx][id->id_rateidx] =
+ interpolate(master_expavgctl.rc_thresh, last_thr,
+ (id->id_rssi << 8));
+
+ RSSADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n",
+ ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr),
+ id->id_rssi, id->id_len,
+ (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) / 2,
+ (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) * 5 % 10,
+ last_thr, ra->ra_rate_thresh[thridx][id->id_rateidx]));
+}
+
+void
+ieee80211_rssadapt_raise_rate(struct ieee80211com *ic,
+ struct ieee80211_rssadapt *ra, struct ieee80211_rssdesc *id)
+{
+ u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr;
+ struct ieee80211_node *ni = id->id_node;
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+ int i, rate, top;
+#ifdef IEEE80211_DEBUG
+ int j;
+#endif
+
+ ra->ra_nok++;
+
+ if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval))
+ return;
+
+ for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
+ i < IEEE80211_RSSADAPT_BKTS;
+ i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
+ thrs = &ra->ra_rate_thresh[i];
+ if (id->id_len <= top)
+ break;
+ }
+
+ if (id->id_rateidx + 1 < rs->rs_nrates &&
+ (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) {
+ rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL);
+
+ RSSADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d ",
+ ic->ic_if.if_xname,
+ IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER* i),
+ rate / 2, rate * 5 % 10, (*thrs)[id->id_rateidx + 1]));
+ oldthr = (*thrs)[id->id_rateidx + 1];
+ if ((*thrs)[id->id_rateidx] == 0)
+ newthr = ra->ra_avg_rssi;
+ else
+ newthr = (*thrs)[id->id_rateidx];
+ (*thrs)[id->id_rateidx + 1] =
+ interpolate(master_expavgctl.rc_decay, oldthr, newthr);
+
+ RSSADAPT_PRINTF(("-> %d\n", (*thrs)[id->id_rateidx + 1]));
+ }
+
+#ifdef IEEE80211_DEBUG
+ if (RSSADAPT_DO_PRINT()) {
+ printf("%s: dst %s thresholds\n", ic->ic_if.if_xname,
+ ether_sprintf(ni->ni_macaddr));
+ for (i = 0; i < IEEE80211_RSSADAPT_BKTS; i++) {
+ printf("%d-byte", IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER * i));
+ for (j = 0; j < rs->rs_nrates; j++) {
+ rate = (rs->rs_rates[j] & IEEE80211_RATE_VAL);
+ printf(", T[%d.%d] = %d", rate / 2,
+ rate * 5 % 10, ra->ra_rate_thresh[i][j]);
+ }
+ printf("\n");
+ }
+ }
+#endif /* IEEE80211_DEBUG */
+}
diff --git a/sys/net80211/ieee80211_rssadapt.h b/sys/net80211/ieee80211_rssadapt.h
new file mode 100644
index 00000000000..37816630557
--- /dev/null
+++ b/sys/net80211/ieee80211_rssadapt.h
@@ -0,0 +1,102 @@
+/* $OpenBSD: ieee80211_rssadapt.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_rssadapt.h,v 1.3 2004/05/06 03:03:20 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``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 David
+ * Young 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.
+ */
+
+/* Data-rate adaptation loosely based on "Link Adaptation Strategy
+ * for IEEE 802.11 WLAN via Received Signal Strength Measurement"
+ * by Javier del Prado Pavon and Sunghyun Choi.
+ */
+
+/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
+#define IEEE80211_RSSADAPT_BKTS 3
+#define IEEE80211_RSSADAPT_BKT0 128
+#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
+
+#define ieee80211_rssadapt_thresh_new \
+ (ieee80211_rssadapt_thresh_denom - ieee80211_rssadapt_thresh_old)
+#define ieee80211_rssadapt_decay_new \
+ (ieee80211_rssadapt_decay_denom - ieee80211_rssadapt_decay_old)
+#define ieee80211_rssadapt_avgrssi_new \
+ (ieee80211_rssadapt_avgrssi_denom - ieee80211_rssadapt_avgrssi_old)
+
+struct ieee80211_rssadapt_expavgctl {
+ /* RSS threshold decay. */
+ u_int rc_decay_denom;
+ u_int rc_decay_old;
+ /* RSS threshold update. */
+ u_int rc_thresh_denom;
+ u_int rc_thresh_old;
+ /* RSS average update. */
+ u_int rc_avgrssi_denom;
+ u_int rc_avgrssi_old;
+};
+
+struct ieee80211_rssadapt {
+ /* exponential average RSSI << 8 */
+ u_int16_t ra_avg_rssi;
+ /* Tx failures in this update interval */
+ u_int32_t ra_nfail;
+ /* Tx successes in this update interval */
+ u_int32_t ra_nok;
+ /* exponential average packets/second */
+ u_int32_t ra_pktrate;
+ /* RSSI threshold for each Tx rate */
+ u_int16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS]
+ [IEEE80211_RATE_SIZE];
+ struct timeval ra_last_raise;
+ struct timeval ra_raise_interval;
+};
+
+/* Properties of a Tx packet, for link adaptation. */
+struct ieee80211_rssdesc {
+ u_int id_len; /* Tx packet length */
+ u_int id_rateidx; /* index into ni->ni_rates */
+ struct ieee80211_node *id_node; /* destination STA MAC */
+ u_int8_t id_rssi; /* destination STA avg RSS @
+ * Tx time
+ */
+};
+
+void ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt *);
+void ieee80211_rssadapt_input(struct ieee80211com *, struct ieee80211_node *,
+ struct ieee80211_rssadapt *, int);
+void ieee80211_rssadapt_lower_rate(struct ieee80211com *,
+ struct ieee80211_node *, struct ieee80211_rssadapt *,
+ struct ieee80211_rssdesc *);
+void ieee80211_rssadapt_raise_rate(struct ieee80211com *,
+ struct ieee80211_rssadapt *, struct ieee80211_rssdesc *);
+int ieee80211_rssadapt_choose(struct ieee80211_rssadapt *,
+ struct ieee80211_rateset *, struct ieee80211_frame *, u_int, int,
+ const char *, int);
+#ifdef IEEE80211_DEBUG
+extern int ieee80211_rssadapt_debug;
+#endif /* IEEE80211_DEBUG */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
new file mode 100644
index 00000000000..e2a483ca267
--- /dev/null
+++ b/sys/net80211/ieee80211_var.h
@@ -0,0 +1,320 @@
+/* $OpenBSD: ieee80211_var.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.15 2004/04/05 22:10:26 sam Exp $
+ */
+#ifndef _NET80211_IEEE80211_VAR_H_
+#define _NET80211_IEEE80211_VAR_H_
+
+/*
+ * Definitions for IEEE 802.11 drivers.
+ */
+
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
+#include <net80211/ieee80211_node.h>
+#include <net80211/ieee80211_proto.h>
+
+#define IEEE80211_CHAN_MAX 255
+#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
+#define IEEE80211_CHAN_ANYC \
+ ((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
+
+#define IEEE80211_TXPOWER_MAX 100 /* max power */
+#define IEEE80211_TXPOWER_MIN 0 /* kill radio (if possible) */
+
+enum ieee80211_phytype {
+ IEEE80211_T_DS, /* direct sequence spread spectrum */
+ IEEE80211_T_FH, /* frequency hopping */
+ IEEE80211_T_OFDM, /* frequency division multiplexing */
+ IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
+};
+#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
+
+/* XXX not really a mode; there are really multiple PHY's */
+enum ieee80211_phymode {
+ IEEE80211_MODE_AUTO = 0, /* autoselect */
+ IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
+ IEEE80211_MODE_11B = 2, /* 2GHz, CCK */
+ IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */
+ IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */
+ IEEE80211_MODE_TURBO = 5, /* 5GHz, OFDM, 2x clock */
+};
+#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+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 */
+};
+
+/*
+ * 802.11g protection mode.
+ */
+enum ieee80211_protmode {
+ IEEE80211_PROT_NONE = 0, /* no protection */
+ IEEE80211_PROT_CTSONLY = 1, /* CTS to self */
+ IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */
+};
+
+/*
+ * 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 */
+};
+
+/* 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) */
+
+/*
+ * Useful combinations of channel characteristics.
+ */
+#define IEEE80211_CHAN_FHSS \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
+#define IEEE80211_CHAN_A \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_B \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+#define IEEE80211_CHAN_PUREG \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_G \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+#define IEEE80211_CHAN_T \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+
+#define IEEE80211_IS_CHAN_FHSS(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
+#define IEEE80211_IS_CHAN_A(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
+#define IEEE80211_IS_CHAN_B(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
+#define IEEE80211_IS_CHAN_PUREG(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
+#define IEEE80211_IS_CHAN_G(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
+#define IEEE80211_IS_CHAN_T(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+
+#define IEEE80211_IS_CHAN_2GHZ(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
+#define IEEE80211_IS_CHAN_5GHZ(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 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)
+
+/* ni_chan encoding for FH phy */
+#define IEEE80211_FH_CHANMOD 80
+#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat))
+#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1)
+#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD)
+
+#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
+
+#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
+
+struct ieee80211com {
+#ifdef __NetBSD__
+ struct ethercom ic_ec;
+#else
+ struct arpcom ic_ac;
+#endif
+ 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 ieee80211com *,
+ struct ieee80211_node *, int);
+ int (*ic_set_tim)(struct ieee80211com *, int, int);
+ u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
+ u_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)];
+ u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)];
+ u_char ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)];
+ struct ifqueue ic_mgtq;
+ struct ifqueue ic_pwrsaveq;
+ u_int32_t ic_flags; /* 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 */
+ u_int32_t ic_aid_bitmap[IEEE80211_MAX_AID / 32 + 1];
+ u_int16_t ic_max_aid;
+ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
+ struct ifmedia ic_media; /* interface media config */
+#ifdef __FreeBSD__
+ struct bpf_if *ic_rawbpf; /* packet filter structure */
+#else
+ caddr_t ic_rawbpf; /* packet filter structure */
+#endif
+ struct ieee80211_node *ic_bss; /* information for this node */
+ struct ieee80211_channel *ic_ibss_chan;
+ int ic_fixed_rate; /* index to ic_sup_rates[] */
+ u_int16_t ic_rtsthreshold;
+ u_int16_t ic_fragthreshold;
+#ifdef __FreeBSD__
+ struct mtx ic_nodelock; /* on node table */
+#endif
+ u_int ic_scangen; /* gen# for timeout scan */
+ struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *);
+ void (*ic_node_free)(struct ieee80211com *,
+ struct ieee80211_node *);
+ void (*ic_node_copy)(struct ieee80211com *,
+ struct ieee80211_node *,
+ const struct ieee80211_node *);
+ u_int8_t (*ic_node_getrssi)(struct ieee80211com *,
+ struct ieee80211_node *);
+ TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */
+ LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE];
+ u_int16_t ic_lintval; /* listen 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_txpower; /* tx power setting (dbM) */
+ u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */
+ 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];
+ struct ieee80211_wepkey ic_nw_keys[IEEE80211_WEP_NKID];
+ int ic_wep_txkey; /* default tx key index */
+ void *ic_wep_ctx; /* wep crypt context */
+ u_int32_t ic_iv; /* initial vector for wep */
+ struct ieee80211_stats ic_stats; /* statistics */
+};
+#ifdef __NetBSD__
+#define ic_if ic_ec.ec_if
+#else
+#define ic_if ic_ac.ac_if
+#endif
+#define ic_softc ic_if.if_softc
+
+#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
+#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN)
+
+/* ic_flags */
+#define IEEE80211_F_ASCAN 0x00000001 /* STATUS: active scan */
+#define IEEE80211_F_SIBSS 0x00000002 /* STATUS: start IBSS */
+#define IEEE80211_F_WEPON 0x00000100 /* CONF: WEP enabled */
+#define IEEE80211_F_IBSSON 0x00000200 /* CONF: IBSS creation enable */
+#define IEEE80211_F_PMGTON 0x00000400 /* CONF: Power mgmt enable */
+#define IEEE80211_F_DESBSSID 0x00000800 /* CONF: des_bssid is set */
+#define IEEE80211_F_SCANAP 0x00001000 /* CONF: Scanning AP */
+#define IEEE80211_F_ROAMING 0x00002000 /* CONF: roaming enabled */
+#define IEEE80211_F_SWRETRY 0x00004000 /* CONF: sw tx retry enabled */
+#define IEEE80211_F_TXPMGT 0x00018000 /* STATUS: tx power */
+#define IEEE80211_F_TXPOW_OFF 0x00000000 /* TX Power: radio disabled */
+#define IEEE80211_F_TXPOW_FIXED 0x00008000 /* TX Power: fixed rate */
+#define IEEE80211_F_TXPOW_AUTO 0x00010000 /* TX Power: undefined */
+#define IEEE80211_F_SHSLOT 0x00020000 /* CONF: short slot time */
+#define IEEE80211_F_SHPREAMBLE 0x00040000 /* CONF: short preamble */
+#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
+#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/
+
+/* ic_caps */
+#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
+#define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */
+#define IEEE80211_C_PMGT 0x00000004 /* CAPABILITY: Power mgmt */
+#define IEEE80211_C_HOSTAP 0x00000008 /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_AHDEMO 0x00000010 /* CAPABILITY: Old Adhoc Demo */
+#define IEEE80211_C_SWRETRY 0x00000020 /* CAPABILITY: sw tx retry */
+#define IEEE80211_C_TXPMGT 0x00000040 /* CAPABILITY: tx power mgmt */
+#define IEEE80211_C_SHSLOT 0x00000080 /* CAPABILITY: short slottime */
+#define IEEE80211_C_SHPREAMBLE 0x00000100 /* CAPABILITY: short preamble */
+#define IEEE80211_C_MONITOR 0x00000200 /* CAPABILITY: monitor mode */
+
+/* flags for ieee80211_fix_rate() */
+#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */
+#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
+#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
+#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
+
+void ieee80211_ifattach(struct ifnet *);
+void ieee80211_ifdetach(struct ifnet *);
+void ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t);
+int ieee80211_media_change(struct ifnet *);
+void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
+int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
+int ieee80211_get_rate(struct ieee80211com *);
+int ieee80211_cfgget(struct ifnet *, u_long, caddr_t);
+int ieee80211_cfgset(struct ifnet *, u_long, caddr_t);
+void ieee80211_watchdog(struct ifnet *);
+int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int);
+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 *);
+u_int ieee80211_ieee2mhz(u_int, u_int);
+int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
+enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
+ struct ieee80211_channel *);
+
+#ifdef IEEE80211_DEBUG
+extern int ieee80211_debug;
+#define IEEE80211_DPRINTF(X) if (ieee80211_debug) printf X
+#define IEEE80211_DPRINTF2(X) if (ieee80211_debug>1) printf X
+#else
+#define IEEE80211_DPRINTF(X)
+#define IEEE80211_DPRINTF2(X)
+#endif
+
+extern int ieee80211_inact_max;
+
+#endif /* _NET80211_IEEE80211_VAR_H_ */