595c6d88b2
<kml@patheticgeek.net>. This patch does NOT add monitor mode support for the Lucent radios. awi(4) was only modified for compatibility with the new mediaopt. It does NOT support monitor mode. Tested by Kevin, Daniel Carosone, and I.
3337 lines
87 KiB
C
3337 lines
87 KiB
C
/* $NetBSD: if_ieee80211subr.c,v 1.24 2003/02/25 01:57:36 dyoung 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 <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: if_ieee80211subr.c,v 1.24 2003/02/25 01:57:36 dyoung Exp $");
|
|
|
|
#include "opt_inet.h"
|
|
#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 <crypto/arc4/arc4.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_ether.h>
|
|
#include <net/if_llc.h>
|
|
#include <net/if_ieee80211.h>
|
|
|
|
#include <dev/ic/wi_ieee.h> /* XXX */
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#endif
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_inarp.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_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;
|
|
|
|
ether_ifattach(ifp, ic->ic_myaddr);
|
|
#if NBPFILTER > 0
|
|
bpfattach2(ifp, DLT_IEEE802_11,
|
|
sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
|
|
#endif
|
|
ieee80211_crc_init();
|
|
ic->ic_iv = arc4random();
|
|
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_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);
|
|
|
|
/* initialize management frame handler */
|
|
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;
|
|
}
|
|
|
|
void
|
|
ieee80211_ifdetach(struct ifnet *ifp)
|
|
{
|
|
struct ieee80211com *ic = (void *)ifp;
|
|
int s;
|
|
|
|
s = splnet();
|
|
IF_PURGE(&ic->ic_mgtq);
|
|
if (ic->ic_wep_ctx != NULL) {
|
|
free(ic->ic_wep_ctx, M_DEVBUF);
|
|
ic->ic_wep_ctx = NULL;
|
|
}
|
|
ieee80211_free_allnodes(ic);
|
|
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;
|
|
ALTQ_DECL(struct altq_pktattr pktattr;)
|
|
|
|
/* trim CRC here for 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;
|
|
}
|
|
|
|
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, ifp->if_broadcastaddr)) {
|
|
/* 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 =
|
|
le16toh(*(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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
#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)
|
|
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) {
|
|
#ifdef ALTQ
|
|
if (ALTQ_IS_ENABLED(&ifp->if_snd))
|
|
altq_etherclassify(&ifp->if_snd, m1,
|
|
&pktattr);
|
|
#endif
|
|
len = m1->m_pkthdr.len;
|
|
IFQ_ENQUEUE(&ifp->if_snd, m1, &pktattr, 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
|
|
(*ifp->if_input)(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:
|
|
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, sizeof(struct ieee80211_frame), 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\n", ifp->if_xname,
|
|
ieee80211_mgt_subtype_name[
|
|
(type & IEEE80211_FC0_SUBTYPE_MASK)
|
|
>> IEEE80211_FC0_SUBTYPE_SHIFT],
|
|
ether_sprintf(ni->ni_macaddr));
|
|
}
|
|
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, sizeof(struct ieee80211_frame), 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;
|
|
|
|
n0 = NULL;
|
|
np = &n0;
|
|
off = 0;
|
|
while (m->m_pkthdr.len > off) {
|
|
if (n0 == NULL) {
|
|
MGETHDR(n, M_DONTWAIT, MT_DATA);
|
|
if (n == NULL) {
|
|
m_freem(m);
|
|
return NULL;
|
|
}
|
|
M_COPY_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 (m->m_pkthdr.len - 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 > m->m_pkthdr.len - off)
|
|
n->m_len = m->m_pkthdr.len - 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 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->p_ucred,
|
|
&curproc->p_acflag)) != 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;
|
|
switch (ic->ic_state) {
|
|
case IEEE80211_S_INIT:
|
|
case IEEE80211_S_SCAN:
|
|
if (ic->ic_opmode == IEEE80211_M_STA)
|
|
chan->i_channel = ic->ic_des_chan;
|
|
else
|
|
chan->i_channel = ic->ic_ibss_chan;
|
|
break;
|
|
default:
|
|
chan->i_channel = ic->ic_bss.ni_chan;
|
|
break;
|
|
}
|
|
break;
|
|
case SIOCGIFGENERIC:
|
|
error = ieee80211_cfgget(ifp, cmd, data);
|
|
break;
|
|
case SIOCSIFGENERIC:
|
|
error = suser(curproc->p_ucred, &curproc->p_acflag);
|
|
if (error)
|
|
break;
|
|
error = ieee80211_cfgset(ifp, cmd, data);
|
|
break;
|
|
default:
|
|
error = ether_ioctl(ifp, 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;
|
|
struct ieee80211_node *ni, *nextbs;
|
|
|
|
if (ic->ic_scan_timer) {
|
|
if (--ic->ic_scan_timer == 0) {
|
|
if (ic->ic_state == IEEE80211_S_SCAN)
|
|
ieee80211_end_scan(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) {
|
|
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_scan_timer != 0 || ic->ic_mgt_timer != 0 ||
|
|
ic->ic_inact_timer != 0)
|
|
ifp->if_timer = 1;
|
|
}
|
|
|
|
void
|
|
ieee80211_next_scan(struct ifnet *ifp)
|
|
{
|
|
struct ieee80211com *ic = (void *)ifp;
|
|
int chan;
|
|
|
|
chan = ic->ic_bss.ni_chan;
|
|
for (;;) {
|
|
chan = (chan + 1) % (IEEE80211_CHAN_MAX + 1);
|
|
if (isset(ic->ic_chan_active, chan))
|
|
break;
|
|
if (chan == ic->ic_bss.ni_chan) {
|
|
DPRINTF(("ieee80211_next_scan: no chan available\n"));
|
|
return;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
void
|
|
ieee80211_end_scan(struct ifnet *ifp)
|
|
{
|
|
struct ieee80211com *ic = (void *)ifp;
|
|
struct ieee80211_node *ni, *nextbs, *selbs;
|
|
void *p;
|
|
u_int8_t rate;
|
|
int i, fail;
|
|
|
|
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) {
|
|
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);
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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 ? "!" : "");
|
|
}
|
|
if (!fail) {
|
|
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);
|
|
}
|
|
|
|
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);
|
|
TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
|
|
LIST_REMOVE(ni, ni_hash);
|
|
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 int
|
|
ieee80211_send_prreq(struct ieee80211com *ic, struct ieee80211_node *ni,
|
|
int type, int dummy)
|
|
{
|
|
int i, 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 += sizeof(struct ieee80211_frame);
|
|
frm = mtod(m, u_int8_t *);
|
|
|
|
*frm++ = IEEE80211_ELEMID_SSID;
|
|
*frm++ = ic->ic_des_esslen;
|
|
memcpy(frm, ic->ic_des_essid, ic->ic_des_esslen);
|
|
frm += ic->ic_des_esslen;
|
|
|
|
*frm++ = IEEE80211_ELEMID_RATES;
|
|
for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
|
|
if (ic->ic_sup_rates[i] != 0)
|
|
frm[i + 1] = ic->ic_sup_rates[i];
|
|
}
|
|
*frm++ = i;
|
|
frm += i;
|
|
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 += 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(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_ELEMID_SSID;
|
|
*frm++ = ni->ni_esslen;
|
|
memcpy(frm, ni->ni_essid, ni->ni_esslen);
|
|
frm += ni->ni_esslen;
|
|
*frm++ = IEEE80211_ELEMID_RATES;
|
|
*frm++ = ni->ni_nrate;
|
|
memcpy(frm, ni->ni_rates, ni->ni_nrate);
|
|
frm += ni->ni_nrate;
|
|
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, *rates;
|
|
u_int16_t capinfo;
|
|
int i, 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 += 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;
|
|
*(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_ELEMID_SSID;
|
|
*frm++ = ni->ni_esslen;
|
|
memcpy(frm, ni->ni_essid, ni->ni_esslen);
|
|
frm += ni->ni_esslen;
|
|
|
|
*frm++ = IEEE80211_ELEMID_RATES;
|
|
rates = frm++; /* update later */
|
|
for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
|
|
if (ni->ni_rates[i] != 0)
|
|
*frm++ = ni->ni_rates[i];
|
|
}
|
|
*rates = frm - (rates + 1);
|
|
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, *rates, *r;
|
|
u_int16_t capinfo;
|
|
int i;
|
|
|
|
/*
|
|
* 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 += 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;
|
|
*(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;
|
|
|
|
*frm++ = IEEE80211_ELEMID_RATES;
|
|
rates = frm++; /* update later */
|
|
if (ni != NULL)
|
|
r = ni->ni_rates;
|
|
else
|
|
r = ic->ic_bss.ni_rates;
|
|
for (i = 0; i < IEEE80211_RATE_SIZE; i++, r++) {
|
|
if (*r != 0)
|
|
*frm++ = *r;
|
|
}
|
|
*rates = frm - (rates + 1);
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (ssid == NULL || rates == NULL) {
|
|
DPRINTF(("ieee80211_recv_beacon: ssid=%p, rates=%p, chan=%d\n",
|
|
ssid, rates, chan));
|
|
return;
|
|
}
|
|
if (ssid[1] > IEEE80211_NWID_LEN) {
|
|
DPRINTF(("ieee80211_recv_beacon: bad ssid len %d from %s\n",
|
|
ssid[1], ether_sprintf(wh->i_addr2)));
|
|
return;
|
|
}
|
|
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)) {
|
|
printf("ieee80211_recv_prreq: %sbeacon ",
|
|
(ni == NULL ? "new " : ""));
|
|
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) {
|
|
/*
|
|
* Update ESSID at probe response to adopt hidden AP by
|
|
* Lucent/Cisco, which announces null ESSID in beacon.
|
|
*/
|
|
if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
|
|
IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
|
|
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 = le16toh(*(u_int16_t *)bintval);
|
|
ni->ni_capinfo = le16toh(*(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_scan_timer == 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;
|
|
}
|
|
if (ssid == NULL || rates == NULL) {
|
|
DPRINTF(("ieee80211_recv_prreq: ssid=%p, rates=%p\n",
|
|
ssid, rates));
|
|
return;
|
|
}
|
|
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 = le16toh(*(u_int16_t *)frm);
|
|
seq = le16toh(*(u_int16_t *)(frm + 2));
|
|
status = le16toh(*(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 = le16toh(*(u_int16_t *)frm); frm += 2;
|
|
bintval = le16toh(*(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;
|
|
}
|
|
if (ssid == NULL || rates == NULL) {
|
|
DPRINTF(("ieee80211_recv_asreq: ssid=%p, rates=%p\n",
|
|
ssid, rates));
|
|
return;
|
|
}
|
|
if (ssid[1] > IEEE80211_NWID_LEN) {
|
|
DPRINTF(("ieee80211_recv_asreq: bad ssid len %d from %s\n",
|
|
ssid[1], ether_sprintf(wh->i_addr2)));
|
|
return;
|
|
}
|
|
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)));
|
|
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)));
|
|
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) {
|
|
ni->ni_associd = 0xc000 | ic->ic_bss.ni_associd++;
|
|
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\n",
|
|
ifp->if_xname, (newassoc ? "newly" : "already"),
|
|
ether_sprintf(ni->ni_macaddr));
|
|
}
|
|
|
|
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 = le16toh(*(u_int16_t *)frm);
|
|
frm += 2;
|
|
|
|
status = le16toh(*(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 = le16toh(*(u_int16_t *)frm);
|
|
frm += 2;
|
|
rates = frm;
|
|
|
|
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 = le16toh(*(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);
|
|
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 = le16toh(*(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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
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_scan_timer = 0;
|
|
ic->ic_mgt_timer = 0;
|
|
IF_PURGE(&ic->ic_mgtq);
|
|
if (ic->ic_wep_ctx != NULL) {
|
|
free(ic->ic_wep_ctx, M_DEVBUF);
|
|
ic->ic_wep_ctx = NULL;
|
|
}
|
|
ieee80211_free_allnodes(ic);
|
|
break;
|
|
}
|
|
break;
|
|
case IEEE80211_S_SCAN:
|
|
ic->ic_flags &= ~IEEE80211_F_SIBSS;
|
|
ni = &ic->ic_bss;
|
|
/* initialize bss for probe request */
|
|
IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
|
|
IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
|
|
ni->ni_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:
|
|
ic->ic_flags |= IEEE80211_F_ASCAN;
|
|
ic->ic_scan_timer = IEEE80211_ASCAN_WAIT;
|
|
/* use lowest rate */
|
|
ni->ni_txrate = 0;
|
|
IEEE80211_SEND_MGMT(ic, ni,
|
|
IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
|
|
break;
|
|
case IEEE80211_S_SCAN:
|
|
/* scan next */
|
|
if (ic->ic_flags & IEEE80211_F_ASCAN) {
|
|
if (ic->ic_scan_timer == 0)
|
|
ic->ic_scan_timer =
|
|
IEEE80211_ASCAN_WAIT;
|
|
IEEE80211_SEND_MGMT(ic, ni,
|
|
IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
|
|
} else {
|
|
if (ic->ic_scan_timer == 0)
|
|
ic->ic_scan_timer =
|
|
IEEE80211_PSCAN_WAIT;
|
|
ifp->if_timer = 1;
|
|
}
|
|
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(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++;
|
|
ic->ic_flags |= IEEE80211_F_ASCAN;
|
|
ic->ic_scan_timer = IEEE80211_ASCAN_WAIT;
|
|
IEEE80211_SEND_MGMT(ic, &ic->ic_bss,
|
|
IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
|
|
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(arc4_ctxlen(), 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_COPY_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;
|
|
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)
|
|
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)
|
|
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 != le32toh(*(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;
|
|
}
|
|
|
|
/*
|
|
* 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:
|
|
strcpy((char *)&wreq.wi_val[1], hostname);
|
|
wreq.wi_val[0] = htole16(strlen(hostname));
|
|
wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
|
|
break;
|
|
case WI_RID_CURRENT_SSID:
|
|
if (ic->ic_state != IEEE80211_S_RUN) {
|
|
wreq.wi_val[0] = 0;
|
|
wreq.wi_len = 1;
|
|
break;
|
|
}
|
|
wreq.wi_val[0] = htole16(ic->ic_bss.ni_esslen);
|
|
memcpy(&wreq.wi_val[1], ic->ic_bss.ni_essid,
|
|
ic->ic_bss.ni_esslen);
|
|
wreq.wi_len = (1 + ic->ic_bss.ni_esslen + 1) / 2;
|
|
break;
|
|
case WI_RID_OWN_SSID:
|
|
case WI_RID_DESIRED_SSID:
|
|
wreq.wi_val[0] = htole16(ic->ic_des_esslen);
|
|
memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
|
|
wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
|
|
break;
|
|
case WI_RID_CURRENT_BSSID:
|
|
if (ic->ic_state == IEEE80211_S_RUN)
|
|
IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss.ni_bssid);
|
|
else
|
|
memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
|
|
wreq.wi_len = IEEE80211_ADDR_LEN / 2;
|
|
break;
|
|
case WI_RID_CHANNEL_LIST:
|
|
memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
|
|
/*
|
|
* Since channel 0 is not available for DS, channel 1
|
|
* is assigned to LSB on WaveLAN.
|
|
*/
|
|
if (ic->ic_phytype == IEEE80211_T_DS)
|
|
i = 1;
|
|
else
|
|
i = 0;
|
|
for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
|
|
if (isset(ic->ic_chan_active, i)) {
|
|
setbit((u_int8_t *)wreq.wi_val, j);
|
|
wreq.wi_len = j / 16 + 1;
|
|
}
|
|
}
|
|
break;
|
|
case WI_RID_OWN_CHNL:
|
|
wreq.wi_val[0] = htole16(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->p_ucred, &curproc->p_acflag);
|
|
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 = le16toh(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 = le16toh(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 (le16toh(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 (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
|
|
ic->ic_opmode = le16toh(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 (le16toh(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 (le16toh(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 (le16toh(wreq.wi_val[0]) != 1)
|
|
return EINVAL; /* not supported */
|
|
break;
|
|
case WI_RID_SYSTEM_SCALE:
|
|
if (wreq.wi_len != 1)
|
|
return EINVAL;
|
|
if (le16toh(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 = le16toh(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 (le16toh(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 = le16toh(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 = le16toh(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 = le16toh(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 = le16toh(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;
|
|
}
|