diff options
-rw-r--r-- | sys/conf/files | 12 | ||||
-rw-r--r-- | sys/net/if_ieee80211subr.c | 3691 | ||||
-rw-r--r-- | sys/net80211/ieee80211.c | 1051 | ||||
-rw-r--r-- | sys/net80211/ieee80211.h | 548 | ||||
-rw-r--r-- | sys/net80211/ieee80211_compat.c | 54 | ||||
-rw-r--r-- | sys/net80211/ieee80211_compat.h | 64 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.c | 364 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.h | 53 | ||||
-rw-r--r-- | sys/net80211/ieee80211_input.c | 1607 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 1384 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.h | 211 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 877 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 206 | ||||
-rw-r--r-- | sys/net80211/ieee80211_output.c | 671 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 566 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.h | 83 | ||||
-rw-r--r-- | sys/net80211/ieee80211_radiotap.h | 213 | ||||
-rw-r--r-- | sys/net80211/ieee80211_rssadapt.c | 409 | ||||
-rw-r--r-- | sys/net80211/ieee80211_rssadapt.h | 102 | ||||
-rw-r--r-- | sys/net80211/ieee80211_var.h | 320 |
20 files changed, 8793 insertions, 3693 deletions
diff --git a/sys/conf/files b/sys/conf/files index e0bf11f7b2e..ab754407126 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.305 2004/06/20 17:19:26 itojun Exp $ +# $OpenBSD: files,v 1.306 2004/06/22 22:53:51 millert Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -693,7 +693,15 @@ file net/slcompress.c sl | ppp | strip file net/if_enc.c enc needs-count file net/if_gre.c gre needs-count #file net/if_ieee1394subr.c ieee1394 -file net/if_ieee80211subr.c wlan +file net80211/ieee80211.c wlan +file net80211/ieee80211_compat.c wlan +file net80211/ieee80211_crypto.c wlan +file net80211/ieee80211_input.c wlan +file net80211/ieee80211_ioctl.c wlan +file net80211/ieee80211_node.c wlan +file net80211/ieee80211_output.c wlan +file net80211/ieee80211_proto.c wlan +file net80211/ieee80211_rssadapt.c wlan file netccitt/ccitt_proto.c ccitt file netccitt/hd_debug.c hdlc file netccitt/hd_input.c hdlc diff --git a/sys/net/if_ieee80211subr.c b/sys/net/if_ieee80211subr.c deleted file mode 100644 index a9a1f22698e..00000000000 --- a/sys/net/if_ieee80211subr.c +++ /dev/null @@ -1,3691 +0,0 @@ -/* $OpenBSD: if_ieee80211subr.c,v 1.2 2004/01/26 03:35:36 fgsch Exp $ */ -/* $NetBSD: if_ieee80211subr.c,v 1.43 2003/07/06 20:54:25 dyoung Exp $ */ -/* $FreeBSD: src/sys/net/if_ieee80211subr.c,v 1.4 2003/01/21 08:55:59 alfred Exp $ */ - -/*- - * Copyright (c) 2001 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Atsushi Onoe. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * IEEE 802.11 generic handler - */ - -#include "bpfilter.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <sys/errno.h> -#include <sys/device.h> -#include <sys/proc.h> - -#include <machine/endian.h> - -#include <dev/rndvar.h> -#include <crypto/arc4.h> - -#include <net/if.h> -#include <net/if_dl.h> -#include <net/if_media.h> -#include <net/if_llc.h> -#include <netinet/in.h> -#include <netinet/if_ether.h> -#include <net/if_ieee80211.h> - -#include <dev/ic/if_wi_ieee.h> /* XXX */ - -#if NBPFILTER > 0 -#include <net/bpf.h> -#endif - -#ifdef IEEE80211_DEBUG -int ieee80211_debug = 0; -#define DPRINTF(X) if (ieee80211_debug) printf X -#define DPRINTF2(X) if (ieee80211_debug>1) printf X -#else -#define DPRINTF(X) -#define DPRINTF2(X) -#endif - -static int ieee80211_send_prreq(struct ieee80211com *, - struct ieee80211_node *, int, int); -static int ieee80211_send_prresp(struct ieee80211com *, - struct ieee80211_node *, int, int); -static int ieee80211_send_auth(struct ieee80211com *, - struct ieee80211_node *, int, int); -static int ieee80211_send_deauth(struct ieee80211com *, - struct ieee80211_node *, int, int); -static int ieee80211_send_asreq(struct ieee80211com *, - struct ieee80211_node *, int, int); -static int ieee80211_send_asresp(struct ieee80211com *, - struct ieee80211_node *, int, int); -static int ieee80211_send_disassoc(struct ieee80211com *, - struct ieee80211_node *, int, int); - -static void ieee80211_recv_beacon(struct ieee80211com *, - struct mbuf *, int, u_int32_t); -static void ieee80211_recv_prreq(struct ieee80211com *, - struct mbuf *, int, u_int32_t); -static void ieee80211_recv_auth(struct ieee80211com *, - struct mbuf *, int, u_int32_t); -static void ieee80211_recv_asreq(struct ieee80211com *, - struct mbuf *, int, u_int32_t); -static void ieee80211_recv_asresp(struct ieee80211com *, - struct mbuf *, int, u_int32_t); -static void ieee80211_recv_disassoc(struct ieee80211com *, - struct mbuf *, int, u_int32_t); -static void ieee80211_recv_deauth(struct ieee80211com *, - struct mbuf *, int, u_int32_t); - -static void ieee80211_recv_pspoll(struct ieee80211com *, - struct mbuf *, int, u_int32_t); - -static void ieee80211_crc_init(void); -static u_int32_t ieee80211_crc_update(u_int32_t, u_int8_t *, int); - -static const char *ieee80211_mgt_subtype_name[] = { - "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", - "probe_req", "probe_resp", "reserved#6", "reserved#7", - "beacon", "atim", "disassoc", "auth", - "deauth", "reserved#13", "reserved#14", "reserved#15" -}; - -void -ieee80211_ifattach(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - int i, rate, mword; - struct ifmediareq imr; - - bcopy(ic->ic_myaddr, ((struct arpcom *)ifp)->ac_enaddr, - ETHER_ADDR_LEN); - ether_ifattach(ifp); -#if NBPFILTER > 0 - bpfattach(&ic->ic_rawbpf, ifp, DLT_IEEE802_11, - sizeof(struct ieee80211_frame_addr4)); -#endif - ieee80211_crc_init(); - memcpy(ic->ic_chan_active, ic->ic_chan_avail, - sizeof(ic->ic_chan_active)); - if (isclr(ic->ic_chan_active, ic->ic_ibss_chan)) { - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { - if (isset(ic->ic_chan_active, i)) { - ic->ic_ibss_chan = i; - break; - } - } - } - /* ic->ic_bss.ni_chan must always be a valid channel */ - if (isclr(ic->ic_chan_active, ic->ic_bss.ni_chan)) - ic->ic_bss.ni_chan = ic->ic_ibss_chan; - - ic->ic_des_chan = IEEE80211_CHAN_ANY; - ic->ic_fixed_rate = -1; - if (ic->ic_lintval == 0) - ic->ic_lintval = 100; /* default sleep */ - TAILQ_INIT(&ic->ic_node); - rate = 0; - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - if (ic->ic_sup_rates[i] != 0) - rate = (ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) / 2; - } - if (rate) - ifp->if_baudrate = IF_Mbps(rate); - ifp->if_hdrlen = sizeof(struct ieee80211_frame); - - if (ic->ic_max_aid == 0) - ic->ic_max_aid = IEEE80211_MAX_AID; - - /* initialize management frame handlers */ - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_BEACON - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_prreq; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_AUTH - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_auth; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_deauth; - ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_disassoc; - - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prreq; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prresp; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_AUTH - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_auth; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_deauth; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp; - ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC - >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_disassoc; - -#define ADD(s, o) ifmedia_add(&ic->ic_media, \ - IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL) - - ifmedia_init(&ic->ic_media, 0, ieee80211_media_change, - ieee80211_media_status); - ADD(IFM_AUTO, 0); /* infrastructure */ - if (ic->ic_flags & IEEE80211_F_HASAHDEMO) - ADD(IFM_AUTO, IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_flags & IEEE80211_F_HASHOSTAP) - ADD(IFM_AUTO, IFM_IEEE80211_HOSTAP); - if (ic->ic_flags & IEEE80211_F_HASIBSS) - ADD(IFM_AUTO, IFM_IEEE80211_ADHOC); - if (ic->ic_flags & IEEE80211_F_HASMONITOR) - ADD(IFM_AUTO, IFM_IEEE80211_MONITOR); - - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - rate = ic->ic_sup_rates[i]; - mword = ieee80211_rate2media(rate, ic->ic_phytype); - if (mword == 0) - continue; - ADD(mword, 0); /* infrastructure */ - if (ic->ic_flags & IEEE80211_F_HASAHDEMO) - ADD(mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_flags & IEEE80211_F_HASHOSTAP) - ADD(mword, IFM_IEEE80211_HOSTAP); - if (ic->ic_flags & IEEE80211_F_HASIBSS) - ADD(mword, IFM_IEEE80211_ADHOC); - if (ic->ic_flags & IEEE80211_F_HASMONITOR) - ADD(mword, IFM_IEEE80211_MONITOR); - } - (*ic->ic_media.ifm_status)(ifp, &imr); - ifmedia_set(&ic->ic_media, imr.ifm_active); -#undef ADD -} - -void -ieee80211_ifdetach(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - int s; - - s = splnet(); - IF_PURGE(&ic->ic_mgtq); - IF_PURGE(&ic->ic_pwrsaveq); - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; - } - ieee80211_free_allnodes(ic); - ifmedia_delete_instance(&ic->ic_media, IFM_INST_ANY); -#if NBPFILTER > 0 - bpfdetach(ifp); -#endif - ether_ifdetach(ifp); - splx(s); -} - -void -ieee80211_input(struct ifnet *ifp, struct mbuf *m, int rssi, u_int32_t rstamp) -{ - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni; - struct ieee80211_frame *wh; - struct ether_header *eh; - void (*rh)(struct ieee80211com *, struct mbuf *, int, u_int); - struct mbuf *m1; - int error, len; - u_int8_t dir, subtype; - u_int8_t *bssid; - u_int16_t rxseq; - - wh = mtod(m, struct ieee80211_frame *); - if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != - IEEE80211_FC0_VERSION_0) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: receive packet with wrong version: %x\n", - ifp->if_xname, wh->i_fc[0]); - goto err; - } - - dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; - - if (ic->ic_state != IEEE80211_S_SCAN) { - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ni = &ic->ic_bss; - if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { - DPRINTF2(("ieee80211_input: other bss %s\n", - ether_sprintf(wh->i_addr2))); - /* not interested in */ - goto out; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - if (dir == IEEE80211_FC1_DIR_NODS) - bssid = wh->i_addr3; - else - bssid = wh->i_addr1; - if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss.ni_bssid) && - !IEEE80211_ADDR_EQ(bssid, etherbroadcastaddr) && - (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != - IEEE80211_FC0_TYPE_CTL) { - /* not interested in */ - DPRINTF2(("ieee80211_input: other bss %s\n", - ether_sprintf(wh->i_addr3))); - goto out; - } - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) { - DPRINTF2(("ieee80211_input: unknown src %s\n", - ether_sprintf(wh->i_addr2))); - ni = &ic->ic_bss; /* XXX allocate? */ - } - break; - case IEEE80211_M_MONITOR: - goto out; - } - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - rxseq = ni->ni_rxseq; - ni->ni_rxseq = - letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; - /* TODO: fragment */ - if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && - rxseq == ni->ni_rxseq) { - /* duplicate, silently discarded */ - goto out; - } - ni->ni_inact = 0; - } - - if (ic->ic_set_tim != NULL && - (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) - && ni->ni_pwrsave == 0) { - /* turn on power save mode */ - - if (ifp->if_flags & IFF_DEBUG) - printf("%s: power save mode on for %s\n", - ifp->if_xname, ether_sprintf(wh->i_addr2)); - - ni->ni_pwrsave = IEEE80211_PS_SLEEP; - } - if (ic->ic_set_tim != NULL && - (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0 && - ni->ni_pwrsave != 0) { - /* turn off power save mode, dequeue stored packets */ - - ni->ni_pwrsave = 0; - if (ic->ic_set_tim) - ic->ic_set_tim(ic, ni->ni_associd, 0); - - if (ifp->if_flags & IFF_DEBUG) - printf("%s: power save mode off for %s\n", - ifp->if_xname, ether_sprintf(wh->i_addr2)); - - while (!IF_IS_EMPTY(&ni->ni_savedq)) { - struct mbuf *m; - IF_DEQUEUE(&ni->ni_savedq, m); - IF_ENQUEUE(&ic->ic_pwrsaveq, m); - (*ifp->if_start)(ifp); - } - } - - switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { - case IEEE80211_FC0_TYPE_DATA: - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - if (dir != IEEE80211_FC1_DIR_FROMDS) - goto out; - if ((ifp->if_flags & IFF_SIMPLEX) && - IEEE80211_IS_MULTICAST(wh->i_addr1) && - IEEE80211_ADDR_EQ(wh->i_addr3, - ic->ic_myaddr)) { - /* - * In IEEE802.11 network, multicast packet - * sent from me is broadcasted from AP. - * It should be silently discarded for - * SIMPLEX interface. - */ - goto out; - } - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - if (dir != IEEE80211_FC1_DIR_NODS) - goto out; - break; - case IEEE80211_M_HOSTAP: - if (dir != IEEE80211_FC1_DIR_TODS) - goto out; - /* check if source STA is associated */ - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) { - DPRINTF(("ieee80211_input: " - "data from unknown src %s\n", - ether_sprintf(wh->i_addr2))); - if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, - 1)) != NULL) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_NOT_AUTHED); - ieee80211_free_node(ic, ni); - } - goto err; - } - if (ni->ni_associd == 0) { - DPRINTF(("ieee80211_input: " - "data from unassoc src %s\n", - ether_sprintf(wh->i_addr2))); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_NOT_ASSOCED); - goto err; - } - break; - case IEEE80211_M_MONITOR: - /* Should never get here */ - break; - } - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - if (ic->ic_flags & IEEE80211_F_WEPON) { - m = ieee80211_wep_crypt(ifp, m, 0); - if (m == NULL) - goto err; - wh = mtod(m, struct ieee80211_frame *); - } else - goto out; - } - /* copy to listener after decrypt */ -#if NBPFILTER > 0 - if (ic->ic_rawbpf) - bpf_mtap(ic->ic_rawbpf, m); -#endif - m = ieee80211_decap(ifp, m); - if (m == NULL) - goto err; - ifp->if_ipackets++; - - /* perform as a bridge within the AP */ - m1 = NULL; - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - eh = mtod(m, struct ether_header *); - if (ETHER_IS_MULTICAST(eh->ether_dhost)) { - m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT); - if (m1 == NULL) - ifp->if_oerrors++; - else - m1->m_flags |= M_MCAST; - } else { - ni = ieee80211_find_node(ic, eh->ether_dhost); - if (ni != NULL && ni->ni_associd != 0) { - m1 = m; - m = NULL; - } - } - if (m1 != NULL) { - len = m1->m_pkthdr.len; - IFQ_ENQUEUE(&ifp->if_snd, m1, NULL, error); - if (error) - ifp->if_oerrors++; - else { - if (m != NULL) - ifp->if_omcasts++; - ifp->if_obytes += len; - } - } - } - if (m != NULL) { -#if NBPFILTER > 0 - /* - * If we forward packet into transmitter of the AP, - * we don't need to duplicate for DLT_EN10MB. - */ - if (ifp->if_bpf && m1 == NULL) - bpf_mtap(ifp->if_bpf, m); -#endif - ether_input_mbuf(ifp, m); - } - return; - - case IEEE80211_FC0_TYPE_MGT: - if (dir != IEEE80211_FC1_DIR_NODS) - goto err; - if (ic->ic_opmode == IEEE80211_M_AHDEMO) - goto out; - subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - /* drop frames without interest */ - if (ic->ic_state == IEEE80211_S_SCAN) { - if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && - subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) - goto out; - } else { - if (ic->ic_opmode != IEEE80211_M_IBSS && - subtype == IEEE80211_FC0_SUBTYPE_BEACON) - goto out; - } - - if (ifp->if_flags & IFF_DEBUG) { - /* avoid to print too many frames */ - int doprint = 0; - - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_BEACON: - if (ic->ic_state == IEEE80211_S_SCAN) - doprint = 1; - break; - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: - if (ic->ic_opmode == IEEE80211_M_IBSS) - doprint = 1; - break; - default: - doprint = 1; - break; - } -#ifdef IEEE80211_DEBUG - doprint += ieee80211_debug; -#endif - if (doprint) - printf("%s: received %s from %s rssi %d\n", - ifp->if_xname, - ieee80211_mgt_subtype_name[subtype - >> IEEE80211_FC0_SUBTYPE_SHIFT], - ether_sprintf(wh->i_addr2), rssi); - } -#if NBPFILTER > 0 - if (ic->ic_rawbpf) - bpf_mtap(ic->ic_rawbpf, m); -#endif - rh = ic->ic_recv_mgmt[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]; - if (rh != NULL) - (*rh)(ic, m, rssi, rstamp); - m_freem(m); - return; - - case IEEE80211_FC0_TYPE_CTL: - if (ic->ic_opmode != IEEE80211_M_HOSTAP) - goto out; - subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - if (subtype == IEEE80211_FC0_SUBTYPE_PS_POLL) { - /* Dump out a single packet from the host */ - if (ifp->if_flags & IFF_DEBUG) - printf("%s: got power save probe from %s\n", - ifp->if_xname, - ether_sprintf(wh->i_addr2)); - ieee80211_recv_pspoll(ic, m, rssi, rstamp); - } - goto out; - - default: - DPRINTF(("ieee80211_input: bad type %x\n", wh->i_fc[0])); - /* should not come here */ - break; - } - err: - ifp->if_ierrors++; - out: - if (m != NULL) { -#if NBPFILTER > 0 - if (ic->ic_rawbpf) - bpf_mtap(ic->ic_rawbpf, m); -#endif - m_freem(m); - } -} - -int -ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, - struct mbuf *m, int type) -{ - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_frame *wh; - - if (ni == NULL) - ni = &ic->ic_bss; - ni->ni_inact = 0; - M_PREPEND(m, IEEE80211_HEADER_LEN(ic), M_DONTWAIT); - if (m == NULL) - return (ENOMEM); - wh = mtod(m, struct ieee80211_frame *); - wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; - wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - *(u_int16_t *)wh->i_dur = 0; - *(u_int16_t *)wh->i_seq = - htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); - ni->ni_txseq++; - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); - - if (ifp->if_flags & IFF_DEBUG) { - /* avoid to print too many frames */ - if (ic->ic_opmode == IEEE80211_M_IBSS || -#ifdef IEEE80211_DEBUG - ieee80211_debug > 1 || -#endif - (type & IEEE80211_FC0_SUBTYPE_MASK) != - IEEE80211_FC0_SUBTYPE_PROBE_RESP) - printf("%s: sending %s to %s on channel %u\n", - ifp->if_xname, - ieee80211_mgt_subtype_name[ - (type & IEEE80211_FC0_SUBTYPE_MASK) - >> IEEE80211_FC0_SUBTYPE_SHIFT], - ether_sprintf(ni->ni_macaddr), - ni->ni_chan); - } - IF_ENQUEUE(&ic->ic_mgtq, m); - ifp->if_timer = 1; - (*ifp->if_start)(ifp); - return (0); -} - -struct mbuf * -ieee80211_encap(struct ifnet *ifp, struct mbuf *m) -{ - struct ieee80211com *ic = (void *)ifp; - struct ether_header eh; - struct ieee80211_frame *wh; - struct llc *llc; - struct ieee80211_node *ni; - - if (m->m_len < sizeof(struct ether_header)) { - m = m_pullup(m, sizeof(struct ether_header)); - if (m == NULL) - return (NULL); - } - memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); - - if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) && - (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_HOSTAP)) { - ni = ieee80211_find_node(ic, eh.ether_dhost); - if (ni == NULL) - ni = &ic->ic_bss; /*XXX*/ - } else - ni = &ic->ic_bss; - ni->ni_inact = 0; - - m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); - llc = mtod(m, struct llc *); - llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; - llc->llc_control = LLC_UI; - llc->llc_snap.org_code[0] = 0; - llc->llc_snap.org_code[1] = 0; - llc->llc_snap.org_code[2] = 0; - llc->llc_snap.ether_type = eh.ether_type; - M_PREPEND(m, IEEE80211_HEADER_LEN(ic), M_DONTWAIT); - if (m == NULL) - return (NULL); - wh = mtod(m, struct ieee80211_frame *); - wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; - *(u_int16_t *)wh->i_dur = 0; - *(u_int16_t *)wh->i_seq = - htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); - ni->ni_txseq++; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); - IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); - IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); - IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); - IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); - break; - case IEEE80211_M_HOSTAP: - wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; - IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); - IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); - IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); - break; - case IEEE80211_M_MONITOR: - /* Should never get here! */ - m_freem(m); - return (NULL); - } - return (m); -} - -struct mbuf * -ieee80211_decap(struct ifnet *ifp, struct mbuf *m) -{ - struct ether_header *eh; - struct ieee80211_frame wh; - struct llc *llc; - - if (m->m_len < sizeof(wh) + sizeof(*llc)) { - m = m_pullup(m, sizeof(wh) + sizeof(*llc)); - if (m == NULL) - return (NULL); - } - memcpy(&wh, mtod(m, caddr_t), sizeof(wh)); - llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh)); - if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && - llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && - llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { - m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh)); - llc = NULL; - } else { - m_adj(m, sizeof(wh) - sizeof(*eh)); - } - eh = mtod(m, struct ether_header *); - switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { - case IEEE80211_FC1_DIR_NODS: - IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); - IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); - break; - case IEEE80211_FC1_DIR_TODS: - IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); - IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); - break; - case IEEE80211_FC1_DIR_FROMDS: - IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); - IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); - break; - case IEEE80211_FC1_DIR_DSTODS: - /* not yet supported */ - DPRINTF(("ieee80211_decap: DS to DS\n")); - m_freem(m); - return (NULL); - } - if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { - struct mbuf *n, *n0, **np; - caddr_t newdata; - int off, pktlen; - - n0 = NULL; - np = &n0; - off = 0; - pktlen = m->m_pkthdr.len; - while (pktlen > off) { - if (n0 == NULL) { - MGETHDR(n, M_DONTWAIT, MT_DATA); - if (n == NULL) { - m_freem(m); - return (NULL); - } - M_MOVE_PKTHDR(n, m); - n->m_len = MHLEN; - } else { - MGET(n, M_DONTWAIT, MT_DATA); - if (n == NULL) { - m_freem(m); - m_freem(n0); - return (NULL); - } - n->m_len = MLEN; - } - if (pktlen - off >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - if (n0 == NULL) { - newdata = - (caddr_t)ALIGN(n->m_data + sizeof(*eh)) - - sizeof(*eh); - n->m_len -= newdata - n->m_data; - n->m_data = newdata; - } - if (n->m_len > pktlen - off) - n->m_len = pktlen - off; - m_copydata(m, off, n->m_len, mtod(n, caddr_t)); - off += n->m_len; - *np = n; - np = &n->m_next; - } - m_freem(m); - m = n0; - } - if (llc != NULL) { - eh = mtod(m, struct ether_header *); - eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); - } - return (m); -} - -int -ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct ieee80211com *ic = (void *)ifp; - struct ifreq *ifr = (struct ifreq *)data; - int i, error = 0; - struct ieee80211_nwid nwid; - struct ieee80211_nwkey *nwkey; - struct ieee80211_power *power; - struct ieee80211_bssid *bssid; - struct ieee80211_channel *chan; - struct ieee80211_wepkey keys[IEEE80211_WEP_NKID]; - static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - switch (cmd) { - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); - break; - case SIOCS80211NWID: - if ((error = copyin(ifr->ifr_data, &nwid, sizeof(nwid))) != 0) - break; - if (nwid.i_len > IEEE80211_NWID_LEN) { - error = EINVAL; - break; - } - memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); - ic->ic_des_esslen = nwid.i_len; - memcpy(ic->ic_des_essid, nwid.i_nwid, nwid.i_len); - error = ENETRESET; - break; - case SIOCG80211NWID: - memset(&nwid, 0, sizeof(nwid)); - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - nwid.i_len = ic->ic_des_esslen; - memcpy(nwid.i_nwid, ic->ic_des_essid, nwid.i_len); - break; - default: - nwid.i_len = ic->ic_bss.ni_esslen; - memcpy(nwid.i_nwid, ic->ic_bss.ni_essid, nwid.i_len); - break; - } - error = copyout(&nwid, ifr->ifr_data, sizeof(nwid)); - break; - case SIOCS80211NWKEY: - nwkey = (struct ieee80211_nwkey *)data; - if ((ic->ic_flags & IEEE80211_F_HASWEP) == 0 && - nwkey->i_wepon != IEEE80211_NWKEY_OPEN) { - error = EINVAL; - break; - } - /* check and copy keys */ - memset(keys, 0, sizeof(keys)); - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - keys[i].wk_len = nwkey->i_key[i].i_keylen; - if ((keys[i].wk_len > 0 && - keys[i].wk_len < IEEE80211_WEP_KEYLEN) || - keys[i].wk_len > sizeof(keys[i].wk_key)) { - error = EINVAL; - break; - } - if (keys[i].wk_len <= 0) - continue; - if ((error = copyin(nwkey->i_key[i].i_keydat, - keys[i].wk_key, keys[i].wk_len)) != 0) - break; - } - if (error) - break; - i = nwkey->i_defkid - 1; - if (i < 0 || i >= IEEE80211_WEP_NKID || - keys[i].wk_len == 0 || - (keys[i].wk_len == -1 && ic->ic_nw_keys[i].wk_len == 0)) { - if (nwkey->i_wepon != IEEE80211_NWKEY_OPEN) { - error = EINVAL; - break; - } - } else - ic->ic_wep_txkey = i; - /* save the key */ - if (nwkey->i_wepon == IEEE80211_NWKEY_OPEN) - ic->ic_flags &= ~IEEE80211_F_WEPON; - else - ic->ic_flags |= IEEE80211_F_WEPON; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - if (keys[i].wk_len < 0) - continue; - ic->ic_nw_keys[i].wk_len = keys[i].wk_len; - memcpy(ic->ic_nw_keys[i].wk_key, keys[i].wk_key, - sizeof(keys[i].wk_key)); - } - error = ENETRESET; - break; - case SIOCG80211NWKEY: - nwkey = (struct ieee80211_nwkey *)data; - if (ic->ic_flags & IEEE80211_F_WEPON) - nwkey->i_wepon = IEEE80211_NWKEY_WEP; - else - nwkey->i_wepon = IEEE80211_NWKEY_OPEN; - nwkey->i_defkid = ic->ic_wep_txkey + 1; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - if (nwkey->i_key[i].i_keydat == NULL) - continue; - /* do not show any keys to non-root user */ - if ((error = suser(curproc, 0)) != 0) - break; - nwkey->i_key[i].i_keylen = ic->ic_nw_keys[i].wk_len; - if ((error = copyout(ic->ic_nw_keys[i].wk_key, - nwkey->i_key[i].i_keydat, - ic->ic_nw_keys[i].wk_len)) != 0) - break; - } - break; - case SIOCS80211POWER: - power = (struct ieee80211_power *)data; - ic->ic_lintval = power->i_maxsleep; - if (power->i_enabled != 0) { - if ((ic->ic_flags & IEEE80211_F_HASPMGT) == 0) - error = EINVAL; - else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { - ic->ic_flags |= IEEE80211_F_PMGTON; - error = ENETRESET; - } - } else { - if (ic->ic_flags & IEEE80211_F_PMGTON) { - ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = ENETRESET; - } - } - break; - case SIOCG80211POWER: - power = (struct ieee80211_power *)data; - power->i_enabled = (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0; - power->i_maxsleep = ic->ic_lintval; - break; - case SIOCS80211BSSID: - bssid = (struct ieee80211_bssid *)data; - if (IEEE80211_ADDR_EQ(bssid->i_bssid, empty_macaddr)) - ic->ic_flags &= ~IEEE80211_F_DESBSSID; - else { - ic->ic_flags |= IEEE80211_F_DESBSSID; - IEEE80211_ADDR_COPY(ic->ic_des_bssid, bssid->i_bssid); - } - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - error = ENETRESET; - break; - default: - if ((ic->ic_flags & IEEE80211_F_DESBSSID) && - !IEEE80211_ADDR_EQ(ic->ic_des_bssid, - ic->ic_bss.ni_bssid)) - error = ENETRESET; - break; - } - break; - case SIOCG80211BSSID: - bssid = (struct ieee80211_bssid *)data; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - IEEE80211_ADDR_COPY(bssid->i_bssid, - ic->ic_myaddr); - else if (ic->ic_flags & IEEE80211_F_DESBSSID) - IEEE80211_ADDR_COPY(bssid->i_bssid, - ic->ic_des_bssid); - else - memset(bssid->i_bssid, 0, IEEE80211_ADDR_LEN); - break; - default: - IEEE80211_ADDR_COPY(bssid->i_bssid, - ic->ic_bss.ni_bssid); - break; - } - break; - case SIOCS80211CHANNEL: - chan = (struct ieee80211_channel *)data; - if (chan->i_channel == IEEE80211_CHAN_ANY) - ic->ic_des_chan = IEEE80211_CHAN_ANY; - else if (chan->i_channel > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, chan->i_channel)) { - error = EINVAL; - break; - } else - ic->ic_ibss_chan = ic->ic_des_chan = chan->i_channel; - switch (ic->ic_state) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - error = ENETRESET; - break; - default: - if (ic->ic_opmode == IEEE80211_M_STA) { - if (ic->ic_des_chan != IEEE80211_CHAN_ANY && - ic->ic_bss.ni_chan != ic->ic_des_chan) - error = ENETRESET; - } else { - if (ic->ic_bss.ni_chan != ic->ic_ibss_chan) - error = ENETRESET; - } - break; - } - break; - case SIOCG80211CHANNEL: - chan = (struct ieee80211_channel *)data; - chan->i_channel = ieee80211_get_channel(ic); - break; - case SIOCGWAVELAN: - error = ieee80211_cfgget(ifp, cmd, data); - break; - case SIOCSWAVELAN: - error = suser(curproc, 0); - if (error) - break; - error = ieee80211_cfgset(ifp, cmd, data); - break; - default: - error = ether_ioctl(ifp, &ic->ic_ac, cmd, data); - break; - } - return (error); -} - -void -ieee80211_print_essid(u_int8_t *essid, int len) -{ - int i; - u_int8_t *p; - - if (len > IEEE80211_NWID_LEN) - len = IEEE80211_NWID_LEN; - /* determine printable or not */ - for (i = 0, p = essid; i < len; i++, p++) { - if (*p < ' ' || *p > 0x7e) - break; - } - if (i == len) { - printf("\""); - for (i = 0, p = essid; i < len; i++, p++) - printf("%c", *p); - printf("\""); - } else { - printf("0x"); - for (i = 0, p = essid; i < len; i++, p++) - printf("%02x", *p); - } -} - -void -ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) -{ - struct ieee80211_frame *wh; - int i; - - wh = (struct ieee80211_frame *)buf; - switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { - case IEEE80211_FC1_DIR_NODS: - printf("NODS %s", ether_sprintf(wh->i_addr2)); - printf("->%s", ether_sprintf(wh->i_addr1)); - printf("(%s)", ether_sprintf(wh->i_addr3)); - break; - case IEEE80211_FC1_DIR_TODS: - printf("TODS %s", ether_sprintf(wh->i_addr2)); - printf("->%s", ether_sprintf(wh->i_addr3)); - printf("(%s)", ether_sprintf(wh->i_addr1)); - break; - case IEEE80211_FC1_DIR_FROMDS: - printf("FRDS %s", ether_sprintf(wh->i_addr3)); - printf("->%s", ether_sprintf(wh->i_addr1)); - printf("(%s)", ether_sprintf(wh->i_addr2)); - break; - case IEEE80211_FC1_DIR_DSTODS: - printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); - printf("->%s", ether_sprintf(wh->i_addr3)); - printf("(%s", ether_sprintf(wh->i_addr2)); - printf("->%s)", ether_sprintf(wh->i_addr1)); - break; - } - switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { - case IEEE80211_FC0_TYPE_DATA: - printf(" data"); - break; - case IEEE80211_FC0_TYPE_MGT: - printf(" %s", ieee80211_mgt_subtype_name[ - (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) - >> IEEE80211_FC0_SUBTYPE_SHIFT]); - break; - default: - printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); - break; - } - if (wh->i_fc[1] & IEEE80211_FC1_WEP) - printf(" WEP"); - if (rate >= 0) - printf(" %dM", rate / 2); - if (rssi >= 0) - printf(" +%d", rssi); - printf("\n"); - if (len > 0) { - for (i = 0; i < len; i++) { - if ((i & 1) == 0) - printf(" "); - printf("%02x", buf[i]); - } - printf("\n"); - } -} - -void -ieee80211_watchdog(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - if (ic->ic_mgt_timer) { - if (--ic->ic_mgt_timer == 0) - ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); - } - if (ic->ic_inact_timer) { - if (--ic->ic_inact_timer == 0) { - struct ieee80211_node *ni, *nextbs; - for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL; ) { - if (++ni->ni_inact <= IEEE80211_INACT_MAX) { - ni = TAILQ_NEXT(ni, ni_list); - continue; - } - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s deauthenticate" - " (reason %d)\n", - ifp->if_xname, - ether_sprintf(ni->ni_macaddr), - IEEE80211_REASON_AUTH_EXPIRE); - nextbs = TAILQ_NEXT(ni, ni_list); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_EXPIRE); - ieee80211_free_node(ic, ni); - ni = nextbs; - } - if (!TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - } - } - if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) - ifp->if_timer = 1; -} - -static void -ieee80211_begin_scan(struct ifnet *ifp, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = (void *)ifp; - int i; - - memcpy(ic->ic_chan_scan, ic->ic_chan_active, - sizeof(ic->ic_chan_scan)); - - DPRINTF(("ieee80211_begin_scan: scanning")); - for (i = 0; i < sizeof(ic->ic_chan_scan) * NBBY; i++) { - if (!isset(ic->ic_chan_scan, i)) - continue; - DPRINTF((" %d", i)); - ni->ni_chan = i; - } - DPRINTF(("\n")); - clrbit(ic->ic_chan_scan, ni->ni_chan); - ic->ic_flags |= IEEE80211_F_ASCAN; - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); -} - -void -ieee80211_next_scan(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - int chan, s; - - s = splnet(); - - chan = ic->ic_bss.ni_chan; - for (;;) { - chan = (chan + 1) % (IEEE80211_CHAN_MAX + 1); - if (isset(ic->ic_chan_scan, chan)) - break; - if (chan == ic->ic_bss.ni_chan) { - DPRINTF(("ieee80211_next_scan: no chan available\n")); - ieee80211_end_scan(ifp); - splx(s); - return; - } - } - clrbit(ic->ic_chan_scan, chan); - DPRINTF(("ieee80211_next_scan: chan %d->%d\n", - ic->ic_bss.ni_chan, chan)); - ic->ic_bss.ni_chan = chan; - ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); - splx(s); -} - -void -ieee80211_create_ibss(struct ieee80211com *ic) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_node *ni; - int i; - - ni = &ic->ic_bss; - if (ifp->if_flags & IFF_DEBUG) - printf("%s: creating ibss\n", ifp->if_xname); - ic->ic_flags |= IEEE80211_F_SIBSS; - ni->ni_nrate = 0; - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - if (ic->ic_sup_rates[i]) - ni->ni_rates[ni->ni_nrate++] = - ic->ic_sup_rates[i]; - } - IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); - IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); - ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ - ni->ni_esslen = ic->ic_des_esslen; - memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); - ni->ni_rssi = 0; - ni->ni_rstamp = 0; - memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); - ni->ni_intval = ic->ic_lintval; - ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; - if (ic->ic_flags & IEEE80211_F_WEPON) - ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; - ni->ni_chan = ic->ic_ibss_chan; - if (ic->ic_phytype == IEEE80211_T_FH) { - ni->ni_fhdwell = 200; /* XXX */ - ni->ni_fhindex = 1; - } - ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); -} - -int -ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) -{ - struct ifnet *ifp = &ic->ic_if; - u_int8_t rate; - int fail; - - fail = 0; - if (isclr(ic->ic_chan_active, ni->ni_chan)) - fail |= 0x01; - if (ic->ic_des_chan != IEEE80211_CHAN_ANY && - ni->ni_chan != ic->ic_des_chan) - fail |= 0x01; - if (ic->ic_opmode == IEEE80211_M_IBSS) { - if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) - fail |= 0x02; - } else { - if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) - fail |= 0x02; - } - if (ic->ic_flags & IEEE80211_F_WEPON) { - if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) - fail |= 0x04; - } else { - if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) - fail |= 0x04; - } - rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); - if (rate & IEEE80211_RATE_BASIC) - fail |= 0x08; - if (ic->ic_des_esslen != 0 && - (ni->ni_esslen != ic->ic_des_esslen || - memcmp(ni->ni_essid, ic->ic_des_essid, - ic->ic_des_esslen) != 0)) - fail |= 0x10; - if ((ic->ic_flags & IEEE80211_F_DESBSSID) && - !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) - fail |= 0x20; - if (ifp->if_flags & IFF_DEBUG) { - printf(" %c %s", fail ? '-' : '+', - ether_sprintf(ni->ni_macaddr)); - printf(" %s%c", ether_sprintf(ni->ni_bssid), - fail & 0x20 ? '!' : ' '); - printf(" %3d%c", ni->ni_chan, - fail & 0x01 ? '!' : ' '); - printf(" %+4d", ni->ni_rssi); - printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, - fail & 0x08 ? '!' : ' '); - printf(" %4s%c", - (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : - (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : - "????", - fail & 0x02 ? '!' : ' '); - printf(" %3s%c ", - (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? - "wep" : "no", - fail & 0x04 ? '!' : ' '); - ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); - printf("%s\n", fail & 0x10 ? "!" : ""); - } - return (fail); -} - -void -ieee80211_end_scan(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni, *nextbs, *selbs; - void *p; - - ni = TAILQ_FIRST(&ic->ic_node); - if (ni == NULL) { - DPRINTF(("ieee80211_end_scan: no scan candidate\n")); - notfound: - if (ic->ic_opmode == IEEE80211_M_IBSS && - (ic->ic_flags & IEEE80211_F_IBSSON) && - ic->ic_des_esslen != 0) { - ieee80211_create_ibss(ic); - return; - } - if (ic->ic_flags & IEEE80211_F_ASCAN) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: entering passive scan mode\n", - ifp->if_xname); - ic->ic_flags &= ~IEEE80211_F_ASCAN; - } - memcpy(ic->ic_chan_scan, ic->ic_chan_active, - sizeof(ic->ic_chan_active)); - ieee80211_next_scan(ifp); - return; - } - selbs = NULL; - if (ifp->if_flags & IFF_DEBUG) - printf("%s:\tmacaddr bssid chan rssi rate flag wep essid\n", - ifp->if_xname); - for (; ni != NULL; ni = nextbs) { - nextbs = TAILQ_NEXT(ni, ni_list); - if (ni->ni_fails) { - /* - * The configuration of the access points may change - * during my scan. So delete the entry for the AP - * and retry to associate if there is another beacon. - */ - if (ni->ni_fails++ > 2) - ieee80211_free_node(ic, ni); - continue; - } - if (ieee80211_match_bss(ic, ni) == 0) { - if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi) - selbs = ni; - } - } - if (selbs == NULL) - goto notfound; - p = ic->ic_bss.ni_private; - ic->ic_bss = *selbs; - ic->ic_bss.ni_private = p; - if (p != NULL && ic->ic_node_privlen) - memcpy(p, selbs->ni_private, ic->ic_node_privlen); - if (ic->ic_opmode == IEEE80211_M_IBSS) { - ieee80211_fix_rate(ic, &ic->ic_bss, IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ic->ic_bss.ni_nrate == 0) { - selbs->ni_fails++; - goto notfound; - } - ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); - } else - ieee80211_new_state(ifp, IEEE80211_S_AUTH, -1); -} - -int -ieee80211_get_rate(struct ieee80211com *ic) -{ - int rate; - if (ic->ic_fixed_rate == -1) { - if (ic->ic_state == IEEE80211_S_RUN) - rate = ic->ic_bss.ni_rates[ic->ic_bss.ni_txrate]; - else - rate = 0; - } else - rate = ic->ic_sup_rates[ic->ic_fixed_rate]; - return (rate & IEEE80211_RATE_VAL); -} - -int -ieee80211_get_channel(struct ieee80211com *ic) -{ - switch (ic->ic_state) { - case IEEE80211_S_INIT: - if (ic->ic_opmode == IEEE80211_M_STA) - return (ic->ic_des_chan); - else - return (ic->ic_ibss_chan); - case IEEE80211_S_SCAN: /* XXX could be confusing if ic_des_chan is - * set - */ - default: - return (ic->ic_bss.ni_chan); - } -} - - -struct ieee80211_node * -ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr, int copy) -{ - struct ieee80211_node *ni; - int hash; - int s; - - ni = malloc(sizeof(struct ieee80211_node) + ic->ic_node_privlen, - M_DEVBUF, M_NOWAIT); - if (ni == NULL) - return (NULL); - if (copy) - memcpy(ni, &ic->ic_bss, sizeof(struct ieee80211_node)); - else - memset(ni, 0, sizeof(struct ieee80211_node)); - IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); - if (ic->ic_node_privlen) { - ni->ni_private = &ni[1]; - memset(ni->ni_private, 0, ic->ic_node_privlen); - } else - ni->ni_private = NULL; - - hash = IEEE80211_NODE_HASH(macaddr); - s = splnet(); - TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); - LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); - splx(s); - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - return (ni); -} - -struct ieee80211_node * -ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) -{ - struct ieee80211_node *ni; - int hash; - int s; - - hash = IEEE80211_NODE_HASH(macaddr); - s = splnet(); - LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { - if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) - break; - } - splx(s); - return (ni); -} - -void -ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) -{ - int s; - - s = splnet(); - if (ic->ic_node_free != NULL) - (*ic->ic_node_free)(ic, ni); - IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); - TAILQ_REMOVE(&ic->ic_node, ni, ni_list); - LIST_REMOVE(ni, ni_hash); - if (!IF_IS_EMPTY(&ni->ni_savedq)) { - IF_PURGE(&ni->ni_savedq); - if (ic->ic_set_tim) - ic->ic_set_tim(ic, ni->ni_associd, 0); - } - splx(s); - free(ni, M_DEVBUF); - if (TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = 0; -} - -void -ieee80211_free_allnodes(struct ieee80211com *ic) -{ - struct ieee80211_node *ni; - - while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) - ieee80211_free_node(ic, ni); -} - -int -ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags) -{ - int i, j, ignore, error; - int okrate, badrate; - u_int8_t r; - - error = 0; - okrate = badrate = 0; - for (i = 0; i < ni->ni_nrate; ) { - ignore = 0; - if (flags & IEEE80211_F_DOSORT) { - for (j = i + 1; j < ni->ni_nrate; j++) { - if ((ni->ni_rates[i] & IEEE80211_RATE_VAL) > - (ni->ni_rates[j] & IEEE80211_RATE_VAL)) { - r = ni->ni_rates[i]; - ni->ni_rates[i] = ni->ni_rates[j]; - ni->ni_rates[j] = r; - } - } - } - r = ni->ni_rates[i] & IEEE80211_RATE_VAL; - badrate = r; - if (flags & IEEE80211_F_DOFRATE) { - if (ic->ic_fixed_rate >= 0 && - r != (ic->ic_sup_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL)) - ignore++; - } - if (flags & IEEE80211_F_DONEGO) { - for (j = 0; j < IEEE80211_RATE_SIZE; j++) { - if (r == - (ic->ic_sup_rates[j] & IEEE80211_RATE_VAL)) - break; - } - if (j == IEEE80211_RATE_SIZE) { - if (ni->ni_rates[i] & IEEE80211_RATE_BASIC) - error++; - ignore++; - } - } - if (flags & IEEE80211_F_DODEL) { - if (ignore) { - ni->ni_nrate--; - for (j = i; j < ni->ni_nrate; j++) - ni->ni_rates[j] = ni->ni_rates[j + 1]; - ni->ni_rates[j] = 0; - continue; - } - } - if (!ignore) - okrate = ni->ni_rates[i]; - i++; - } - if (okrate == 0 || error != 0) - return (badrate | IEEE80211_RATE_BASIC); - return (okrate & IEEE80211_RATE_VAL); -} - -static u_int8_t * -ieee80211_add_rates(u_int8_t *frm, const u_int8_t rates[IEEE80211_RATE_SIZE]) -{ - int i, j; - - *frm++ = IEEE80211_ELEMID_RATES; - j = 0; - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - if (rates[i] != 0) { - frm[1 + j] = rates[i]; - j++; - } - } - *frm++ = j; - return (frm + j); -} - -static u_int8_t * -ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) -{ - *frm++ = IEEE80211_ELEMID_SSID; - *frm++ = len; - memcpy(frm, ssid, len); - return (frm + len); -} - -void -ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m) -{ - /* Store the new packet on our queue, changing the TIM if necessary */ - - if (IF_IS_EMPTY(&ni->ni_savedq)) { - ic->ic_set_tim(ic, ni->ni_associd, 1); - } - if (ni->ni_savedq.ifq_len >= IEEE80211_PS_MAX_QUEUE) { - IF_DROP(&ni->ni_savedq); - m_freem(m); - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: station %s power save queue overflow" - " of size %d drops %d\n", - ic->ic_if.if_xname, - ether_sprintf(ni->ni_macaddr), - IEEE80211_PS_MAX_QUEUE, - ni->ni_savedq.ifq_drops); - } else { - IF_ENQUEUE(&ni->ni_savedq, m); - } -} - -static int -ieee80211_send_prreq(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int dummy) -{ - int ret; - struct mbuf *m; - u_int8_t *frm; - - /* - * prreq frame format - * [tlv] ssid - * [tlv] supported rates - */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - m->m_data += IEEE80211_HEADER_LEN(ic); - frm = mtod(m, u_int8_t *); - frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); - frm = ieee80211_add_rates(frm, ic->ic_sup_rates); - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); - - ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type); - ic->ic_mgt_timer = IEEE80211_TRANS_WAIT; - return (ret); -} - -static int -ieee80211_send_prresp(struct ieee80211com *ic, struct ieee80211_node *bs0, - int type, int dummy) -{ - struct mbuf *m; - u_int8_t *frm; - struct ieee80211_node *ni = &ic->ic_bss; - u_int16_t capinfo; - - /* - * probe response frame format - * [8] time stamp - * [2] beacon interval - * [2] cabability information - * [tlv] ssid - * [tlv] supported rates - * [tlv] parameter set (IBSS) - */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - m->m_data += IEEE80211_HEADER_LEN(ic); - frm = mtod(m, u_int8_t *); - - memset(frm, 0, 8); /* timestamp should be filled later */ - frm += 8; - *(u_int16_t *)frm = htole16(ni->ni_intval); - frm += 2; - if (ic->ic_opmode == IEEE80211_M_IBSS) - capinfo = IEEE80211_CAPINFO_IBSS; - else - capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) - capinfo |= IEEE80211_CAPINFO_PRIVACY; - *(u_int16_t *)frm = htole16(capinfo); - frm += 2; - - frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); - frm = ieee80211_add_rates(frm, ni->ni_rates); - - if (ic->ic_opmode == IEEE80211_M_IBSS) { - *frm++ = IEEE80211_ELEMID_IBSSPARMS; - *frm++ = 2; - *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ - } else { /* IEEE80211_M_HOSTAP */ - /* TODO: TIM */ - *frm++ = IEEE80211_ELEMID_TIM; - *frm++ = 4; /* length */ - *frm++ = 0; /* DTIM count */ - *frm++ = 1; /* DTIM period */ - *frm++ = 0; /* bitmap control */ - *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ - } - /* TODO: check MHLEN limit */ - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); - - return (ieee80211_mgmt_output(&ic->ic_if, bs0, m, type)); -} - -static int -ieee80211_send_auth(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int seq) -{ - struct mbuf *m; - u_int16_t *frm; - int ret; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - MH_ALIGN(m, 2 * 3); - m->m_pkthdr.len = m->m_len = 6; - frm = mtod(m, u_int16_t *); - /* TODO: shared key auth */ - frm[0] = htole16(IEEE80211_AUTH_ALG_OPEN); - frm[1] = htole16(seq); - frm[2] = 0; /* status */ - ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type); - if (ic->ic_opmode == IEEE80211_M_STA) - ic->ic_mgt_timer = IEEE80211_TRANS_WAIT; - return (ret); -} - -static int -ieee80211_send_deauth(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int reason) -{ - struct ifnet *ifp = &ic->ic_if; - struct mbuf *m; - - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s deauthenticate (reason %d)\n", - ifp->if_xname, ether_sprintf(ni->ni_macaddr), reason); - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - MH_ALIGN(m, 2); - m->m_pkthdr.len = m->m_len = 2; - *mtod(m, u_int16_t *) = htole16(reason); - return (ieee80211_mgmt_output(&ic->ic_if, ni, m, type)); -} - -static int -ieee80211_send_asreq(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int dummy) -{ - struct mbuf *m; - u_int8_t *frm; - u_int16_t capinfo; - int ret; - - /* - * asreq frame format - * [2] capability information - * [2] listen interval - * [6*] current AP address (reassoc only) - * [tlv] ssid - * [tlv] supported rates - */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - m->m_data += IEEE80211_HEADER_LEN(ic); - frm = mtod(m, u_int8_t *); - - capinfo = 0; - if (ic->ic_opmode == IEEE80211_M_IBSS) - capinfo |= IEEE80211_CAPINFO_IBSS; - else /* IEEE80211_M_STA */ - capinfo |= IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) - capinfo |= IEEE80211_CAPINFO_PRIVACY; - *(u_int16_t *)frm = htole16(capinfo); - frm += 2; - - *(u_int16_t *)frm = htole16(ic->ic_lintval); - frm += 2; - - if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { - IEEE80211_ADDR_COPY(frm, ic->ic_bss.ni_bssid); - frm += IEEE80211_ADDR_LEN; - } - - frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); - frm = ieee80211_add_rates(frm, ic->ic_sup_rates); - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); - - ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type); - ic->ic_mgt_timer = IEEE80211_TRANS_WAIT; - return (ret); -} - -static int -ieee80211_send_asresp(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int status) -{ - struct mbuf *m; - u_int8_t *frm; - u_int16_t capinfo; - - /* - * asreq frame format - * [2] capability information - * [2] status - * [2] association ID - * [tlv] supported rates - */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - m->m_data += IEEE80211_HEADER_LEN(ic); - frm = mtod(m, u_int8_t *); - - capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_WEPON) - capinfo |= IEEE80211_CAPINFO_PRIVACY; - *(u_int16_t *)frm = htole16(capinfo); - frm += 2; - - *(u_int16_t *)frm = htole16(status); - frm += 2; - - if (status == IEEE80211_STATUS_SUCCESS && ni != NULL) - *(u_int16_t *)frm = htole16(ni->ni_associd); - else - *(u_int16_t *)frm = htole16(0); - frm += 2; - - if (ni != NULL) - frm = ieee80211_add_rates(frm, ni->ni_rates); - else - frm = ieee80211_add_rates(frm, ic->ic_bss.ni_rates); - m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); - - return (ieee80211_mgmt_output(&ic->ic_if, ni, m, type)); -} - -static int -ieee80211_send_disassoc(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int reason) -{ - struct ifnet *ifp = &ic->ic_if; - struct mbuf *m; - - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s disassociate (reason %d)\n", - ifp->if_xname, ether_sprintf(ni->ni_macaddr), reason); - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOMEM); - MH_ALIGN(m, 2); - m->m_pkthdr.len = m->m_len = 2; - *mtod(m, u_int16_t *) = htole16(reason); - return (ieee80211_mgmt_output(&ic->ic_if, ni, m, - IEEE80211_FC0_SUBTYPE_DISASSOC)); -} - -/* Verify the existence and length of __elem or get out. */ -#define IEEE80211_VERIFY_ELEMENT(__subr_name, __wh, __elem, __maxlen) \ - do { \ - if (__elem == NULL) { \ - DPRINTF((#__subr_name ": no " #__elem "\n")); \ - return; \ - } \ - if (__elem[1] > __maxlen) { \ - DPRINTF((#__subr_name ": bad " #__elem \ - " len %d from %s\n", \ - __elem[1], ether_sprintf(__wh->i_addr2))); \ - return; \ - } \ - } while (0) - -static void -ieee80211_recv_beacon(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - u_int8_t *frm, *efrm, *tstamp, *bintval, *capinfo, *ssid, *rates; - u_int8_t chan, fhindex; - u_int16_t fhdwell; - - if (ic->ic_opmode != IEEE80211_M_IBSS && - ic->ic_state != IEEE80211_S_SCAN) { - /* XXX: may be useful for background scan */ - return; - } - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - /* - * beacon frame format - * [8] time stamp - * [2] beacon interval - * [2] cabability information - * [tlv] ssid - * [tlv] supported rates - * [tlv] parameter set (FH/DS) - */ - tstamp = frm; frm += 8; - bintval = frm; frm += 2; - capinfo = frm; frm += 2; - ssid = rates = NULL; - chan = ic->ic_bss.ni_chan; - fhdwell = 0; - fhindex = 0; - while (frm < efrm) { - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - case IEEE80211_ELEMID_FHPARMS: - if (ic->ic_phytype == IEEE80211_T_FH) { - fhdwell = (frm[3] << 8) | frm[2]; - chan = IEEE80211_FH_CHAN(frm[4], frm[5]); - fhindex = frm[6]; - } - break; - case IEEE80211_ELEMID_DSPARMS: - if (ic->ic_phytype == IEEE80211_T_DS) - chan = frm[2]; - break; - } - frm += frm[1] + 2; - } - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_beacon, wh, rates, - IEEE80211_RATE_SIZE); - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_beacon, wh, ssid, - IEEE80211_NWID_LEN); - ni = ieee80211_find_node(ic, wh->i_addr2); -#ifdef IEEE80211_DEBUG - if (ieee80211_debug && - (ieee80211_debug > 1 || ni == NULL || - ic->ic_state == IEEE80211_S_SCAN)) { - int is_prresp = ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == - IEEE80211_FC0_SUBTYPE_PROBE_RESP); - printf("ieee80211_recv_beacon: %s%s on chan %u (bss chan %u) ", - (ni == NULL ? "new " : ""), - is_prresp ? "probe response" : "beacon", - chan, ic->ic_bss.ni_chan); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %s\n", ether_sprintf(wh->i_addr2)); - } -#endif - if (ni == NULL) { - if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 0)) == NULL) - return; - ni->ni_esslen = ssid[1]; - memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); - memcpy(ni->ni_essid, ssid + 2, ssid[1]); - } else if (ssid[1] != 0) { - ni->ni_esslen = ssid[1]; - memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); - memcpy(ni->ni_essid, ssid + 2, ssid[1]); - } - IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); - memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE); - ni->ni_nrate = rates[1]; - memcpy(ni->ni_rates, rates + 2, ni->ni_nrate); - ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp)); - ni->ni_intval = letoh16(*(u_int16_t *)bintval); - ni->ni_capinfo = letoh16(*(u_int16_t *)capinfo); - ni->ni_chan = chan; - ni->ni_fhdwell = fhdwell; - ni->ni_fhindex = fhindex; - if (ic->ic_state == IEEE80211_S_SCAN && - (ic->ic_flags & IEEE80211_F_ASCAN) == 0) - ieee80211_end_scan(&ic->ic_if); -} - -static void -ieee80211_recv_prreq(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - u_int8_t *frm, *efrm, *ssid, *rates; - u_int8_t rate; - int allocbs; - - if (ic->ic_opmode == IEEE80211_M_STA) - return; - if (ic->ic_state != IEEE80211_S_RUN) - return; - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - /* - * prreq frame format - * [tlv] ssid - * [tlv] supported rates - */ - ssid = rates = NULL; - while (frm < efrm) { - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - } - frm += frm[1] + 2; - } - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_prreq, wh, rates, - IEEE80211_RATE_SIZE); - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_prreq, wh, ssid, - IEEE80211_NWID_LEN); - if (ssid[1] != 0 && - (ssid[1] != ic->ic_bss.ni_esslen || - memcmp(ssid + 2, ic->ic_bss.ni_essid, ic->ic_bss.ni_esslen) != 0)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("ieee80211_recv_prreq: ssid unmatch "); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %s\n", ether_sprintf(wh->i_addr2)); - } -#endif - return; - } - - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) { - if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 1)) == NULL) - return; - DPRINTF(("ieee80211_recv_prreq: new req from %s\n", - ether_sprintf(wh->i_addr2))); - allocbs = 1; - } else - allocbs = 0; - memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE); - ni->ni_nrate = rates[1]; - memcpy(ni->ni_rates, rates + 2, ni->ni_nrate); - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | - IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (rate & IEEE80211_RATE_BASIC) { - DPRINTF(("ieee80211_recv_prreq: rate negotiation failed: %s\n", - ether_sprintf(wh->i_addr2))); - } else { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, - 0); - } - if (allocbs && (ic->ic_opmode == IEEE80211_M_HOSTAP)) - ieee80211_free_node(ic, ni); -} - -static void -ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - u_int8_t *frm, *efrm; - u_int16_t algo, seq, status; - int allocbs; - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - /* - * auth frame format - * [2] algorithm - * [2] sequence - * [2] status - * [tlv*] challenge - */ - if (frm + 6 > efrm) { - DPRINTF(("ieee80211_recv_auth: too short from %s\n", - ether_sprintf(wh->i_addr2))); - return; - } - algo = letoh16(*(u_int16_t *)frm); - seq = letoh16(*(u_int16_t *)(frm + 2)); - status = letoh16(*(u_int16_t *)(frm + 4)); - if (algo != IEEE80211_AUTH_ALG_OPEN) { - /* TODO: shared key auth */ - DPRINTF(("ieee80211_recv_auth: unsupported auth %d from %s\n", - algo, ether_sprintf(wh->i_addr2))); - return; - } - switch (ic->ic_opmode) { - case IEEE80211_M_IBSS: - if (ic->ic_state != IEEE80211_S_RUN || seq != 1) - return; - ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - - case IEEE80211_M_AHDEMO: - case IEEE80211_M_MONITOR: - /* should not come here */ - break; - - case IEEE80211_M_HOSTAP: - if (ic->ic_state != IEEE80211_S_RUN || seq != 1) - return; - allocbs = 0; - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) { - ni = ieee80211_alloc_node(ic, wh->i_addr2, 0); - if (ni == NULL) - return; - IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss.ni_bssid); - allocbs = 1; - } - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s %s authenticated\n", - ifp->if_xname, - (allocbs ? "newly" : "already"), - ether_sprintf(ni->ni_macaddr)); - break; - - case IEEE80211_M_STA: - if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) - return; - if (status != 0) { - printf("%s: authentication failed (reason %d) for %s\n", - ic->ic_if.if_xname, status, - ether_sprintf(wh->i_addr3)); - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni != NULL) - ni->ni_fails++; - return; - } - ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - } -} - -static void -ieee80211_recv_asreq(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - struct ieee80211_node *ni = &ic->ic_bss; - u_int8_t *frm, *efrm, *ssid, *rates; - u_int16_t capinfo, bintval; - int reassoc, resp, newassoc; - - if (ic->ic_opmode != IEEE80211_M_HOSTAP || - (ic->ic_state != IEEE80211_S_RUN)) - return; - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == - IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { - reassoc = 1; - resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; - } else { - reassoc = 0; - resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; - } - /* - * asreq frame format - * [2] capability information - * [2] listen interval - * [6*] current AP address (reassoc only) - * [tlv] ssid - * [tlv] supported rates - */ - if (frm + (reassoc ? 10 : 4) > efrm) { - DPRINTF(("ieee80211_recv_asreq: too short from %s\n", - ether_sprintf(wh->i_addr2))); - return; - } - - if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss.ni_bssid)) { - DPRINTF(("ieee80211_recv_asreq: ignore other bss from %s\n", - ether_sprintf(wh->i_addr2))); - return; - } - capinfo = letoh16(*(u_int16_t *)frm); frm += 2; - bintval = letoh16(*(u_int16_t *)frm); frm += 2; - if (reassoc) - frm += 6; /* ignore current AP info */ - ssid = rates = NULL; - while (frm < efrm) { - switch (*frm) { - case IEEE80211_ELEMID_SSID: - ssid = frm; - break; - case IEEE80211_ELEMID_RATES: - rates = frm; - break; - } - frm += frm[1] + 2; - } - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_asreq, wh, rates, - IEEE80211_RATE_SIZE); - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_asreq, wh, ssid, - IEEE80211_NWID_LEN); - if (ssid[1] != ic->ic_bss.ni_esslen || - memcmp(ssid + 2, ic->ic_bss.ni_essid, ssid[1]) != 0) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("ieee80211_recv_asreq: ssid unmatch "); - ieee80211_print_essid(ssid + 2, ssid[1]); - printf(" from %s\n", ether_sprintf(wh->i_addr2)); - } -#endif - return; - } - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni == NULL) { - DPRINTF(("ieee80211_recv_asreq: not authenticated for %s\n", - ether_sprintf(wh->i_addr2))); - if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 1)) == NULL) - return; - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_ASSOC_NOT_AUTHED); - ieee80211_free_node(ic, ni); - return; - } - if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || - (capinfo & IEEE80211_CAPINFO_PRIVACY) != - ((ic->ic_flags & IEEE80211_F_WEPON) ? - IEEE80211_CAPINFO_PRIVACY : 0)) { - DPRINTF(("ieee80211_recv_asreq: capability unmatch %x for %s\n", - capinfo, ether_sprintf(wh->i_addr2))); - IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); - ni->ni_associd = 0; - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO); - return; - } - memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE); - ni->ni_nrate = rates[1]; - memcpy(ni->ni_rates, rates + 2, ni->ni_nrate); - ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ni->ni_nrate == 0) { - DPRINTF(("ieee80211_recv_asreq: rate unmatch for %s\n", - ether_sprintf(wh->i_addr2))); - IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); - ni->ni_associd = 0; - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE); - return; - } - ni->ni_rssi = rssi; - ni->ni_rstamp = rstamp; - ni->ni_intval = bintval; - ni->ni_capinfo = capinfo; - ni->ni_chan = ic->ic_bss.ni_chan; - ni->ni_fhdwell = ic->ic_bss.ni_fhdwell; - ni->ni_fhindex = ic->ic_bss.ni_fhindex; - if (ni->ni_associd == 0) { - u_int16_t aid; - - /* - * It would be clever to search the bitmap more efficiently, - * but this will do for now. - */ - for (aid = 1; aid < ic->ic_max_aid; aid++) { - if (!IEEE80211_AID_ISSET(aid, ic->ic_aid_bitmap)) - break; - } - - if (ic->ic_bss.ni_associd >= ic->ic_max_aid) { - IEEE80211_SEND_MGMT(ic, ni, resp, - IEEE80211_REASON_ASSOC_TOOMANY); - return; - } else { - ni->ni_associd = aid | 0xc000; - IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); - newassoc = 1; - } - } else - newassoc = 0; - IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s %s associated at aid %d\n", - ifp->if_xname, (newassoc ? "newly" : "already"), - ether_sprintf(ni->ni_macaddr), - (ni->ni_associd & ~0xc000)); -} - -static void -ieee80211_recv_asresp(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - struct ieee80211_node *ni = &ic->ic_bss; - u_int8_t *frm, *efrm, *rates; - int status; - - if (ic->ic_opmode != IEEE80211_M_STA || - ic->ic_state != IEEE80211_S_ASSOC) - return; - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - /* - * asresp frame format - * [2] capability information - * [2] status - * [2] association ID - * [tlv] supported rates - */ - if (frm + 6 > efrm) { - DPRINTF(("ieee80211_recv_asresp: too short from %s\n", - ether_sprintf(wh->i_addr2))); - return; - } - - ni->ni_capinfo = letoh16(*(u_int16_t *)frm); - frm += 2; - - status = letoh16(*(u_int16_t *)frm); - frm += 2; - if (status != 0) { - printf("%s: association failed (reason %d) for %s\n", - ifp->if_xname, status, ether_sprintf(wh->i_addr3)); - ni = ieee80211_find_node(ic, wh->i_addr2); - if (ni != NULL) - ni->ni_fails++; - return; - } - ni->ni_associd = letoh16(*(u_int16_t *)frm); - frm += 2; - rates = frm; - - IEEE80211_VERIFY_ELEMENT(ieee80211_recv_asresp, wh, rates, - IEEE80211_RATE_SIZE); - memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE); - ni->ni_nrate = rates[1]; - memcpy(ni->ni_rates, rates + 2, ni->ni_nrate); - ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | - IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ni->ni_nrate == 0) - return; - ieee80211_new_state(ifp, IEEE80211_S_RUN, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); -} - -static void -ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - u_int8_t *frm, *efrm; - u_int16_t reason; - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - /* - * disassoc frame format - * [2] reason - */ - if (frm + 2 > efrm) { - DPRINTF(("ieee80211_recv_disassoc: too short from %s\n", - ether_sprintf(wh->i_addr2))); - return; - } - reason = letoh16(*(u_int16_t *)frm); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - case IEEE80211_M_HOSTAP: - if ((ni = ieee80211_find_node(ic, wh->i_addr2)) != NULL) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s disassociated" - " by peer (reason %d)\n", ifp->if_xname, - ether_sprintf(ni->ni_macaddr), reason); - IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); - ni->ni_associd = 0; - } - break; - default: - break; - } -} - -static void -ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - u_int8_t *frm, *efrm; - u_int16_t reason; - - wh = mtod(m0, struct ieee80211_frame *); - frm = (u_int8_t *)&wh[1]; - efrm = mtod(m0, u_int8_t *) + m0->m_len; - /* - * dauth frame format - * [2] reason - */ - if (frm + 2 > efrm) { - DPRINTF(("ieee80211_recv_deauth: too short from %s\n", - ether_sprintf(wh->i_addr2))); - return; - } - reason = letoh16(*(u_int16_t *)frm); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH, - wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); - break; - case IEEE80211_M_HOSTAP: - if ((ni = ieee80211_find_node(ic, wh->i_addr2)) != NULL) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s deauthenticated" - " by peer (reason %d)\n", ifp->if_xname, - ether_sprintf(ni->ni_macaddr), reason); - ieee80211_free_node(ic, ni); - } - break; - default: - break; - } -} - - -static void -ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi, - u_int32_t rstamp) -{ - struct ifnet *ifp = &ic->ic_if; - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - struct mbuf *m; - u_int16_t aid; - - if (ic->ic_set_tim == NULL) /* No powersaving functionality */ - return; - - wh = mtod(m0, struct ieee80211_frame *); - - if ((ni = ieee80211_find_node(ic, wh->i_addr2)) == NULL) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s sent bogus power save poll\n", - ifp->if_xname, ether_sprintf(wh->i_addr2)); - return; - } - - memcpy(&aid, wh->i_dur, sizeof(wh->i_dur)); - if ((aid & 0xc000) != 0xc000) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s sent bogus aid %x\n", - ifp->if_xname, ether_sprintf(wh->i_addr2), aid); - return; - } - - if (aid != ni->ni_associd) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s aid %x doesn't match pspoll " - "aid %x\n", - ifp->if_xname, ether_sprintf(wh->i_addr2), - ni->ni_associd, aid); - return; - } - - /* Okay, take the first queued packet and put it out... */ - - IF_DEQUEUE(&ni->ni_savedq, m); - if (m == NULL) { - if (ifp->if_flags & IFF_DEBUG) - printf("%s: station %s sent pspoll, " - "but no packets are saved\n", - ifp->if_xname, ether_sprintf(wh->i_addr2)); - return; - } - wh = mtod(m, struct ieee80211_frame *); - - /* - * If this is the last packet, turn off the TIM fields. - * If there are more packets, set the more packets bit. - */ - - if (IF_IS_EMPTY(&ni->ni_savedq)) { - if (ic->ic_set_tim) - ic->ic_set_tim(ic, ni->ni_associd, 0); - } else { - wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; - } - - if (ifp->if_flags & IFF_DEBUG) - printf("%s: enqueued power saving packet for station %s\n", - ifp->if_xname, ether_sprintf(ni->ni_macaddr)); - - IF_ENQUEUE(&ic->ic_pwrsaveq, m); - (*ifp->if_start)(ifp); -} - - -int -ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt) -{ - struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni = &ic->ic_bss; - u_int8_t old_bssid[IEEE80211_ADDR_LEN]; - int i, error, ostate; -#ifdef IEEE80211_DEBUG - static const char *stname[] = - { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" }; -#endif - - ostate = ic->ic_state; - DPRINTF(("ieee80211_new_state: %s -> %s\n", - stname[ostate], stname[nstate])); - if (ic->ic_newstate) { - error = (*ic->ic_newstate)(ic->ic_softc, nstate); - if (error == EINPROGRESS) - return (0); - if (error != 0) - return (error); - } - - /* state transition */ - ic->ic_state = nstate; - switch (nstate) { - case IEEE80211_S_INIT: - switch (ostate) { - case IEEE80211_S_INIT: - break; - case IEEE80211_S_RUN: - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_ASSOC_LEAVE); - break; - case IEEE80211_M_HOSTAP: - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if (ni->ni_associd == 0) - continue; - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DISASSOC, - IEEE80211_REASON_ASSOC_LEAVE); - } - break; - default: - break; - } - /* FALLTHRU */ - case IEEE80211_S_ASSOC: - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_LEAVE); - break; - case IEEE80211_M_HOSTAP: - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_LEAVE); - } - break; - default: - break; - } - /* FALLTHRU */ - case IEEE80211_S_AUTH: - case IEEE80211_S_SCAN: - ic->ic_mgt_timer = 0; - IF_PURGE(&ic->ic_mgtq); - IF_PURGE(&ic->ic_pwrsaveq); - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; - } - ieee80211_free_allnodes(ic); - break; - } - break; - case IEEE80211_S_SCAN: - ic->ic_flags &= ~IEEE80211_F_SIBSS; - ni = &ic->ic_bss; - /* initialize bss for probe request */ - IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr); - IEEE80211_ADDR_COPY(old_bssid, ic->ic_bss.ni_bssid); - IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr); - ni->ni_nrate = 0; - memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE); - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - if (ic->ic_sup_rates[i] != 0) - ni->ni_rates[ni->ni_nrate++] = - ic->ic_sup_rates[i]; - } - ni->ni_associd = 0; - ni->ni_rstamp = 0; - switch (ostate) { - case IEEE80211_S_INIT: - /* use lowest rate */ - ni->ni_txrate = 0; - ieee80211_begin_scan(ifp, ni); - break; - case IEEE80211_S_SCAN: - /* scan next */ - if (ic->ic_flags & IEEE80211_F_ASCAN) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); - } - break; - case IEEE80211_S_RUN: - /* beacon miss */ - if (ifp->if_flags & IFF_DEBUG) - printf("%s: no recent beacons from %s;" - " rescanning\n", - ifp->if_xname, ether_sprintf(old_bssid)); - ieee80211_free_allnodes(ic); - /* FALLTHRU */ - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: - /* timeout restart scan */ - ni = ieee80211_find_node(ic, ic->ic_bss.ni_macaddr); - if (ni != NULL) - ni->ni_fails++; - ieee80211_begin_scan(ifp, &ic->ic_bss); - break; - } - break; - case IEEE80211_S_AUTH: - switch (ostate) { - case IEEE80211_S_INIT: - DPRINTF(("ieee80211_new_state: invalid transition\n")); - break; - case IEEE80211_S_SCAN: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 1); - break; - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: - switch (mgt) { - case IEEE80211_FC0_SUBTYPE_AUTH: - /* ??? */ - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 2); - break; - case IEEE80211_FC0_SUBTYPE_DEAUTH: - /* ignore and retry scan on timeout */ - break; - } - break; - case IEEE80211_S_RUN: - switch (mgt) { - case IEEE80211_FC0_SUBTYPE_AUTH: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 2); - ic->ic_state = ostate; /* stay RUN */ - break; - case IEEE80211_FC0_SUBTYPE_DEAUTH: - /* try to reauth */ - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 1); - break; - } - break; - } - break; - case IEEE80211_S_ASSOC: - switch (ostate) { - case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - case IEEE80211_S_ASSOC: - DPRINTF(("ieee80211_new_state: invalid transition\n")); - break; - case IEEE80211_S_AUTH: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); - break; - case IEEE80211_S_RUN: - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); - break; - } - break; - case IEEE80211_S_RUN: - switch (ostate) { - case IEEE80211_S_INIT: - case IEEE80211_S_AUTH: - case IEEE80211_S_RUN: - DPRINTF(("ieee80211_new_state: invalid transition\n")); - break; - case IEEE80211_S_SCAN: /* adhoc mode */ - case IEEE80211_S_ASSOC: /* infra mode */ - if (ifp->if_flags & IFF_DEBUG) { - printf("%s: ", ifp->if_xname); - if (ic->ic_opmode == IEEE80211_M_STA) - printf("associated "); - else - printf("synchronized "); - printf("with %s ssid ", - ether_sprintf(ic->ic_bss.ni_bssid)); - ieee80211_print_essid(ic->ic_bss.ni_essid, - ic->ic_bss.ni_esslen); - printf(" channel %d\n", ic->ic_bss.ni_chan); - } - /* start with highest negotiated rate */ - ic->ic_bss.ni_txrate = ic->ic_bss.ni_nrate - 1; - ic->ic_mgt_timer = 0; - (*ifp->if_start)(ifp); - break; - } - break; - } - return (0); -} - -struct mbuf * -ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag) -{ - struct ieee80211com *ic = (void *)ifp; - struct mbuf *m, *n, *n0; - struct ieee80211_frame *wh; - int i, left, len, moff, noff, kid; - u_int32_t iv, crc; - u_int8_t *ivp; - void *ctx; - u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; - u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; - - n0 = NULL; - if ((ctx = ic->ic_wep_ctx) == NULL) { - ctx = malloc(RC4STATE, M_DEVBUF, M_NOWAIT); - if (ctx == NULL) - goto fail; - ic->ic_wep_ctx = ctx; - } - m = m0; - left = m->m_pkthdr.len; - MGET(n, M_DONTWAIT, m->m_type); - n0 = n; - if (n == NULL) - goto fail; - M_MOVE_PKTHDR(n, m); - len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; - if (txflag) { - n->m_pkthdr.len += len; - } else { - n->m_pkthdr.len -= len; - left -= len; - } - n->m_len = MHLEN; - if (n->m_pkthdr.len >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - len = sizeof(struct ieee80211_frame); - memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len); - wh = mtod(n, struct ieee80211_frame *); - left -= len; - moff = len; - noff = len; - if (txflag) { - kid = ic->ic_wep_txkey; - wh->i_fc[1] |= IEEE80211_FC1_WEP; - if (!ic->ic_iv_flag) { - ic->ic_iv = arc4random(); - ic->ic_iv_flag++; - } else - iv = ic->ic_iv + 1; - /* - * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: - * (B, 255, N) with 3 <= B < 8 - */ - if (iv >= 0x03ff00 && - (iv & 0xf8ff00) == 0x00ff00) - iv += 0x000100; - ic->ic_iv = iv + 1; - /* put iv in little endian to prepare 802.11i */ - ivp = mtod(n, u_int8_t *) + noff; - for (i = 0; i < IEEE80211_WEP_IVLEN; i++) { - ivp[i] = iv & 0xff; - iv >>= 8; - } - ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */ - noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } else { - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - ivp = mtod(m, u_int8_t *) + moff; - kid = ivp[IEEE80211_WEP_IVLEN] >> 6; - moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } - memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN); - memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key, - ic->ic_nw_keys[kid].wk_len); - rc4_keysetup(ctx, keybuf, - IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len); - - /* encrypt with calculating CRC */ - crc = ~0; - while (left > 0) { - len = m->m_len - moff; - if (len == 0) { - m = m->m_next; - moff = 0; - continue; - } - if (len > n->m_len - noff) { - len = n->m_len - noff; - if (len == 0) { - MGET(n->m_next, M_DONTWAIT, n->m_type); - if (n->m_next == NULL) - goto fail; - n = n->m_next; - n->m_len = MLEN; - if (left >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - noff = 0; - continue; - } - } - if (len > left) - len = left; - rc4_crypt(ctx, mtod(n, caddr_t) + noff, - mtod(m, caddr_t) + moff, len); - if (txflag) - crc = ieee80211_crc_update(crc, - mtod(m, u_int8_t *) + moff, len); - else - crc = ieee80211_crc_update(crc, - mtod(n, u_int8_t *) + noff, len); - left -= len; - moff += len; - noff += len; - } - crc = ~crc; - if (txflag) { - *(u_int32_t *)crcbuf = htole32(crc); - if (n->m_len >= noff + sizeof(crcbuf)) - n->m_len = noff + sizeof(crcbuf); - else { - n->m_len = noff; - MGET(n->m_next, M_DONTWAIT, n->m_type); - if (n->m_next == NULL) - goto fail; - n = n->m_next; - n->m_len = sizeof(crcbuf); - noff = 0; - } - rc4_crypt(ctx, mtod(n, caddr_t) + noff, crcbuf, - sizeof(crcbuf)); - } else { - n->m_len = noff; - for (noff = 0; noff < sizeof(crcbuf); noff += len) { - len = sizeof(crcbuf) - noff; - if (len > m->m_len - moff) - len = m->m_len - moff; - if (len > 0) - rc4_crypt(ctx, crcbuf + noff, - mtod(m, caddr_t) + moff, len); - m = m->m_next; - moff = 0; - } - if (crc != letoh32(*(u_int32_t *)crcbuf)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("%s: decrypt CRC error\n", - ifp->if_xname); - if (ieee80211_debug > 1) - ieee80211_dump_pkt(n0->m_data, - n0->m_len, -1, -1); - } -#endif - goto fail; - } - } - m_freem(m0); - return (n0); - - fail: - m_freem(m0); - m_freem(n0); - return (NULL); -} - -/* - * CRC 32 -- routine from RFC 2083 - */ - -/* Table of CRCs of all 8-bit messages */ -static u_int32_t ieee80211_crc_table[256]; - -/* Make the table for a fast CRC. */ -static void -ieee80211_crc_init(void) -{ - u_int32_t c; - int n, k; - - for (n = 0; n < 256; n++) { - c = (u_int32_t)n; - for (k = 0; k < 8; k++) { - if (c & 1) - c = 0xedb88320UL ^ (c >> 1); - else - c = c >> 1; - } - ieee80211_crc_table[n] = c; - } -} - -/* - * Update a running CRC with the bytes buf[0..len-1]--the CRC - * should be initialized to all 1's, and the transmitted value - * is the 1's complement of the final running CRC - */ - -static u_int32_t -ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len) -{ - u_int8_t *endbuf; - - for (endbuf = buf + len; buf < endbuf; buf++) - crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); - return (crc); -} - -int -ieee80211_media_change(struct ifnet *ifp) -{ - struct ieee80211com *ic = (void *)ifp; - struct ifmedia_entry *ime; - enum ieee80211_opmode newmode; - int i, rate, error = 0; - - ime = ic->ic_media.ifm_cur; - if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) { - i = -1; - } else { - rate = ieee80211_media2rate(ime->ifm_media, ic->ic_phytype); - if (rate == 0) - return (EINVAL); - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - if ((ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) == rate) - break; - } - if (i == IEEE80211_RATE_SIZE) - return (EINVAL); - } - if (ic->ic_fixed_rate != i) { - ic->ic_fixed_rate = i; - error = ENETRESET; - } - - if ((ime->ifm_media & IFM_IEEE80211_ADHOC) && - (ime->ifm_media & IFM_FLAG0)) - newmode = IEEE80211_M_AHDEMO; - else if (ime->ifm_media & IFM_IEEE80211_ADHOC) - newmode = IEEE80211_M_IBSS; - else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) - newmode = IEEE80211_M_HOSTAP; - else if (ime->ifm_media & IFM_IEEE80211_MONITOR) - newmode = IEEE80211_M_MONITOR; - else - newmode = IEEE80211_M_STA; - if (ic->ic_opmode != newmode) { - ic->ic_opmode = newmode; - error = ENETRESET; - } - if (error == ENETRESET) { - if ((ifp->if_flags & IFF_UP) != 0) - error = (*ifp->if_init)(ifp); - else - error = 0; - } - if (error == 0) - ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); - - return (error); -} - -void -ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) -{ - struct ieee80211com *ic = (void *)ifp; - int rate; - - imr->ifm_status = IFM_AVALID; - if (ic->ic_state == IEEE80211_S_RUN) - imr->ifm_status |= IFM_ACTIVE; - imr->ifm_active = IFM_IEEE80211; - if (ic->ic_state == IEEE80211_S_RUN) - rate = ic->ic_bss.ni_rates[ic->ic_bss.ni_txrate] & - IEEE80211_RATE_VAL; - else { - if (ic->ic_fixed_rate == -1) - rate = 0; - else - rate = ic->ic_sup_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL; - } - imr->ifm_active |= ieee80211_rate2media(rate, ic->ic_phytype); - switch (ic->ic_opmode) { - case IEEE80211_M_AHDEMO: - imr->ifm_active |= IFM_IEEE80211_ADHOC | IFM_FLAG0; - break; - case IEEE80211_M_HOSTAP: - imr->ifm_active |= IFM_IEEE80211_HOSTAP; - break; - case IEEE80211_M_IBSS: - imr->ifm_active |= IFM_IEEE80211_ADHOC; - break; - case IEEE80211_M_MONITOR: - imr->ifm_active |= IFM_IEEE80211_MONITOR; - break; - case IEEE80211_M_STA: - break; - } -} - -/* - * convert IEEE80211 rate value to ifmedia subtype. - * ieee80211 rate is in unit of 0.5Mbps. - */ - -int -ieee80211_rate2media(int rate, enum ieee80211_phytype phytype) -{ - int mword; - - mword = 0; - switch (phytype) { - case IEEE80211_T_FH: - switch (rate & IEEE80211_RATE_VAL) { - case 0: - mword = IFM_AUTO; - break; - case 2: - mword = IFM_IEEE80211_FH1; - break; - case 4: - mword = IFM_IEEE80211_FH2; - break; - default: - mword = IFM_NONE; - break; - } - break; - - case IEEE80211_T_DS: - switch (rate & IEEE80211_RATE_VAL) { - case 0: - mword = IFM_AUTO; - break; - case 2: - mword = IFM_IEEE80211_DS1; - break; - case 4: - mword = IFM_IEEE80211_DS2; - break; - case 11: - mword = IFM_IEEE80211_DS5; - break; - case 22: - mword = IFM_IEEE80211_DS11; - break; - default: - mword = IFM_NONE; - break; - } - break; - - default: - mword = IFM_MANUAL; - break; - } - return (mword); -} - -int -ieee80211_media2rate(int mword, enum ieee80211_phytype phytype) -{ - int rate; - - rate = 0; - switch (phytype) { - case IEEE80211_T_FH: - switch (IFM_SUBTYPE(mword)) { - case IFM_IEEE80211_FH1: - rate = 2; - break; - case IFM_IEEE80211_FH2: - rate = 4; - break; - } - break; - - case IEEE80211_T_DS: - switch (IFM_SUBTYPE(mword)) { - case IFM_IEEE80211_DS1: - rate = 2; - break; - case IFM_IEEE80211_DS2: - rate = 4; - break; - case IFM_IEEE80211_DS5: - rate = 11; - break; - case IFM_IEEE80211_DS11: - rate = 22; - break; - } - break; - - default: - break; - } - return (rate); -} - -/* - * XXX - * Wireless LAN specific configuration interface, which is compatible - * with wiconfig(8). - */ - -int -ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct ieee80211com *ic = (void *)ifp; - int i, j, error; - struct ifreq *ifr = (struct ifreq *)data; - struct wi_req wreq; - struct wi_ltv_keys *keys; - struct wi_apinfo *ap; - struct ieee80211_node *ni; -#ifdef WICACHE - struct wi_sigcache wsc; -#endif /* WICACHE */ - - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return (error); - wreq.wi_len = 0; - switch (wreq.wi_type) { - case WI_RID_SERIALNO: - /* nothing appropriate */ - break; - case WI_RID_NODENAME: - strlcpy((char *)&wreq.wi_val[1], hostname, - sizeof(wreq.wi_val) - 1); - wreq.wi_val[0] = htole16(strlen(hostname)); - wreq.wi_len = (1 + strlen(hostname) + 1) / 2; - break; - case WI_RID_CURRENT_SSID: - if (ic->ic_state != IEEE80211_S_RUN) { - wreq.wi_val[0] = 0; - wreq.wi_len = 1; - break; - } - wreq.wi_val[0] = htole16(ic->ic_bss.ni_esslen); - memcpy(&wreq.wi_val[1], ic->ic_bss.ni_essid, - ic->ic_bss.ni_esslen); - wreq.wi_len = (1 + ic->ic_bss.ni_esslen + 1) / 2; - break; - case WI_RID_OWN_SSID: - case WI_RID_DESIRED_SSID: - wreq.wi_val[0] = htole16(ic->ic_des_esslen); - memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); - wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; - break; - case WI_RID_CURRENT_BSSID: - if (ic->ic_state == IEEE80211_S_RUN) - IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss.ni_bssid); - else - memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); - wreq.wi_len = IEEE80211_ADDR_LEN / 2; - break; - case WI_RID_CHANNEL_LIST: - memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { - if (isset(ic->ic_chan_active, i)) { - setbit((u_int8_t *)wreq.wi_val, j); - wreq.wi_len = j / 16 + 1; - } - } - break; - case WI_RID_OWN_CHNL: - wreq.wi_val[0] = htole16(ic->ic_ibss_chan); - wreq.wi_len = 1; - break; - case WI_RID_CURRENT_CHAN: - wreq.wi_val[0] = htole16(ic->ic_bss.ni_chan); - wreq.wi_len = 1; - break; - case WI_RID_COMMS_QUALITY: - wreq.wi_val[0] = 0; /* quality */ - wreq.wi_val[1] = htole16(ic->ic_bss.ni_rssi); /* signal */ - wreq.wi_val[2] = 0; /* noise */ - wreq.wi_len = 3; - break; - case WI_RID_PROMISC: - wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_PORTTYPE: - wreq.wi_val[0] = htole16(ic->ic_opmode); - wreq.wi_len = 1; - break; - case WI_RID_MAC_NODE: - IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); - wreq.wi_len = IEEE80211_ADDR_LEN / 2; - break; - case WI_RID_TX_RATE: - if (ic->ic_fixed_rate == -1) - wreq.wi_val[0] = 0; /* auto */ - else - wreq.wi_val[0] = htole16( - (ic->ic_sup_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL) / 2); - wreq.wi_len = 1; - break; - case WI_RID_CUR_TX_RATE: - wreq.wi_val[0] = htole16( - (ic->ic_bss.ni_rates[ic->ic_bss.ni_txrate] & - IEEE80211_RATE_VAL) / 2); - wreq.wi_len = 1; - break; - case WI_RID_RTS_THRESH: - wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: RTS */ - wreq.wi_len = 1; - break; - case WI_RID_CREATE_IBSS: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_MICROWAVE_OVEN: - wreq.wi_val[0] = 0; /* no ... not supported */ - wreq.wi_len = 1; - break; - case WI_RID_ROAMING_MODE: - wreq.wi_val[0] = htole16(1); /* enabled ... not supported */ - wreq.wi_len = 1; - break; - case WI_RID_SYSTEM_SCALE: - wreq.wi_val[0] = htole16(1); /* low density ... not supp */ - wreq.wi_len = 1; - break; - case WI_RID_PM_ENABLED: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_MAX_SLEEP: - wreq.wi_val[0] = htole16(ic->ic_lintval); - wreq.wi_len = 1; - break; - case WI_RID_CUR_BEACON_INT: - wreq.wi_val[0] = htole16(ic->ic_bss.ni_intval); - wreq.wi_len = 1; - break; - case WI_RID_WEP_AVAIL: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_HASWEP) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_CNFAUTHMODE: - wreq.wi_val[0] = htole16(1); /* TODO: open system only */ - wreq.wi_len = 1; - break; - case WI_RID_ENCRYPTION: - wreq.wi_val[0] = - htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0); - wreq.wi_len = 1; - break; - case WI_RID_TX_CRYPT_KEY: - wreq.wi_val[0] = htole16(ic->ic_wep_txkey); - wreq.wi_len = 1; - break; - case WI_RID_DEFLT_CRYPT_KEYS: - keys = (struct wi_ltv_keys *)&wreq; - /* do not show keys to non-root user */ - error = suser(curproc, 0); - if (error) { - memset(keys, 0, sizeof(*keys)); - error = 0; - break; - } - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - keys->wi_keys[i].wi_keylen = - htole16(ic->ic_nw_keys[i].wk_len); - memcpy(keys->wi_keys[i].wi_keydat, - ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); - } - wreq.wi_len = sizeof(*keys) / 2; - break; - case WI_RID_MAX_DATALEN: - wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ - wreq.wi_len = 1; - break; - case WI_RID_IFACE_STATS: - /* XXX: should be implemented in lower drivers */ - break; - case WI_RID_READ_APS: - if (ic->ic_opmode != IEEE80211_M_HOSTAP) { - for (i = 0; i < IEEE80211_PSCAN_WAIT; i++) { - tsleep((caddr_t)ic, PWAIT | PCATCH, "i80211", - hz); - if (ic->ic_state != IEEE80211_S_SCAN || - (ic->ic_flags & IEEE80211_F_SCANAP) == 0 || - (ic->ic_flags & IEEE80211_F_ASCAN) == 0) - break; - } - ic->ic_flags &= ~IEEE80211_F_SCANAP; - memcpy(ic->ic_chan_active, ic->ic_chan_avail, - sizeof(ic->ic_chan_active)); - } - i = 0; - ap = (void *)((char *)wreq.wi_val + sizeof(i)); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) - break; - memset(ap, 0, sizeof(*ap)); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); - ap->namelen = ic->ic_des_esslen; - if (ic->ic_des_esslen) - memcpy(ap->name, ic->ic_des_essid, - ic->ic_des_esslen); - } else { - IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); - ap->namelen = ni->ni_esslen; - if (ni->ni_esslen) - memcpy(ap->name, ni->ni_essid, - ni->ni_esslen); - } - ap->channel = ni->ni_chan; - ap->signal = ni->ni_rssi; - ap->capinfo = ni->ni_capinfo; - ap->interval = ni->ni_intval; - for (j = 0; j < ni->ni_nrate; j++) { - if (ni->ni_rates[j] & IEEE80211_RATE_BASIC) { - ap->rate = (ni->ni_rates[j] & - IEEE80211_RATE_VAL) * 5; /* XXX */ - } - } - i++; - ap++; - } - memcpy(wreq.wi_val, &i, sizeof(i)); - wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; - break; -#ifdef WICACHE - case WI_RID_READ_CACHE: - i = 0; - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { - if (i == MAXWICACHE) - break; - IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); - memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); - wsc.signal = ni->ni_rssi; - wsc.noise = 0; - wsc.quality = 0; - memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, - &wsc, sizeof(wsc)); - i++; - } - wreq.wi_len = sizeof(wsc) * i / 2; - break; -#endif /* WICACHE */ - case WI_RID_SCAN_APS: - error = EINVAL; - break; - default: - error = EINVAL; - break; - } - if (error == 0) { - wreq.wi_len++; - error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); - } - return (error); -} - -int -ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct ieee80211com *ic = (void *)ifp; - int i, j, len, error; - struct ifreq *ifr = (struct ifreq *)data; - struct wi_ltv_keys *keys; - struct wi_req wreq; - u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; - - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return (error); - if (wreq.wi_len-- < 1) - return (EINVAL); - switch (wreq.wi_type) { - case WI_RID_SERIALNO: - case WI_RID_NODENAME: - return (EPERM); - case WI_RID_CURRENT_SSID: - return (EPERM); - case WI_RID_OWN_SSID: - case WI_RID_DESIRED_SSID: - len = letoh16(wreq.wi_val[0]); - if (wreq.wi_len < (1 + len + 1) / 2) - return (EINVAL); - if (len > IEEE80211_NWID_LEN) - return (EINVAL); - ic->ic_des_esslen = len; - memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); - memcpy(ic->ic_des_essid, &wreq.wi_val[1], len); - error = ENETRESET; - break; - case WI_RID_CURRENT_BSSID: - return (EPERM); - case WI_RID_OWN_CHNL: - if (wreq.wi_len != 1) - return (EINVAL); - i = letoh16(wreq.wi_val[0]); - if (i < 0 || - i > IEEE80211_CHAN_MAX || - isclr(ic->ic_chan_active, i)) - return (EINVAL); - ic->ic_ibss_chan = i; - if (ic->ic_flags & IEEE80211_F_SIBSS) - error = ENETRESET; - break; - case WI_RID_CURRENT_CHAN: - return (EPERM); - case WI_RID_COMMS_QUALITY: - return (EPERM); - case WI_RID_PROMISC: - if (wreq.wi_len != 1) - return (EINVAL); - if (ifp->if_flags & IFF_PROMISC) { - if (wreq.wi_val[0] == 0) { - ifp->if_flags &= ~IFF_PROMISC; - error = ENETRESET; - } - } else { - if (wreq.wi_val[0] != 0) { - ifp->if_flags |= IFF_PROMISC; - error = ENETRESET; - } - } - break; - case WI_RID_PORTTYPE: - if (wreq.wi_len != 1) - return (EINVAL); - switch (letoh16(wreq.wi_val[0])) { - case IEEE80211_M_STA: - break; - case IEEE80211_M_IBSS: - if (!(ic->ic_flags & IEEE80211_F_HASIBSS)) - return (EINVAL); - break; - case IEEE80211_M_AHDEMO: - if (ic->ic_phytype != IEEE80211_T_DS || - !(ic->ic_flags & IEEE80211_F_HASAHDEMO)) - return (EINVAL); - break; - case IEEE80211_M_HOSTAP: - if (!(ic->ic_flags & IEEE80211_F_HASHOSTAP)) - return (EINVAL); - break; - default: - return (EINVAL); - } - if (letoh16(wreq.wi_val[0]) != ic->ic_opmode) { - ic->ic_opmode = letoh16(wreq.wi_val[0]); - error = ENETRESET; - } - break; - case WI_RID_MAC_NODE: - if (wreq.wi_len != IEEE80211_ADDR_LEN / 2) - return (EINVAL); - IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); - /* if_init will copy lladdr into ic_myaddr */ - error = ENETRESET; - break; - case WI_RID_TX_RATE: - if (wreq.wi_len != 1) - return (EINVAL); - if (wreq.wi_val[0] == 0) { - /* auto */ - ic->ic_fixed_rate = -1; - break; - } - for (i = 0; i < IEEE80211_RATE_SIZE; i++) { - if (letoh16(wreq.wi_val[0]) == - (ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) / 2) - break; - } - if (i == IEEE80211_RATE_SIZE) - return (EINVAL); - ic->ic_fixed_rate = i; - error = ENETRESET; - break; - case WI_RID_CUR_TX_RATE: - return (EPERM); - case WI_RID_RTS_THRESH: - if (wreq.wi_len != 1) - return (EINVAL); - if (letoh16(wreq.wi_val[0]) != IEEE80211_MAX_LEN) - return (EINVAL); /* TODO: RTS */ - break; - case WI_RID_CREATE_IBSS: - if (wreq.wi_len != 1) - return (EINVAL); - if (wreq.wi_val[0] != 0) { - if ((ic->ic_flags & IEEE80211_F_HASIBSS) == 0) - return (EINVAL); - if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { - ic->ic_flags |= IEEE80211_F_IBSSON; - if (ic->ic_opmode == IEEE80211_M_IBSS && - ic->ic_state == IEEE80211_S_SCAN) - error = ENETRESET; - } - } else { - if (ic->ic_flags & IEEE80211_F_IBSSON) { - ic->ic_flags &= ~IEEE80211_F_IBSSON; - if (ic->ic_flags & IEEE80211_F_SIBSS) { - ic->ic_flags &= ~IEEE80211_F_SIBSS; - error = ENETRESET; - } - } - } - break; - case WI_RID_MICROWAVE_OVEN: - if (wreq.wi_len != 1) - return (EINVAL); - if (wreq.wi_val[0] != 0) - return (EINVAL); /* not supported */ - break; - case WI_RID_ROAMING_MODE: - if (wreq.wi_len != 1) - return EINVAL; - if (letoh16(wreq.wi_val[0]) != 1) - return (EINVAL); /* not supported */ - break; - case WI_RID_SYSTEM_SCALE: - if (wreq.wi_len != 1) - return (EINVAL); - if (letoh16(wreq.wi_val[0]) != 1) - return (EINVAL); /* not supported */ - break; - case WI_RID_PM_ENABLED: - if (wreq.wi_len != 1) - return (EINVAL); - if (wreq.wi_val[0] != 0) { - if ((ic->ic_flags & IEEE80211_F_HASPMGT) == 0) - return (EINVAL); - if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { - ic->ic_flags |= IEEE80211_F_PMGTON; - error = ENETRESET; - } - } else { - if (ic->ic_flags & IEEE80211_F_PMGTON) { - ic->ic_flags &= ~IEEE80211_F_PMGTON; - error = ENETRESET; - } - } - break; - case WI_RID_MAX_SLEEP: - if (wreq.wi_len != 1) - return (EINVAL); - ic->ic_lintval = letoh16(wreq.wi_val[0]); - if (ic->ic_flags & IEEE80211_F_PMGTON) - error = ENETRESET; - break; - case WI_RID_CUR_BEACON_INT: - return (EPERM); - case WI_RID_WEP_AVAIL: - return (EPERM); - case WI_RID_CNFAUTHMODE: - if (wreq.wi_len != 1) - return (EINVAL); - if (letoh16(wreq.wi_val[0]) != 1) - return (EINVAL); /* TODO: shared key auth */ - break; - case WI_RID_ENCRYPTION: - if (wreq.wi_len != 1) - return (EINVAL); - if (wreq.wi_val[0] != 0) { - if ((ic->ic_flags & IEEE80211_F_HASWEP) == 0) - return (EINVAL); - if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { - ic->ic_flags |= IEEE80211_F_WEPON; - error = ENETRESET; - } - } else { - if (ic->ic_flags & IEEE80211_F_WEPON) { - ic->ic_flags &= ~IEEE80211_F_WEPON; - error = ENETRESET; - } - } - break; - case WI_RID_TX_CRYPT_KEY: - if (wreq.wi_len != 1) - return (EINVAL); - i = letoh16(wreq.wi_val[0]); - if (i >= IEEE80211_WEP_NKID) - return (EINVAL); - ic->ic_wep_txkey = i; - break; - case WI_RID_DEFLT_CRYPT_KEYS: - if (wreq.wi_len != sizeof(struct wi_ltv_keys) / 2) - return (EINVAL); - keys = (struct wi_ltv_keys *)&wreq; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - len = letoh16(keys->wi_keys[i].wi_keylen); - if (len != 0 && len < IEEE80211_WEP_KEYLEN) - return (EINVAL); - if (len > sizeof(ic->ic_nw_keys[i].wk_key)) - return (EINVAL); - } - memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - len = letoh16(keys->wi_keys[i].wi_keylen); - ic->ic_nw_keys[i].wk_len = len; - memcpy(ic->ic_nw_keys[i].wk_key, - keys->wi_keys[i].wi_keydat, len); - } - error = ENETRESET; - break; - case WI_RID_MAX_DATALEN: - if (wreq.wi_len != 1) - return (EINVAL); - len = letoh16(wreq.wi_val[0]); - if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) - return (EINVAL); - if (len != IEEE80211_MAX_LEN) - return (EINVAL); /* TODO: fragment */ - break; - case WI_RID_IFACE_STATS: - error = EPERM; - break; - case WI_RID_SCAN_APS: - if (ic->ic_opmode == IEEE80211_M_HOSTAP) - break; - wreq.wi_len -= 2; /* XXX: tx rate? */ - /* FALLTHRU */ - case WI_RID_CHANNEL_LIST: - memset(chanlist, 0, sizeof(chanlist)); - /* - * Since channel 0 is not available for DS, channel 1 - * is assigned to LSB on WaveLAN. - */ - if (ic->ic_phytype == IEEE80211_T_DS) - i = 1; - else - i = 0; - for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { - if (j / 16 >= wreq.wi_len) - break; - if (isclr((u_int8_t *)wreq.wi_val, j)) - continue; - if (isclr(ic->ic_chan_avail, i)) { - if (wreq.wi_type == WI_RID_CHANNEL_LIST) - error = EPERM; - else - continue; - } - setbit(chanlist, i); - } - if (error == EPERM && ic->ic_chancheck != NULL) - error = (*ic->ic_chancheck)(ic->ic_softc, chanlist); - if (error) - return (error); - memcpy(ic->ic_chan_active, chanlist, - sizeof(ic->ic_chan_active)); - if (isclr(chanlist, ic->ic_ibss_chan)) { - for (i = 0; i <= IEEE80211_CHAN_MAX; i++) - if (isset(chanlist, i)) { - ic->ic_ibss_chan = i; - break; - } - } - if (isclr(chanlist, ic->ic_bss.ni_chan)) - ic->ic_bss.ni_chan = ic->ic_ibss_chan; - if (wreq.wi_type == WI_RID_CHANNEL_LIST) - error = ENETRESET; - else { - ic->ic_flags |= IEEE80211_F_SCANAP; - error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1); - } - break; - default: - error = EINVAL; - break; - } - return (error); -} diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c new file mode 100644 index 00000000000..df3887bcbb5 --- /dev/null +++ b/sys/net80211/ieee80211.c @@ -0,0 +1,1051 @@ +/* $OpenBSD: ieee80211.c,v 1.1 2004/06/22 22:53:51 millert Exp $ */ +/* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.11 2004/04/02 20:19:20 sam Exp $"); +#elif defined (__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $"); +#endif + +/* + * IEEE 802.11 generic handler + */ + +#ifdef __NetBSD__ +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined( __FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +#ifdef IEEE80211_DEBUG +int ieee80211_debug = 0; +#ifdef __NetBSD__ +static int ieee80211_debug_nodenum; +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug, + 0, "IEEE 802.11 media debugging printfs"); +#endif +#endif + +int ieee80211_inact_max = IEEE80211_INACT_MAX; +#ifdef __NetBSD__ +static int ieee80211_inact_max_nodenum; +#endif + +static void ieee80211_set11gbasicrates(struct ieee80211_rateset *, + enum ieee80211_phymode); + +#if 0 +static const char *ieee80211_phymode_name[] = { + "auto", /* IEEE80211_MODE_AUTO */ + "11a", /* IEEE80211_MODE_11A */ + "11b", /* IEEE80211_MODE_11B */ + "11g", /* IEEE80211_MODE_11G */ + "FH", /* IEEE80211_MODE_FH */ + "turbo", /* IEEE80211_MODE_TURBO */ +}; +#endif + +void +ieee80211_ifattach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_channel *c; + int i; + +#if defined(__OpenBSD__) + memcpy(((struct arpcom *)ifp)->ac_enaddr, ic->ic_myaddr, + ETHER_ADDR_LEN); + ether_ifattach(ifp); +#else + ether_ifattach(ifp, ic->ic_myaddr); +#endif +#if NBPFILTER > 0 + BPF_ATTACH(ifp, DLT_IEEE802_11, + sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); +#endif + ieee80211_crypto_attach(ifp); + + /* + * Fill in 802.11 available channel set, mark + * all available channels as active, and pick + * a default channel if not already specified. + */ + memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); + ic->ic_modecaps |= 1<<IEEE80211_MODE_AUTO; + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + c = &ic->ic_channels[i]; + if (c->ic_flags) { + /* + * Verify driver passed us valid data. + */ + if (i != ieee80211_chan2ieee(ic, c)) { + if_printf(ifp, "bad channel ignored; " + "freq %u flags %x number %u\n", + c->ic_freq, c->ic_flags, i); + c->ic_flags = 0; /* NB: remove */ + continue; + } + setbit(ic->ic_chan_avail, i); + /* + * Identify mode capabilities. + */ + if (IEEE80211_IS_CHAN_A(c)) + ic->ic_modecaps |= 1<<IEEE80211_MODE_11A; + if (IEEE80211_IS_CHAN_B(c)) + ic->ic_modecaps |= 1<<IEEE80211_MODE_11B; + if (IEEE80211_IS_CHAN_PUREG(c)) + ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; + if (IEEE80211_IS_CHAN_FHSS(c)) + ic->ic_modecaps |= 1<<IEEE80211_MODE_FH; + if (IEEE80211_IS_CHAN_T(c)) + ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO; + } + } + /* validate ic->ic_curmode */ + if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0) + ic->ic_curmode = IEEE80211_MODE_AUTO; + ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + + (void) ieee80211_setmode(ic, ic->ic_curmode); + + if (ic->ic_lintval == 0) + ic->ic_lintval = 100; /* default sleep */ + ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ + + ieee80211_node_attach(ifp); + ieee80211_proto_attach(ifp); +} + +void +ieee80211_ifdetach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + ieee80211_proto_detach(ifp); + ieee80211_crypto_detach(ifp); + ieee80211_node_detach(ifp); +#ifdef __FreeBSD__ + ifmedia_removeall(&ic->ic_media); +#else + ifmedia_delete_instance(&ic->ic_media, IFM_INST_ANY); +#endif +#if NBPFILTER > 0 + BPF_DETACH(ifp); +#endif + ether_ifdetach(ifp); +} + +/* + * Convert MHz frequency to IEEE channel number. + */ +u_int +ieee80211_mhz2ieee(u_int freq, u_int flags) +{ + if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + else + return 15 + ((freq - 2512) / 20); + } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ + return (freq - 5000) / 5; + } else { /* either, guess */ + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + if (freq < 5000) + return 15 + ((freq - 2512) / 20); + return (freq - 5000) / 5; + } +} + +/* + * Convert channel to IEEE channel number. + */ +u_int +ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) + return c - ic->ic_channels; + else if (c == IEEE80211_CHAN_ANYC) + return IEEE80211_CHAN_ANY; + else if (c != NULL) { + if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n", + c->ic_freq, c->ic_flags); + return 0; /* XXX */ + } else { + if_printf(&ic->ic_if, "invalid channel (NULL)\n"); + return 0; /* XXX */ + } +} + +/* + * Convert IEEE channel number to MHz frequency. + */ +u_int +ieee80211_ieee2mhz(u_int chan, u_int flags) +{ + if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ + if (chan == 14) + return 2484; + if (chan < 14) + return 2407 + chan*5; + else + return 2512 + ((chan-15)*20); + } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ + return 5000 + (chan*5); + } else { /* either, guess */ + if (chan == 14) + return 2484; + if (chan < 14) /* 0-13 */ + return 2407 + chan*5; + if (chan < 27) /* 15-26 */ + return 2512 + ((chan-15)*20); + return 5000 + (chan*5); + } +} + +/* + * Setup the media data structures according to the channel and + * rate tables. This must be called by the driver after + * ieee80211_attach and before most anything else. + */ +void +ieee80211_media_init(struct ifnet *ifp, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ +#define ADD(_ic, _s, _o) \ + ifmedia_add(&(_ic)->ic_media, \ + IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) + struct ieee80211com *ic = (void *)ifp; + struct ifmediareq imr; + int i, j, mode, rate, maxrate, mword, mopt, r; + struct ieee80211_rateset *rs; + struct ieee80211_rateset allrates; + + /* + * Do late attach work that must wait for any subclass + * (i.e. driver) work such as overriding methods. + */ + ieee80211_node_lateattach(ifp); + + /* + * Fill in media characteristics. + */ + ifmedia_init(&ic->ic_media, 0, media_change, media_stat); + maxrate = 0; + memset(&allrates, 0, sizeof(allrates)); + for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { + static const u_int mopts[] = { + IFM_AUTO, + IFM_IEEE80211_11A, + IFM_IEEE80211_11B, + IFM_IEEE80211_11G, + IFM_IEEE80211_FH, + IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + }; + if ((ic->ic_modecaps & (1<<mode)) == 0) + continue; + mopt = mopts[mode]; + ADD(ic, IFM_AUTO, mopt); /* e.g. 11a auto */ + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); + if (mode == IEEE80211_MODE_AUTO) + continue; + rs = &ic->ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + mword = ieee80211_rate2media(ic, rate, mode); + if (mword == 0) + continue; + ADD(ic, mword, mopt); + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); + /* + * Add rate to the collection of all rates. + */ + r = rate & IEEE80211_RATE_VAL; + for (j = 0; j < allrates.rs_nrates; j++) + if (allrates.rs_rates[j] == r) + break; + if (j == allrates.rs_nrates) { + /* unique, add to the set */ + allrates.rs_rates[j] = r; + allrates.rs_nrates++; + } + rate = (rate & IEEE80211_RATE_VAL) / 2; + if (rate > maxrate) + maxrate = rate; + } + } + for (i = 0; i < allrates.rs_nrates; i++) { + mword = ieee80211_rate2media(ic, allrates.rs_rates[i], + IEEE80211_MODE_AUTO); + if (mword == 0) + continue; + mword = IFM_SUBTYPE(mword); /* remove media options */ + ADD(ic, mword, 0); + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, mword, IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, mword, IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, mword, IFM_IEEE80211_MONITOR); + } + ieee80211_media_status(ifp, &imr); + ifmedia_set(&ic->ic_media, imr.ifm_active); + + if (maxrate) + ifp->if_baudrate = IF_Mbps(maxrate); + + if (ic->ic_max_aid == 0) + ic->ic_max_aid = IEEE80211_MAX_AID; + +#undef ADD +} + +static int +findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +{ +#define IEEERATE(_ic,_m,_i) \ + ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) + int i, nrates = ic->ic_sup_rates[mode].rs_nrates; + for (i = 0; i < nrates; i++) + if (IEEERATE(ic, mode, i) == rate) + return i; + return -1; +#undef IEEERATE +} + +/* + * Handle a media change request. + */ +int +ieee80211_media_change(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + struct ifmedia_entry *ime; + enum ieee80211_opmode newopmode; + enum ieee80211_phymode newphymode; + int i, j, newrate, error = 0; + + ime = ic->ic_media.ifm_cur; + /* + * First, identify the phy mode. + */ + switch (IFM_MODE(ime->ifm_media)) { + case IFM_IEEE80211_11A: + newphymode = IEEE80211_MODE_11A; + break; + case IFM_IEEE80211_11B: + newphymode = IEEE80211_MODE_11B; + break; + case IFM_IEEE80211_11G: + newphymode = IEEE80211_MODE_11G; + break; + case IFM_IEEE80211_FH: + newphymode = IEEE80211_MODE_FH; + break; + case IFM_AUTO: + newphymode = IEEE80211_MODE_AUTO; + break; + default: + return EINVAL; + } + /* + * Turbo mode is an ``option''. Eventually it + * needs to be applied to 11g too. + */ + if (ime->ifm_media & IFM_IEEE80211_TURBO) { + if (newphymode != IEEE80211_MODE_11A) + return EINVAL; + newphymode = IEEE80211_MODE_TURBO; + } + /* + * Validate requested mode is available. + */ + if ((ic->ic_modecaps & (1<<newphymode)) == 0) + return EINVAL; + + /* + * Next, the fixed/variable rate. + */ + i = -1; + if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { + /* + * Convert media subtype to rate. + */ + newrate = ieee80211_media2rate(ime->ifm_media); + if (newrate == 0) + return EINVAL; + /* + * Check the rate table for the specified/current phy. + */ + if (newphymode == IEEE80211_MODE_AUTO) { + /* + * In autoselect mode search for the rate. + */ + for (j = IEEE80211_MODE_11A; + j < IEEE80211_MODE_MAX; j++) { + if ((ic->ic_modecaps & (1<<j)) == 0) + continue; + i = findrate(ic, j, newrate); + if (i != -1) { + /* lock mode too */ + newphymode = j; + break; + } + } + } else { + i = findrate(ic, newphymode, newrate); + } + if (i == -1) /* mode/rate mismatch */ + return EINVAL; + } + /* NB: defer rate setting to later */ + + /* + * Deduce new operating mode but don't install it just yet. + */ + if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == + (IFM_IEEE80211_ADHOC|IFM_FLAG0)) + newopmode = IEEE80211_M_AHDEMO; + else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) + newopmode = IEEE80211_M_HOSTAP; + else if (ime->ifm_media & IFM_IEEE80211_ADHOC) + newopmode = IEEE80211_M_IBSS; + else if (ime->ifm_media & IFM_IEEE80211_MONITOR) + newopmode = IEEE80211_M_MONITOR; + else + newopmode = IEEE80211_M_STA; + + /* + * Autoselect doesn't make sense when operating as an AP. + * If no phy mode has been selected, pick one and lock it + * down so rate tables can be used in forming beacon frames + * and the like. + */ + if (newopmode == IEEE80211_M_HOSTAP && + newphymode == IEEE80211_MODE_AUTO) { + for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) + if (ic->ic_modecaps & (1<<j)) { + newphymode = j; + break; + } + } + + /* + * Handle phy mode change. + */ + if (ic->ic_curmode != newphymode) { /* change phy mode */ + error = ieee80211_setmode(ic, newphymode); + if (error != 0) + return error; + error = ENETRESET; + } + + /* + * Committed to changes, install the rate setting. + */ + if (ic->ic_fixed_rate != i) { + ic->ic_fixed_rate = i; /* set fixed tx rate */ + error = ENETRESET; + } + + /* + * Handle operating mode change. + */ + if (ic->ic_opmode != newopmode) { + ic->ic_opmode = newopmode; + switch (newopmode) { + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_STA: + case IEEE80211_M_MONITOR: + ic->ic_flags &= ~IEEE80211_F_IBSSON; + break; + case IEEE80211_M_IBSS: + ic->ic_flags |= IEEE80211_F_IBSSON; +#ifdef notdef + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_set11gbasicrates( + &ic->ic_sup_rates[newphymode], + IEEE80211_MODE_11B); +#endif + break; + } + error = ENETRESET; + } +#ifdef notdef + if (error == 0) + ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); +#endif + return error; +} + +void +ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_node *ni = NULL; + + imr->ifm_status = IFM_AVALID; + imr->ifm_active = IFM_IEEE80211; + if (ic->ic_state == IEEE80211_S_RUN) + imr->ifm_status |= IFM_ACTIVE; + imr->ifm_active |= IFM_AUTO; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + ni = ic->ic_bss; + /* calculate rate subtype */ + imr->ifm_active |= ieee80211_rate2media(ic, + ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); + break; + case IEEE80211_M_IBSS: + imr->ifm_active |= IFM_IEEE80211_ADHOC; + break; + case IEEE80211_M_AHDEMO: + /* should not come here */ + break; + case IEEE80211_M_HOSTAP: + imr->ifm_active |= IFM_IEEE80211_HOSTAP; + break; + case IEEE80211_M_MONITOR: + imr->ifm_active |= IFM_IEEE80211_MONITOR; + break; + } + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + imr->ifm_active |= IFM_IEEE80211_11A; + break; + case IEEE80211_MODE_11B: + imr->ifm_active |= IFM_IEEE80211_11B; + break; + case IEEE80211_MODE_11G: + imr->ifm_active |= IFM_IEEE80211_11G; + break; + case IEEE80211_MODE_FH: + imr->ifm_active |= IFM_IEEE80211_FH; + break; + case IEEE80211_MODE_TURBO: + imr->ifm_active |= IFM_IEEE80211_11A + | IFM_IEEE80211_TURBO; + break; + } +} + +void +ieee80211_watchdog(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) + ieee80211_timeout_nodes(ic); + + if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) + ifp->if_timer = 1; +} + +/* + * Mark the basic rates for the 11g rate table based on the + * operating mode. For real 11g we mark all the 11b rates + * and 6, 12, and 24 OFDM. For 11b compatibility we mark only + * 11b rates. There's also a pseudo 11a-mode used to mark only + * the basic OFDM rates. + */ +static void +ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) +{ + static const struct ieee80211_rateset basic[] = { + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ + { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */ + { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */ + { 0 }, /* IEEE80211_MODE_FH */ + { 0 }, /* IEEE80211_MODE_TURBO */ + }; + int i, j; + + for (i = 0; i < rs->rs_nrates; i++) { + rs->rs_rates[i] &= IEEE80211_RATE_VAL; + for (j = 0; j < basic[mode].rs_nrates; j++) + if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { + rs->rs_rates[i] |= IEEE80211_RATE_BASIC; + break; + } + } +} + +/* + * Set the current phy mode and recalculate the active channel + * set based on the available channels for this mode. Also + * select a new default/current channel if the current one is + * inappropriate for this mode. + */ +int +ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const u_int chanflags[] = { + 0, /* IEEE80211_MODE_AUTO */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ + IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ + IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ + IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ + IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ + }; + struct ieee80211_channel *c; + u_int modeflags; + int i; + + /* validate new mode */ + if ((ic->ic_modecaps & (1<<mode)) == 0) { + IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n", + __func__, mode, ic->ic_modecaps)); + return EINVAL; + } + + /* + * Verify at least one channel is present in the available + * channel list before committing to the new mode. + */ + IASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); + modeflags = chanflags[mode]; + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + c = &ic->ic_channels[i]; + if (mode == IEEE80211_MODE_AUTO) { + /* ignore turbo channels for autoselect */ + if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) + break; + } else { + if ((c->ic_flags & modeflags) == modeflags) + break; + } + } + if (i > IEEE80211_CHAN_MAX) { + IEEE80211_DPRINTF(("%s: no channels found for mode %u\n", + __func__, mode)); + return EINVAL; + } + + /* + * Calculate the active channel set. + */ + memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + c = &ic->ic_channels[i]; + if (mode == IEEE80211_MODE_AUTO) { + /* take anything but pure turbo channels */ + if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) + setbit(ic->ic_chan_active, i); + } else { + if ((c->ic_flags & modeflags) == modeflags) + setbit(ic->ic_chan_active, i); + } + } + /* + * If no current/default channel is setup or the current + * channel is wrong for the mode then pick the first + * available channel from the active list. This is likely + * not the right one. + */ + if (ic->ic_ibss_chan == NULL || + isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + break; + } + IASSERT(ic->ic_ibss_chan != NULL && + isset(ic->ic_chan_active, + ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), + ("Bad IBSS channel %u\n", + ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); + } + + /* + * Set/reset state flags that influence beacon contents, etc. + * + * XXX what if we have stations already associated??? + * XXX probably not right for autoselect? + */ + if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + if (mode == IEEE80211_MODE_11G) { + if (ic->ic_caps & IEEE80211_C_SHSLOT) + ic->ic_flags |= IEEE80211_F_SHSLOT; + ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], + IEEE80211_MODE_11G); + } else { + ic->ic_flags &= ~IEEE80211_F_SHSLOT; + } + + ic->ic_curmode = mode; + return 0; +#undef N +} + +/* + * Return the phy mode for with the specified channel so the + * caller can select a rate set. This is problematic and the + * work here assumes how things work elsewhere in this code. + * + * XXX never returns turbo modes -dcy + */ +enum ieee80211_phymode +ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) +{ + /* + * NB: this assumes the channel would not be supplied to us + * unless it was already compatible with the current mode. + */ + if (ic->ic_curmode != IEEE80211_MODE_AUTO || + chan == IEEE80211_CHAN_ANYC) + return ic->ic_curmode; + /* + * In autoselect mode; deduce a mode based on the channel + * characteristics. We assume that turbo-only channels + * are not considered when the channel set is constructed. + */ + if (IEEE80211_IS_CHAN_5GHZ(chan)) + return IEEE80211_MODE_11A; + else if (IEEE80211_IS_CHAN_FHSS(chan)) + return IEEE80211_MODE_FH; + else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) + return IEEE80211_MODE_11G; + else + return IEEE80211_MODE_11B; +} + +/* + * convert IEEE80211 rate value to ifmedia subtype. + * ieee80211 rate is in unit of 0.5Mbps. + */ +int +ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const struct { + u_int m; /* rate + mode */ + u_int r; /* if_media rate */ + } rates[] = { + { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, + { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, + { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, + { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, + { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, + { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, + { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, + { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, + { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, + { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, + { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, + { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, + { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, + { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, + { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, + { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, + { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, + { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, + { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, + { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, + { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, + { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, + { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, + { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, + { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, + { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, + { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, + /* NB: OFDM72 doesn't realy exist so we don't handle it */ + }; + u_int mask, i; + + mask = rate & IEEE80211_RATE_VAL; + switch (mode) { + case IEEE80211_MODE_11A: + case IEEE80211_MODE_TURBO: + mask |= IFM_IEEE80211_11A; + break; + case IEEE80211_MODE_11B: + mask |= IFM_IEEE80211_11B; + break; + case IEEE80211_MODE_FH: + mask |= IFM_IEEE80211_FH; + break; + case IEEE80211_MODE_AUTO: + /* NB: ic may be NULL for some drivers */ + if (ic && ic->ic_phytype == IEEE80211_T_FH) { + mask |= IFM_IEEE80211_FH; + break; + } + /* NB: hack, 11g matches both 11b+11a rates */ + /* fall thru... */ + case IEEE80211_MODE_11G: + mask |= IFM_IEEE80211_11G; + break; + } + for (i = 0; i < N(rates); i++) + if (rates[i].m == mask) + return rates[i].r; + return IFM_AUTO; +#undef N +} + +int +ieee80211_media2rate(int mword) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + static const struct { + int subtype; + int rate; + } ieeerates[] = { + { IFM_AUTO, -1 }, + { IFM_MANUAL, 0 }, + { IFM_NONE, 0 }, + { IFM_IEEE80211_FH1, 2 }, + { IFM_IEEE80211_FH2, 4 }, + { IFM_IEEE80211_DS1, 2 }, + { IFM_IEEE80211_DS2, 4 }, + { IFM_IEEE80211_DS5, 11 }, + { IFM_IEEE80211_DS11, 22 }, + { IFM_IEEE80211_DS22, 44 }, + { IFM_IEEE80211_OFDM6, 12 }, + { IFM_IEEE80211_OFDM9, 18 }, + { IFM_IEEE80211_OFDM12, 24 }, + { IFM_IEEE80211_OFDM18, 36 }, + { IFM_IEEE80211_OFDM24, 48 }, + { IFM_IEEE80211_OFDM36, 72 }, + { IFM_IEEE80211_OFDM48, 96 }, + { IFM_IEEE80211_OFDM54, 108 }, + { IFM_IEEE80211_OFDM72, 144 }, + }; + for (i = 0; i < N(ieeerates); i++) { + if (ieeerates[i].subtype == IFM_SUBTYPE(mword)) + return ieeerates[i].rate; + } + return 0; +#undef N +} + +#ifdef __NetBSD__ +/* TBD factor with sysctl_ath_verify. */ +static int +sysctl_ieee80211_verify(SYSCTLFN_ARGS) +{ + int error, t; + struct sysctlnode node; + + node = *rnode; + t = *(int*)rnode->sysctl_data; + node.sysctl_data = &t; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + IEEE80211_DPRINTF(("%s: t = %d, nodenum = %d, rnodenum = %d\n", + __func__, t, node.sysctl_num, rnode->sysctl_num)); + + if (node.sysctl_num == ieee80211_inact_max_nodenum) { + if (t < 1) + return (EINVAL); + t = roundup(t, IEEE80211_INACT_WAIT) / IEEE80211_INACT_WAIT; +#ifdef IEEE80211_DEBUG + } else if (node.sysctl_num == ieee80211_debug_nodenum) { + if (t < 0 || t > 2) + return (EINVAL); +#endif /* IEEE80211_DEBUG */ + } else + return (EINVAL); + + *(int*)rnode->sysctl_data = t; + + return (0); +} + +/* + * Setup sysctl(3) MIB, net.ieee80211.* + * + * TBD condition CTLFLAG_PERMANENT on being an LKM or not + */ +SYSCTL_SETUP(sysctl_ieee80211, "sysctl ieee80211 subtree setup") +{ + int rc; + struct sysctlnode *cnode, *rnode; + + if ((rc = sysctl_createv(clog, 0, NULL, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "net", NULL, + NULL, 0, NULL, 0, CTL_NET, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "link", + "link-layer statistics and controls", + NULL, 0, NULL, 0, PF_LINK, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211", + "IEEE 802.11 WLAN statistics and controls", + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + +#ifdef IEEE80211_DEBUG + + /* control debugging printfs */ + if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "debug", SYSCTL_DESCR("Enable IEEE 802.11 debugging output"), + sysctl_ieee80211_verify, 0, &ieee80211_debug, 0, + CTL_CREATE, CTL_EOL)) != 0) + goto err; + + ieee80211_debug_nodenum = cnode->sysctl_num; + +#endif /* IEEE80211_DEBUG */ + + /* control inactivity timer */ + if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "maxinact", SYSCTL_DESCR("Station inactivity timeout"), + sysctl_ieee80211_verify, 0, &ieee80211_inact_max, + 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + + ieee80211_inact_max_nodenum = cnode->sysctl_num; + + return; +err: + printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +/* + * Module glue. + * + * NB: the module name is "wlan" for compatibility with NetBSD. + */ + +static int +ieee80211_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("wlan: <802.11 Link Layer>\n"); + return 0; + case MOD_UNLOAD: + return 0; + } + return EINVAL; +} + +static moduledata_t ieee80211_mod = { + "wlan", + ieee80211_modevent, + 0 +}; +DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan, 1); +MODULE_DEPEND(wlan, rc4, 1, 1, 1); +MODULE_DEPEND(wlan, ether, 1, 1, 1); +#endif diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h new file mode 100644 index 00000000000..f845845d2b6 --- /dev/null +++ b/sys/net80211/ieee80211.h @@ -0,0 +1,548 @@ +/* $OpenBSD: ieee80211.h,v 1.1 2004/06/22 22:53:51 millert Exp $ */ +/* $NetBSD: ieee80211.h,v 1.6 2004/04/30 23:51:53 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.5 2004/04/05 17:47:40 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_H_ +#define _NET80211_IEEE80211_H_ + +/* + * 802.11 protocol definitions. + */ + +#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ +/* is 802.11 address multicast/broadcast? */ +#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01) + +/* IEEE 802.11 PLCP header */ +struct ieee80211_plcp_hdr { + u_int16_t i_sfd; + u_int8_t i_signal; + u_int8_t i_service; + u_int16_t i_length; + u_int16_t i_crc; +} __attribute__((__packed__)); + +#define IEEE80211_PLCP_SFD 0xF3A0 +#define IEEE80211_PLCP_SERVICE 0x00 + +/* + * generic definitions for IEEE 802.11 frames + */ +struct ieee80211_frame { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __attribute__((__packed__)); + +struct ieee80211_qosframe { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + u_int8_t i_qos[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __attribute__((__packed__)); + +struct ieee80211_qoscntl { + u_int8_t i_qos[2]; +}; + +struct ieee80211_frame_addr4 { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + u_int8_t i_addr4[IEEE80211_ADDR_LEN]; +} __attribute__((__packed__)); + + +struct ieee80211_qosframe_addr4 { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + u_int8_t i_addr4[IEEE80211_ADDR_LEN]; + u_int8_t i_qos[2]; +} __attribute__((__packed__)); + +/* + * Management Notification Frame + */ +struct ieee80211_mnf { + u_int8_t mnf_category; + u_int8_t mnf_action; + u_int8_t mnf_dialog; + u_int8_t mnf_status; +} __attribute__((__packed__)); +#define MNF_SETUP_REQ 0 +#define MNF_SETUP_RESP 1 +#define MNF_TEARDOWN 2 + +/* + * WME/802.11e Tspec Element + */ +struct ieee80211_wme_tspec { + u_int8_t ts_id; + u_int8_t ts_len; + u_int8_t ts_oui[3]; + u_int8_t ts_oui_type; + u_int8_t ts_oui_subtype; + u_int8_t ts_version; + u_int8_t ts_tsinfo[3]; + u_int8_t ts_nom_msdu[2]; + u_int8_t ts_max_msdu[2]; + u_int8_t ts_min_svc[4]; + u_int8_t ts_max_svc[4]; + u_int8_t ts_inactv_intv[4]; + u_int8_t ts_susp_intv[4]; + u_int8_t ts_start_svc[4]; + u_int8_t ts_min_rate[4]; + u_int8_t ts_mean_rate[4]; + u_int8_t ts_max_burst[4]; + u_int8_t ts_min_phy[4]; + u_int8_t ts_peak_rate[4]; + u_int8_t ts_delay[4]; + u_int8_t ts_surplus[2]; + u_int8_t ts_medium_time[2]; +} __attribute__((__packed__)); + +#define IEEE80211_FC0_VERSION_MASK 0x03 +#define IEEE80211_FC0_VERSION_SHIFT 0 +#define IEEE80211_FC0_VERSION_0 0x00 +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define IEEE80211_FC0_TYPE_SHIFT 2 +#define IEEE80211_FC0_TYPE_MGT 0x00 +#define IEEE80211_FC0_TYPE_CTL 0x04 +#define IEEE80211_FC0_TYPE_DATA 0x08 + +#define IEEE80211_FC0_SUBTYPE_MASK 0xf0 +#define IEEE80211_FC0_SUBTYPE_SHIFT 4 +/* for TYPE_MGT */ +#define IEEE80211_FC0_SUBTYPE_ASSOC_REQ 0x00 +#define IEEE80211_FC0_SUBTYPE_ASSOC_RESP 0x10 +#define IEEE80211_FC0_SUBTYPE_REASSOC_REQ 0x20 +#define IEEE80211_FC0_SUBTYPE_REASSOC_RESP 0x30 +#define IEEE80211_FC0_SUBTYPE_PROBE_REQ 0x40 +#define IEEE80211_FC0_SUBTYPE_PROBE_RESP 0x50 +#define IEEE80211_FC0_SUBTYPE_BEACON 0x80 +#define IEEE80211_FC0_SUBTYPE_ATIM 0x90 +#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0 +#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0 +#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0 +/* for TYPE_CTL */ +#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0 +#define IEEE80211_FC0_SUBTYPE_RTS 0xb0 +#define IEEE80211_FC0_SUBTYPE_CTS 0xc0 +#define IEEE80211_FC0_SUBTYPE_ACK 0xd0 +#define IEEE80211_FC0_SUBTYPE_CF_END 0xe0 +#define IEEE80211_FC0_SUBTYPE_CF_END_ACK 0xf0 +/* for TYPE_DATA (bit combination) */ +#define IEEE80211_FC0_SUBTYPE_DATA 0x00 +#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10 +#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20 +#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30 +#define IEEE80211_FC0_SUBTYPE_NODATA 0x40 +#define IEEE80211_FC0_SUBTYPE_CFACK 0x50 +#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60 +#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70 +#define IEEE80211_FC0_SUBTYPE_QOS 0x80 + +#define IEEE80211_FC1_DIR_MASK 0x03 +#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ +#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */ +#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */ +#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */ + +#define IEEE80211_FC1_MORE_FRAG 0x04 +#define IEEE80211_FC1_RETRY 0x08 +#define IEEE80211_FC1_PWR_MGT 0x10 +#define IEEE80211_FC1_MORE_DATA 0x20 +#define IEEE80211_FC1_WEP 0x40 +#define IEEE80211_FC1_ORDER 0x80 + +#define IEEE80211_SEQ_FRAG_MASK 0x000f +#define IEEE80211_SEQ_FRAG_SHIFT 0 +#define IEEE80211_SEQ_SEQ_MASK 0xfff0 +#define IEEE80211_SEQ_SEQ_SHIFT 4 + +#define IEEE80211_NWID_LEN 32 + +#define IEEE80211_QOS_TXOP 0x00ff +/* bit 8 is reserved */ +#define IEEE80211_QOS_ACKPOLICY 0x0600 +#define IEEE80211_QOS_ESOP 0x0800 +#define IEEE80211_QOS_TID 0xf000 + +/* + * Control frames. + */ +struct ieee80211_frame_min { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + /* FCS */ +} __attribute__((__packed__)); + +struct ieee80211_frame_rts { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + u_int8_t i_ta[IEEE80211_ADDR_LEN]; + /* FCS */ +} __attribute__((__packed__)); + +struct ieee80211_frame_cts { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + /* FCS */ +} __attribute__((__packed__)); + +struct ieee80211_frame_ack { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + /* FCS */ +} __attribute__((__packed__)); + +struct ieee80211_frame_pspoll { + u_int8_t i_fc[2]; + u_int8_t i_aid[2]; + u_int8_t i_bssid[IEEE80211_ADDR_LEN]; + u_int8_t i_ta[IEEE80211_ADDR_LEN]; + /* FCS */ +} __attribute__((__packed__)); + +struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; /* should be zero */ + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + u_int8_t i_bssid[IEEE80211_ADDR_LEN]; + /* FCS */ +} __attribute__((__packed__)); + +/* + * BEACON management packets + * + * octet timestamp[8] + * octet beacon interval[2] + * octet capability information[2] + * information element + * octet elemid + * octet length + * octet information[length] + */ + +typedef uint8_t *ieee80211_mgt_beacon_t; + +#define IEEE80211_BEACON_INTERVAL(beacon) \ + ((beacon)[8] | ((beacon)[9] << 8)) +#define IEEE80211_BEACON_CAPABILITY(beacon) \ + ((beacon)[10] | ((beacon)[11] << 8)) + +#define IEEE80211_CAPINFO_ESS 0x0001 +#define IEEE80211_CAPINFO_IBSS 0x0002 +#define IEEE80211_CAPINFO_CF_POLLABLE 0x0004 +#define IEEE80211_CAPINFO_CF_POLLREQ 0x0008 +#define IEEE80211_CAPINFO_PRIVACY 0x0010 +#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020 +#define IEEE80211_CAPINFO_PBCC 0x0040 +#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080 +/* bits 8-9 are reserved */ +#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400 +#define IEEE80211_CAPINFO_RSN 0x0800 +/* bit 12 is reserved */ +#define IEEE80211_CAPINFO_DSSSOFDM 0x2000 +/* bits 14-15 are reserved */ + +/* + * 802.11i/WPA information element (maximally sized). + */ +struct ieee80211_ie_wpa { + u_int8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ + u_int8_t wpa_type; /* OUI type */ + u_int16_t wpa_version; /* spec revision */ + u_int32_t wpa_mcipher[1]; /* multicast/group key cipher */ + u_int16_t wpa_uciphercnt; /* # pairwise key ciphers */ + u_int32_t wpa_uciphers[8];/* ciphers */ + u_int16_t wpa_authselcnt; /* authentication selector cnt*/ + u_int32_t wpa_authsels[8];/* selectors */ +} __attribute__((__packed__)); + +/* + * Management information elements + */ +struct ieee80211_information { + char ssid[IEEE80211_NWID_LEN+1]; + struct rates { + u_int8_t *p; + } rates; + struct fh { + u_int16_t dwell; + u_int8_t set; + u_int8_t pattern; + u_int8_t index; + } fh; + struct ds { + u_int8_t channel; + } ds; + struct cf { + u_int8_t count; + u_int8_t period; + u_int8_t maxdur[2]; + u_int8_t dur[2]; + } cf; + struct tim { + u_int8_t count; + u_int8_t period; + u_int8_t bitctl; + /* u_int8_t pvt[251]; The driver needs to use this. */ + } tim; + struct ibss { + u_int16_t atim; + } ibss; + struct challenge { + u_int8_t *p; + u_int8_t len; + } challenge; + struct erp { + u_int8_t flags; + } erp; + struct country { + u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ + struct { + u_int8_t schan; /* starting channel */ + u_int8_t nchan; /* number channels */ + u_int8_t maxtxpwr; + } band[4]; /* up to 4 sub bands */ + } country; + struct ath { + u_int8_t flags; + } ath; + struct ieee80211_ie_wpa wpa; +}; + +enum { + IEEE80211_ELEMID_SSID = 0, + IEEE80211_ELEMID_RATES = 1, + IEEE80211_ELEMID_FHPARMS = 2, + IEEE80211_ELEMID_DSPARMS = 3, + IEEE80211_ELEMID_CFPARMS = 4, + IEEE80211_ELEMID_TIM = 5, + IEEE80211_ELEMID_IBSSPARMS = 6, + IEEE80211_ELEMID_COUNTRY = 7, + IEEE80211_ELEMID_CHALLENGE = 16, + /* 17-31 reserved for challenge text extension */ + IEEE80211_ELEMID_ERP = 42, + IEEE80211_ELEMID_XRATES = 50, + IEEE80211_ELEMID_TPC = 150, + IEEE80211_ELEMID_CCKM = 156, + IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ +}; + +#define IEEE80211_CHALLENGE_LEN 128 + +#define IEEE80211_RATE_BASIC 0x80 +#define IEEE80211_RATE_VAL 0x7f + +/* EPR information element flags */ +#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 +#define IEEE80211_ERP_USE_PROTECTION 0x02 +#define IEEE80211_ERP_BARKER_MODE 0x04 + +/* Atheros private advanced capabilities info */ +#define ATHEROS_CAP_TURBO_PRIME 0x01 +#define ATHEROS_CAP_COMPRESSION 0x02 +#define ATHEROS_CAP_FAST_FRAME 0x04 +/* bits 3-6 reserved */ +#define ATHEROS_CAP_BOOST 0x80 + +#define ATH_OUI 0x7f0300 /* Atheros OUI */ +#define ATH_OUI_TYPE 0x01 +#define ATH_OUI_VERSION 0x01 + +#define WPA_OUI 0xf25000 +#define WPA_OUI_TYPE 0x01 +#define WPA_OUI_VERSION 1 /* current supported version */ + +#define WPA_CSE_NULL 0x00 +#define WPA_CSE_WEP40 0x01 +#define WPA_CSE_TKIP 0x02 +#define WPA_CSE_WRAP 0x03 /* WPA2/802.11i */ +#define WPA_CSE_CCMP 0x04 +#define WPA_CSE_WEP104 0x05 + +#define WPA_ASE_NONE 0x00 +#define WPA_ASE_8021X_UNSPEC 0x01 +#define WPA_ASE_8021X_PSK 0x02 + +/* + * AUTH management packets + * + * octet algo[2] + * octet seq[2] + * octet status[2] + * octet chal.id + * octet chal.length + * octet chal.text[253] + */ + +typedef u_int8_t *ieee80211_mgt_auth_t; + +#define IEEE80211_AUTH_ALGORITHM(auth) \ + ((auth)[0] | ((auth)[1] << 8)) +#define IEEE80211_AUTH_TRANSACTION(auth) \ + ((auth)[2] | ((auth)[3] << 8)) +#define IEEE80211_AUTH_STATUS(auth) \ + ((auth)[4] | ((auth)[5] << 8)) + +#define IEEE80211_AUTH_ALG_OPEN 0x0000 +#define IEEE80211_AUTH_ALG_SHARED 0x0001 +#define IEEE80211_AUTH_ALG_LEAP 0x0080 + +enum { + IEEE80211_AUTH_OPEN_REQUEST = 1, + IEEE80211_AUTH_OPEN_RESPONSE = 2, +}; + +enum { + IEEE80211_AUTH_SHARED_REQUEST = 1, + IEEE80211_AUTH_SHARED_CHALLENGE = 2, + IEEE80211_AUTH_SHARED_RESPONSE = 3, + IEEE80211_AUTH_SHARED_PASS = 4, +}; + +/* + * Reason codes + * + * Unlisted codes are reserved + */ + +enum { + IEEE80211_REASON_UNSPECIFIED = 1, + IEEE80211_REASON_AUTH_EXPIRE = 2, + IEEE80211_REASON_AUTH_LEAVE = 3, + IEEE80211_REASON_ASSOC_EXPIRE = 4, + IEEE80211_REASON_ASSOC_TOOMANY = 5, + IEEE80211_REASON_NOT_AUTHED = 6, + IEEE80211_REASON_NOT_ASSOCED = 7, + IEEE80211_REASON_ASSOC_LEAVE = 8, + IEEE80211_REASON_ASSOC_NOT_AUTHED = 9, + + IEEE80211_REASON_RSN_REQUIRED = 11, + IEEE80211_REASON_RSN_INCONSISTENT = 12, + IEEE80211_REASON_IE_INVALID = 13, + IEEE80211_REASON_MIC_FAILURE = 14, + + IEEE80211_STATUS_SUCCESS = 0, + IEEE80211_STATUS_UNSPECIFIED = 1, + IEEE80211_STATUS_CAPINFO = 10, + IEEE80211_STATUS_NOT_ASSOCED = 11, + IEEE80211_STATUS_OTHER = 12, + IEEE80211_STATUS_ALG = 13, + IEEE80211_STATUS_SEQUENCE = 14, + IEEE80211_STATUS_CHALLENGE = 15, + IEEE80211_STATUS_TIMEOUT = 16, + IEEE80211_STATUS_TOOMANY = 17, + IEEE80211_STATUS_BASIC_RATE = 18, + IEEE80211_STATUS_SP_REQUIRED = 19, + IEEE80211_STATUS_PBCC_REQUIRED = 20, + IEEE80211_STATUS_CA_REQUIRED = 21, + IEEE80211_STATUS_TOO_MANY_STATIONS = 22, + IEEE80211_STATUS_RATES = 23, + IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, + IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, +}; + +#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ +#define IEEE80211_WEP_IVLEN 3 /* 24bit */ +#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ +#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ +#define IEEE80211_WEP_NKID 4 /* number of key ids */ + +#define IEEE80211_CRC_LEN 4 + +/* + * Maximum acceptable MTU is: + * IEEE80211_MAX_LEN - WEP overhead - CRC - + * QoS overhead - RSN/WPA overhead + * Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default + * mtu is Ethernet-compatible; it's set by ether_ifattach. + */ +#define IEEE80211_MTU_MAX 2290 +#define IEEE80211_MTU_MIN 32 + +#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ + (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_MIN_LEN \ + (sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN) + +#define IEEE80211_MAX_AID 2007 + +#define IEEE80211_AID_SET(b, w) \ + ((w)[((b) & ~0xc000) / 32] |= (1 << (((b) & ~0xc000) % 32))) +#define IEEE80211_AID_CLR(b, w) \ + ((w)[((b) & ~0xc000) / 32] &= ~(1 << (((b) & ~0xc000) % 32))) +#define IEEE80211_AID_ISSET(b, w) \ + ((w)[((b) & ~0xc000) / 32] & (1 << (((b) & ~0xc000) % 32))) + +/* + * RTS frame length parameters. The default is specified in + * the 802.11 spec. The max may be wrong for jumbo frames. + */ +#define IEEE80211_RTS_DEFAULT 512 +#define IEEE80211_RTS_MIN 1 +#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN + +enum { + IEEE80211_AUTH_NONE = 0, + IEEE80211_AUTH_OPEN = 1, + IEEE80211_AUTH_SHARED = 2, +}; + +#endif /* _NET80211_IEEE80211_H_ */ diff --git a/sys/net80211/ieee80211_compat.c b/sys/net80211/ieee80211_compat.c new file mode 100644 index 00000000000..c171cdab40d --- /dev/null +++ b/sys/net80211/ieee80211_compat.c @@ -0,0 +1,54 @@ +/* $OpenBSD: ieee80211_compat.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_compat.c,v 1.3 2003/09/23 15:57:25 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/socket.h> +#include <sys/systm.h> +#include <machine/stdarg.h> +#include <net/if.h> +#include <net80211/ieee80211_compat.h> + +#ifdef __NetBSD__ +__KERNEL_RCSID(0, "$NetBSD: ieee80211_compat.c,v 1.3 2003/09/23 15:57:25 dyoung Exp $"); +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +void +if_printf(struct ifnet *ifp, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + printf("%s: ", ifp->if_xname); + vprintf(fmt, ap); + + va_end(ap); + return; +} +#endif diff --git a/sys/net80211/ieee80211_compat.h b/sys/net80211/ieee80211_compat.h new file mode 100644 index 00000000000..b217c261ceb --- /dev/null +++ b/sys/net80211/ieee80211_compat.h @@ -0,0 +1,64 @@ +/* $OpenBSD: ieee80211_compat.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_compat.h,v 1.5 2004/01/13 23:37:30 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _NET80211_IEEE80211_COMPAT_H_ +#define _NET80211_IEEE80211_COMPAT_H_ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define IASSERT(cond, complaint) if (!(cond)) panic complaint +#else +#define IASSERT(cond, complaint) KASSERT(cond, complaint) +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +void if_printf(struct ifnet *, const char *, ...) + __attribute__((__format__(__printf__,2,3))); +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define ieee80211_node_critsec_decl(v) int v +#define ieee80211_node_critsec_begin(ic, v) do { v = splnet(); } while (0) +#define ieee80211_node_critsec_end(ic, v) splx(v) +#else +#define ieee80211_node_critsec_decl(v) /* empty */ +#define ieee80211_node_critsec_begin(ic, v) mtx_lock(&(ic)->ic_nodelock) +#define ieee80211_node_critsec_end(ic, v) mtx_unlock(&ic->ic_nodelock) +#endif + +#if defined(__OpenBSD__) +#define BPF_ATTACH(ifp_x,dlt_x,dlf_x,bpf_x) \ + bpfattach(bpf_x,ifp_x,dlt_x,dlf_x) +#else +#define BPF_ATTACH(ifp_x,dlt_x,dlf_x,bpf_x) \ + bpfattach2(ifp_x,dlt_x,dlf_x,bpf_x) +#endif +#define BPF_DETACH(ifp_x) bpfdetach(ifp_x) +#define BPF_MTAP(bpf_x,mbf_x) bpf_mtap(bpf_x,mbf_x) + +#endif /* _NET80211_IEEE80211_COMPAT_H_ */ diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c new file mode 100644 index 00000000000..055caf247a6 --- /dev/null +++ b/sys/net80211/ieee80211_crypto.c @@ -0,0 +1,364 @@ +/* $OpenBSD: ieee80211_crypto.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_crypto.c,v 1.5 2003/12/14 09:56:53 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto.c,v 1.5 2003/12/14 09:56:53 dyoung Exp $"); +#endif + +#if defined(__NetBSD__) +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +#if defined(__FreeBSD__) +#include <crypto/rc4/rc4.h> +#define arc4_ctxlen() sizeof (struct rc4_state) +#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l) +#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l) +#elif defined(__OpenBSD__) +#include <dev/rndvar.h> +#include <crypto/arc4.h> +#define arc4_ctxlen() sizeof (struct rc4_ctx) +#define arc4_setkey(_c,_k,_l) rc4_keysetup(_c,_k,_l) +#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l) +#else +#include <crypto/arc4/arc4.h> +#endif + +static void ieee80211_crc_init(void); +static u_int32_t ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len); + +void +ieee80211_crypto_attach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + /* + * Setup crypto support. + */ + ieee80211_crc_init(); + ic->ic_iv = arc4random(); +} + +void +ieee80211_crypto_detach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + if (ic->ic_wep_ctx != NULL) { + free(ic->ic_wep_ctx, M_DEVBUF); + ic->ic_wep_ctx = NULL; + } +} + +struct mbuf * +ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag) +{ + struct ieee80211com *ic = (void *)ifp; + struct mbuf *m, *n, *n0; + struct ieee80211_frame *wh; + int i, left, len, moff, noff, kid; + u_int32_t iv, crc; + u_int8_t *ivp; + void *ctx; + u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; + + n0 = NULL; + if ((ctx = ic->ic_wep_ctx) == NULL) { + ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + goto fail; + } + ic->ic_wep_ctx = ctx; + } + m = m0; + left = m->m_pkthdr.len; + MGET(n, M_DONTWAIT, m->m_type); + n0 = n; + if (n == NULL) { + if (txflag) + ic->ic_stats.is_tx_nombuf++; + else + ic->ic_stats.is_rx_nombuf++; + goto fail; + } +#if defined(__FreeBSD__) + M_MOVE_PKTHDR(n, m); +#elif defined(__OpenBSD__) + M_DUP_PKTHDR(n, m); +#else + M_COPY_PKTHDR(n, m); +#endif + len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; + if (txflag) { + n->m_pkthdr.len += len; + } else { + n->m_pkthdr.len -= len; + left -= len; + } + n->m_len = MHLEN; + if (n->m_pkthdr.len >= MINCLSIZE) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + len = sizeof(struct ieee80211_frame); + memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len); + wh = mtod(n, struct ieee80211_frame *); + left -= len; + moff = len; + noff = len; + if (txflag) { + kid = ic->ic_wep_txkey; + wh->i_fc[1] |= IEEE80211_FC1_WEP; + iv = ic->ic_iv; + /* + * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: + * (B, 255, N) with 3 <= B < 8 + */ + if (iv >= 0x03ff00 && + (iv & 0xf8ff00) == 0x00ff00) + iv += 0x000100; + ic->ic_iv = iv + 1; + /* put iv in little endian to prepare 802.11i */ + ivp = mtod(n, u_int8_t *) + noff; + for (i = 0; i < IEEE80211_WEP_IVLEN; i++) { + ivp[i] = iv & 0xff; + iv >>= 8; + } + ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */ + noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; + } else { + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + ivp = mtod(m, u_int8_t *) + moff; + kid = ivp[IEEE80211_WEP_IVLEN] >> 6; + moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; + } + memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN); + memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key, + ic->ic_nw_keys[kid].wk_len); + arc4_setkey(ctx, keybuf, + IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len); + + /* encrypt with calculating CRC */ + crc = ~0; + while (left > 0) { + len = m->m_len - moff; + if (len == 0) { + m = m->m_next; + moff = 0; + continue; + } + if (len > n->m_len - noff) { + len = n->m_len - noff; + if (len == 0) { + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) { + if (txflag) + ic->ic_stats.is_tx_nombuf++; + else + ic->ic_stats.is_rx_nombuf++; + goto fail; + } + n = n->m_next; + n->m_len = MLEN; + if (left >= MINCLSIZE) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + noff = 0; + continue; + } + } + if (len > left) + len = left; + arc4_encrypt(ctx, mtod(n, caddr_t) + noff, + mtod(m, caddr_t) + moff, len); + if (txflag) + crc = ieee80211_crc_update(crc, + mtod(m, u_int8_t *) + moff, len); + else + crc = ieee80211_crc_update(crc, + mtod(n, u_int8_t *) + noff, len); + left -= len; + moff += len; + noff += len; + } + crc = ~crc; + if (txflag) { + *(u_int32_t *)crcbuf = htole32(crc); + if (n->m_len >= noff + sizeof(crcbuf)) + n->m_len = noff + sizeof(crcbuf); + else { + n->m_len = noff; + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) { + ic->ic_stats.is_tx_nombuf++; + goto fail; + } + n = n->m_next; + n->m_len = sizeof(crcbuf); + noff = 0; + } + arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf, + sizeof(crcbuf)); + } else { + n->m_len = noff; + for (noff = 0; noff < sizeof(crcbuf); noff += len) { + len = sizeof(crcbuf) - noff; + if (len > m->m_len - moff) + len = m->m_len - moff; + if (len > 0) + arc4_encrypt(ctx, crcbuf + noff, + mtod(m, caddr_t) + moff, len); + m = m->m_next; + moff = 0; + } + if (crc != letoh32(*(u_int32_t *)crcbuf)) { +#ifdef IEEE80211_DEBUG + if (ieee80211_debug) { + if_printf(ifp, "decrypt CRC error\n"); + if (ieee80211_debug > 1) + ieee80211_dump_pkt(n0->m_data, + n0->m_len, -1, -1); + } +#endif + ic->ic_stats.is_rx_decryptcrc++; + goto fail; + } + } + m_freem(m0); + return n0; + + fail: + m_freem(m0); + m_freem(n0); + return NULL; +} + +/* + * CRC 32 -- routine from RFC 2083 + */ + +/* Table of CRCs of all 8-bit messages */ +static u_int32_t ieee80211_crc_table[256]; + +/* Make the table for a fast CRC. */ +static void +ieee80211_crc_init(void) +{ + u_int32_t c; + int n, k; + + for (n = 0; n < 256; n++) { + c = (u_int32_t)n; + for (k = 0; k < 8; k++) { + if (c & 1) + c = 0xedb88320UL ^ (c >> 1); + else + c = c >> 1; + } + ieee80211_crc_table[n] = c; + } +} + +/* + * Update a running CRC with the bytes buf[0..len-1]--the CRC + * should be initialized to all 1's, and the transmitted value + * is the 1's complement of the final running CRC + */ + +static u_int32_t +ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len) +{ + u_int8_t *endbuf; + + for (endbuf = buf + len; buf < endbuf; buf++) + crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return crc; +} diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h new file mode 100644 index 00000000000..553072ddd54 --- /dev/null +++ b/sys/net80211/ieee80211_crypto.h @@ -0,0 +1,53 @@ +/* $OpenBSD: ieee80211_crypto.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_CRYPTO_H_ +#define _NET80211_IEEE80211_CRYPTO_H_ + +/* + * 802.11 protocol crypto-related definitions. + */ +#define IEEE80211_KEYBUF_SIZE 16 + +struct ieee80211_wepkey { + int wk_len; + u_int8_t wk_key[IEEE80211_KEYBUF_SIZE]; +}; + +extern void ieee80211_crypto_attach(struct ifnet *); +extern void ieee80211_crypto_detach(struct ifnet *); +extern struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int); +#endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c new file mode 100644 index 00000000000..5ddfa6ed618 --- /dev/null +++ b/sys/net80211/ieee80211_input.c @@ -0,0 +1,1607 @@ +/* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.20 2004/04/02 23:35:24 sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $"); +#endif + +#if defined(__NetBSD__) +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +#ifdef __OpenBSD__ +#include <dev/rndvar.h> +#endif + +static void ieee80211_recv_pspoll(struct ieee80211com *, + struct mbuf *, int, u_int32_t); + +/* + * Process a received frame. The node associated with the sender + * should be supplied. If nothing was found in the node table then + * the caller is assumed to supply a reference to ic_bss instead. + * The RSSI and a timestamp are also supplied. The RSSI data is used + * during AP scanning to select a AP to associate with; it can have + * any units so long as values have consistent units and higher values + * mean ``better signal''. The receive timestamp is currently not used + * by the 802.11 layer. + */ +void +ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, + int rssi, u_int32_t rstamp) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_frame *wh; + struct ether_header *eh; + struct mbuf *m1; + int error, len; + u_int8_t dir, type, subtype; + u_int8_t *bssid; + u_int16_t rxseq; +#if !defined(__OpenBSD__) + ALTQ_DECL(struct altq_pktattr pktattr;) +#endif + + IASSERT(ni != NULL, ("null node")); + + /* trim CRC here so WEP can find its own CRC at the end of packet. */ + if (m->m_flags & M_HASFCS) { + m_adj(m, -IEEE80211_CRC_LEN); + m->m_flags &= ~M_HASFCS; + } + + /* + * In monitor mode, send everything directly to bpf. + * Also do not process frames w/o i_addr2 any further. + * XXX may want to include the CRC + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR || + m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) + goto out; + + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) { + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "receive packet with wrong version: %x\n", + wh->i_fc[0]); + ic->ic_stats.is_rx_badversion++; + goto err; + } + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + /* + * NB: We are not yet prepared to handle control frames, + * but permitting drivers to send them to us allows + * them to go through bpf tapping at the 802.11 layer. + */ + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + /* XXX statistic */ + IEEE80211_DPRINTF2(("%s: frame too short, len %u\n", + __func__, m->m_pkthdr.len)); + ic->ic_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + if (ic->ic_state != IEEE80211_S_SCAN) { + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) { + /* not interested in */ + IEEE80211_DPRINTF2(("%s: discard frame from " + "bss %s\n", __func__, + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + if (dir == IEEE80211_FC1_DIR_NODS) + bssid = wh->i_addr3; + else + bssid = wh->i_addr1; + if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && +#ifdef __OpenBSD__ + !IEEE80211_ADDR_EQ(bssid, etherbroadcastaddr) && +#else + !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr) && +#endif + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_DATA) { + /* not interested in */ + IEEE80211_DPRINTF2(("%s: discard frame from " + "bss %s\n", __func__, + ether_sprintf(bssid))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } + break; + case IEEE80211_M_MONITOR: + goto out; + default: + /* XXX catch bad values */ + break; + } + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + rxseq = ni->ni_rxseq; + ni->ni_rxseq = + letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + /* TODO: fragment */ + if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + rxseq == ni->ni_rxseq) { + /* duplicate, silently discarded */ + ic->ic_stats.is_rx_dup++; /* XXX per-station stat */ + goto out; + } + ni->ni_inact = 0; + } + + if (ic->ic_set_tim != NULL && + (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) + && ni->ni_pwrsave == 0) { + /* turn on power save mode */ + + if (ifp->if_flags & IFF_DEBUG) + printf("%s: power save mode on for %s\n", + ifp->if_xname, ether_sprintf(wh->i_addr2)); + + ni->ni_pwrsave = IEEE80211_PS_SLEEP; + } + if (ic->ic_set_tim != NULL && + (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0 && + ni->ni_pwrsave != 0) { + /* turn off power save mode, dequeue stored packets */ + + ni->ni_pwrsave = 0; + if (ic->ic_set_tim) + ic->ic_set_tim(ic, ni->ni_associd, 0); + + if (ifp->if_flags & IFF_DEBUG) + printf("%s: power save mode off for %s\n", + ifp->if_xname, ether_sprintf(wh->i_addr2)); + + while (!IF_IS_EMPTY(&ni->ni_savedq)) { + struct mbuf *m; + IF_DEQUEUE(&ni->ni_savedq, m); + IF_ENQUEUE(&ic->ic_pwrsaveq, m); + (*ifp->if_start)(ifp); + } + } + + switch (type) { + case IEEE80211_FC0_TYPE_DATA: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + if (dir != IEEE80211_FC1_DIR_FROMDS) { + ic->ic_stats.is_rx_wrongdir++; + goto out; + } + if ((ifp->if_flags & IFF_SIMPLEX) && + IEEE80211_IS_MULTICAST(wh->i_addr1) && + IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { + /* + * In IEEE802.11 network, multicast packet + * sent from me is broadcasted from AP. + * It should be silently discarded for + * SIMPLEX interface. + */ + ic->ic_stats.is_rx_mcastecho++; + goto out; + } + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + if (dir != IEEE80211_FC1_DIR_NODS) { + ic->ic_stats.is_rx_wrongdir++; + goto out; + } + break; + case IEEE80211_M_HOSTAP: + if (dir != IEEE80211_FC1_DIR_TODS) { + ic->ic_stats.is_rx_wrongdir++; + goto out; + } + /* check if source STA is associated */ + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(("%s: data from unknown src " + "%s\n", __func__, + ether_sprintf(wh->i_addr2))); + /* NB: caller deals with reference */ + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni != NULL) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_AUTHED); + ieee80211_free_node(ic, ni); + } + ic->ic_stats.is_rx_notassoc++; + goto err; + } + if (ni->ni_associd == 0) { + IEEE80211_DPRINTF(("ieee80211_input: " + "data from unassoc src %s\n", + ether_sprintf(wh->i_addr2))); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_NOT_ASSOCED); + ieee80211_unref_node(&ni); + ic->ic_stats.is_rx_notassoc++; + goto err; + } + break; + case IEEE80211_M_MONITOR: + break; + } + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + if (ic->ic_flags & IEEE80211_F_WEPON) { + m = ieee80211_wep_crypt(ifp, m, 0); + if (m == NULL) { + ic->ic_stats.is_rx_wepfail++; + goto err; + } + wh = mtod(m, struct ieee80211_frame *); + } else { + ic->ic_stats.is_rx_nowep++; + goto out; + } + } +#if NBPFILTER > 0 + /* copy to listener after decrypt */ + if (ic->ic_rawbpf) + BPF_MTAP(ic->ic_rawbpf, m); +#endif + m = ieee80211_decap(ifp, m); + if (m == NULL) { + IEEE80211_DPRINTF(("ieee80211_input: " + "decapsulation error for src %s\n", + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_decap++; + goto err; + } + ifp->if_ipackets++; + + /* perform as a bridge within the AP */ + m1 = NULL; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT); + if (m1 == NULL) + ifp->if_oerrors++; + else + m1->m_flags |= M_MCAST; + } else { + ni = ieee80211_find_node(ic, eh->ether_dhost); + if (ni != NULL) { + if (ni->ni_associd != 0) { + m1 = m; + m = NULL; + } + ieee80211_free_node(ic, ni); + } + } + if (m1 != NULL) { +#if !defined(__OpenBSD__) +#ifdef ALTQ + if (ALTQ_IS_ENABLED(&ifp->if_snd)) + altq_etherclassify(&ifp->if_snd, m1, + &pktattr); +#endif +#endif + len = m1->m_pkthdr.len; + IFQ_ENQUEUE(&ifp->if_snd, m1, NULL, error); + if (error) + ifp->if_oerrors++; + else { + if (m != NULL) + ifp->if_omcasts++; + ifp->if_obytes += len; + } + } + } + if (m != NULL) { +#if NBPFILTER > 0 + /* + * If we forward packet into transmitter of the AP, + * we don't need to duplicate for DLT_EN10MB. + */ + if (ifp->if_bpf && m1 == NULL) + BPF_MTAP(ifp->if_bpf, m); +#endif +#if defined(__OpenBSD__) + ether_input_mbuf(ifp, m); +#else + (*ifp->if_input)(ifp, m); +#endif + } + return; + + case IEEE80211_FC0_TYPE_MGT: + if (dir != IEEE80211_FC1_DIR_NODS) { + ic->ic_stats.is_rx_wrongdir++; + goto err; + } + if (ic->ic_opmode == IEEE80211_M_AHDEMO) { + ic->ic_stats.is_rx_ahdemo_mgt++; + goto out; + } + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + /* drop frames without interest */ + if (ic->ic_state == IEEE80211_S_SCAN) { + if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && + subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + ic->ic_stats.is_rx_mgtdiscard++; + goto out; + } + } else { + if (ic->ic_opmode != IEEE80211_M_IBSS && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + ic->ic_stats.is_rx_mgtdiscard++; + goto out; + } + } + + if (ifp->if_flags & IFF_DEBUG) { + /* avoid to print too many frames */ + int doprint = 0; + + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + if (ic->ic_state == IEEE80211_S_SCAN) + doprint = 1; + break; + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + if (ic->ic_opmode == IEEE80211_M_IBSS) + doprint = 1; + break; + default: + doprint = 1; + break; + } +#ifdef IEEE80211_DEBUG + doprint += ieee80211_debug; +#endif + if (doprint) + if_printf(ifp, "received %s from %s rssi %d\n", + ieee80211_mgt_subtype_name[subtype + >> IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(wh->i_addr2), rssi); + } +#if NBPFILTER > 0 + if (ic->ic_rawbpf) + BPF_MTAP(ic->ic_rawbpf, m); +#endif + (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); + m_freem(m); + return; + + case IEEE80211_FC0_TYPE_CTL: + ic->ic_stats.is_rx_ctl++; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + goto out; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if (subtype == IEEE80211_FC0_SUBTYPE_PS_POLL) { + /* XXX statistic */ + /* Dump out a single packet from the host */ + if (ifp->if_flags & IFF_DEBUG) + printf("%s: got power save probe from %s\n", + ifp->if_xname, + ether_sprintf(wh->i_addr2)); + ieee80211_recv_pspoll(ic, m, rssi, rstamp); + } + goto out; + + default: + IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type)); + /* should not come here */ + break; + } + err: + ifp->if_ierrors++; + out: + if (m != NULL) { +#if NBPFILTER > 0 + if (ic->ic_rawbpf) + BPF_MTAP(ic->ic_rawbpf, m); +#endif + m_freem(m); + } +} + +struct mbuf * +ieee80211_decap(struct ifnet *ifp, struct mbuf *m) +{ + struct ether_header *eh; + struct ieee80211_frame wh; + struct llc *llc; + + if (m->m_len < sizeof(wh) + sizeof(*llc)) { + m = m_pullup(m, sizeof(wh) + sizeof(*llc)); + if (m == NULL) + return NULL; + } + memcpy(&wh, mtod(m, caddr_t), sizeof(wh)); + llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh)); + if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && + llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && + llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { + m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh)); + llc = NULL; + } else { + m_adj(m, sizeof(wh) - sizeof(*eh)); + } + eh = mtod(m, struct ether_header *); + switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + /* not yet supported */ + IEEE80211_DPRINTF(("%s: DS to DS\n", __func__)); + m_freem(m); + return NULL; + } +#ifdef ALIGNED_POINTER + if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) { + struct mbuf *n, *n0, **np; + caddr_t newdata; + int off, pktlen; + + n0 = NULL; + np = &n0; + off = 0; + pktlen = m->m_pkthdr.len; + while (pktlen > off) { + if (n0 == NULL) { + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + return NULL; + } +#if defined(__FreeBSD__) + M_MOVE_PKTHDR(n, m); +#elif defined(__OpenBSD__) + M_DUP_PKTHDR(n, m); +#else + M_COPY_PKTHDR(n, m); +#endif + n->m_len = MHLEN; + } else { + MGET(n, M_DONTWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + m_freem(n0); + return NULL; + } + n->m_len = MLEN; + } + if (pktlen - off >= MINCLSIZE) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n0 == NULL) { + newdata = + (caddr_t)ALIGN(n->m_data + sizeof(*eh)) - + sizeof(*eh); + n->m_len -= newdata - n->m_data; + n->m_data = newdata; + } + if (n->m_len > pktlen - off) + n->m_len = pktlen - off; + m_copydata(m, off, n->m_len, mtod(n, caddr_t)); + off += n->m_len; + *np = n; + np = &n->m_next; + } + m_freem(m); + m = n0; + } +#endif /* ALIGNED_POINTER */ + if (llc != NULL) { + eh = mtod(m, struct ether_header *); + eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); + } + return m; +} + +/* + * Install received rate set information in the node's state block. + */ +static int +ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, + u_int8_t *rates, u_int8_t *xrates, int flags) +{ + struct ieee80211_rateset *rs = &ni->ni_rates; + + memset(rs, 0, sizeof(*rs)); + rs->rs_nrates = rates[1]; + memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); + if (xrates != NULL) { + u_int8_t nxrates; + /* + * Tack on 11g extended supported rate element. + */ + nxrates = xrates[1]; + if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { + nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; + IEEE80211_DPRINTF(("%s: extended rate set too large;" + " only using %u of %u rates\n", + __func__, nxrates, xrates[1])); + ic->ic_stats.is_rx_rstoobig++; + } + memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); + rs->rs_nrates += nxrates; + } + return ieee80211_fix_rate(ic, ni, flags); +} + +/* Verify the existence and length of __elem or get out. */ +#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ + if ((__elem) == NULL) { \ + IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n", \ + __func__, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT])); \ + ic->ic_stats.is_rx_elem_missing++; \ + return; \ + } \ + if ((__elem)[1] > (__maxlen)) { \ + IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s " \ + "frame from %s\n", __func__, (__elem)[1], \ + ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + ether_sprintf(wh->i_addr2))); \ + ic->ic_stats.is_rx_elem_toobig++; \ + return; \ + } \ +} while (0) + +#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ + if ((_len) < (_minlen)) { \ + IEEE80211_DPRINTF(("%s: %s frame too short from %s\n", \ + __func__, \ + ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + ether_sprintf(wh->i_addr2))); \ + ic->ic_stats.is_rx_elem_toosmall++; \ + return; \ + } \ +} while (0) + +static void +ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, + struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, + u_int16_t status) +{ + struct ifnet *ifp = &ic->ic_if; + int allocbs; + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: + if (ic->ic_state != IEEE80211_S_RUN || + seq != IEEE80211_AUTH_OPEN_REQUEST) { + IEEE80211_DPRINTF(("%s: discard auth from %s; " + "state %u, seq %u\n", __func__, + ether_sprintf(wh->i_addr2), + ic->ic_state, seq)); + ic->ic_stats.is_rx_bad_auth++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_AUTH, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + + case IEEE80211_M_AHDEMO: + /* should not come here */ + break; + + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN || + seq != IEEE80211_AUTH_OPEN_REQUEST) { + IEEE80211_DPRINTF(("%s: discard auth from %s; " + "state %u, seq %u\n", __func__, + ether_sprintf(wh->i_addr2), + ic->ic_state, seq)); + ic->ic_stats.is_rx_bad_auth++; + return; + } + if (ni == ic->ic_bss) { + ni = ieee80211_alloc_node(ic, wh->i_addr2); + if (ni == NULL) { + ic->ic_stats.is_rx_nodealloc++; + return; + } + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + ni->ni_chan = ic->ic_bss->ni_chan; + allocbs = 1; + } else + allocbs = 0; + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s %s authenticated (open)\n", + ether_sprintf(ni->ni_macaddr), + (allocbs ? "newly" : "already")); + break; + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH || + seq != IEEE80211_AUTH_OPEN_RESPONSE) { + ic->ic_stats.is_rx_bad_auth++; + IEEE80211_DPRINTF(("%s: discard auth from %s; " + "state %u, seq %u\n", __func__, + ether_sprintf(wh->i_addr2), + ic->ic_state, seq)); + return; + } + if (status != 0) { + if_printf(&ic->ic_if, + "open authentication failed (reason %d) for %s\n", + status, + ether_sprintf(wh->i_addr3)); + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_MONITOR: + break; + } +} + +/* TBD send appropriate responses on error? */ +static void +ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, + u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, + u_int32_t rstamp, u_int16_t seq, u_int16_t status) +{ + struct ifnet *ifp = &ic->ic_if; + u_int8_t *challenge = NULL; + int allocbs, i; + + if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { + IEEE80211_DPRINTF(("%s: WEP is off\n", __func__)); + return; + } + + if (frm + 1 < efrm) { + if (frm[1] + 2 > efrm - frm) { + IEEE80211_DPRINTF(("elt %d %d bytes too long\n", + frm[0], (frm[1] + 2) - (int)(efrm - frm))); + ic->ic_stats.is_rx_bad_auth++; + return; + } + if (*frm == IEEE80211_ELEMID_CHALLENGE) + challenge = frm; + frm += frm[1] + 2; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_CHALLENGE: + case IEEE80211_AUTH_SHARED_RESPONSE: + if (challenge == NULL) { + IEEE80211_DPRINTF(("%s: no challenge sent\n", + __func__)); + ic->ic_stats.is_rx_bad_auth++; + return; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DPRINTF(("%s: bad challenge len %d\n", + __func__, challenge[1])); + ic->ic_stats.is_rx_bad_auth++; + return; + } + default: + break; + } + switch (ic->ic_opmode) { + case IEEE80211_M_MONITOR: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + IEEE80211_DPRINTF(("%s: unexpected operating mode\n", + __func__)); + return; + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN) { + IEEE80211_DPRINTF(("%s: not running\n", __func__)); + return; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_REQUEST: + if (ni == ic->ic_bss) { + ni = ieee80211_alloc_node(ic, wh->i_addr2); + if (ni == NULL) { + ic->ic_stats.is_rx_nodealloc++; + return; + } + IEEE80211_ADDR_COPY(ni->ni_bssid, + ic->ic_bss->ni_bssid); + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + ni->ni_chan = ic->ic_bss->ni_chan; + allocbs = 1; + } else + allocbs = 0; + if (ni->ni_challenge == NULL) + ni->ni_challenge = (u_int32_t*)malloc( + IEEE80211_CHALLENGE_LEN, M_DEVBUF, + M_NOWAIT); + if (ni->ni_challenge == NULL) { + IEEE80211_DPRINTF(("challenge alloc failed\n")); + /* XXX statistic */ + return; + } + for (i = IEEE80211_CHALLENGE_LEN / sizeof(u_int32_t); + --i >= 0; ) + ni->ni_challenge[i] = arc4random(); + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s shared key " + "%sauthentication\n", + ether_sprintf(ni->ni_macaddr), + allocbs ? "" : "re"); + break; + case IEEE80211_AUTH_SHARED_RESPONSE: + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(("%s: unknown STA\n", + __func__)); + return; + } + if (ni->ni_challenge == NULL) { + IEEE80211_DPRINTF(( + "%s: no challenge recorded\n", __func__)); + ic->ic_stats.is_rx_bad_auth++; + return; + } + if (memcmp(ni->ni_challenge, &challenge[2], + challenge[1]) != 0) { + IEEE80211_DPRINTF(("%s: challenge mismatch\n", + __func__)); + ic->ic_stats.is_rx_auth_fail++; + return; + } + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s authenticated " + "(shared key)\n", + ether_sprintf(ni->ni_macaddr)); + break; + default: + IEEE80211_DPRINTF(("%s: bad seq %d from %s\n", + __func__, seq, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_bad_auth++; + return; + } + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH) + return; + switch (seq) { + case IEEE80211_AUTH_SHARED_PASS: + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + if (status != 0) { + if_printf(&ic->ic_if, + "%s: shared authentication failed " + "(reason %d) for %s\n", + __func__, status, + ether_sprintf(wh->i_addr3)); + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_AUTH_SHARED_CHALLENGE: + if (ni->ni_challenge == NULL) + ni->ni_challenge = (u_int32_t*)malloc( + challenge[1], M_DEVBUF, M_NOWAIT); + if (ni->ni_challenge == NULL) { + IEEE80211_DPRINTF(( + "%s: challenge alloc failed\n", __func__)); + /* XXX statistic */ + return; + } + memcpy(ni->ni_challenge, &challenge[2], challenge[1]); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + default: + IEEE80211_DPRINTF(("%s: bad seq %d from %s\n", + __func__, seq, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_bad_auth++; + return; + } + break; + } +} + +void +ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_node *ni, + int subtype, int rssi, u_int32_t rstamp) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_frame *wh; + u_int8_t *frm, *efrm; + u_int8_t *ssid, *rates, *xrates; + int reassoc, resp, newassoc, allocbs; + + wh = mtod(m0, struct ieee80211_frame *); + frm = (u_int8_t *)&wh[1]; + efrm = mtod(m0, u_int8_t *) + m0->m_len; + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: { + u_int8_t *tstamp, *bintval, *capinfo, *country; + u_int8_t chan, bchan, fhindex, erp; + u_int16_t fhdwell; + int isprobe; + + if (ic->ic_opmode != IEEE80211_M_IBSS && + ic->ic_state != IEEE80211_S_SCAN) { + /* XXX: may be useful for background scan */ + return; + } + isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP); + + /* + * beacon/probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] capability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] country information + * [tlv] parameter set (FH/DS) + * [tlv] erp information + * [tlv] extended supported rates + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 12); + tstamp = frm; frm += 8; + bintval = frm; frm += 2; + capinfo = frm; frm += 2; + ssid = rates = xrates = country = NULL; + bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); + chan = bchan; + fhdwell = 0; + fhindex = 0; + erp = 0; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_COUNTRY: + country = frm; + break; + case IEEE80211_ELEMID_FHPARMS: + if (ic->ic_phytype == IEEE80211_T_FH) { + fhdwell = (frm[3] << 8) | frm[2]; + chan = IEEE80211_FH_CHAN(frm[4], frm[5]); + fhindex = frm[6]; + } + break; + case IEEE80211_ELEMID_DSPARMS: + /* + * XXX hack this since depending on phytype + * is problematic for multi-mode devices. + */ + if (ic->ic_phytype != IEEE80211_T_FH) + chan = frm[2]; + break; + case IEEE80211_ELEMID_TIM: + break; + case IEEE80211_ELEMID_IBSSPARMS: + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + case IEEE80211_ELEMID_ERP: + if (frm[1] != 1) { + IEEE80211_DPRINTF(("%s: invalid ERP " + "element; length %u, expecting " + "1\n", __func__, frm[1])); + ic->ic_stats.is_rx_elem_toobig++; + break; + } + erp = frm[2]; + break; + default: + IEEE80211_DPRINTF2(("%s: element id %u/len %u " + "ignored\n", __func__, *frm, frm[1])); + ic->ic_stats.is_rx_elem_unknown++; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + if ( +#if IEEE80211_CHAN_MAX < 255 + chan > IEEE80211_CHAN_MAX || +#endif + isclr(ic->ic_chan_active, chan)) { + IEEE80211_DPRINTF(("%s: ignore %s with invalid channel " + "%u\n", __func__, + isprobe ? "probe response" : "beacon", + chan)); + ic->ic_stats.is_rx_badchan++; + return; + } + if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) { + /* + * Frame was received on a channel different from the + * one indicated in the DS params element id; + * silently discard it. + * + * NB: this can happen due to signal leakage. + * But we should take it for FH phy because + * the rssi value should be correct even for + * different hop pattern in FH. + */ + IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked " + "for channel %u\n", __func__, + isprobe ? "probe response" : "beacon", + bchan, chan)); + ic->ic_stats.is_rx_chanmismatch++; + return; + } + + /* + * Use mac and channel for lookup so we collect all + * potential AP's when scanning. Otherwise we may + * see the same AP on multiple channels and will only + * record the last one. We could filter APs here based + * on rssi, etc. but leave that to the end of the scan + * so we can keep the selection criteria in one spot. + * This may result in a bloat of the scanned AP list but + * it shouldn't be too much. + */ + ni = ieee80211_lookup_node(ic, wh->i_addr2, + &ic->ic_channels[chan]); +#ifdef IEEE80211_DEBUG + if (ieee80211_debug && + (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) { + printf("%s: %s%s on chan %u (bss chan %u) ", + __func__, (ni == NULL ? "new " : ""), + isprobe ? "probe response" : "beacon", + chan, bchan); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf(" from %s\n", ether_sprintf(wh->i_addr2)); + printf("%s: caps 0x%x bintval %u erp 0x%x\n", + __func__, letoh16(*(u_int16_t *)capinfo), + letoh16(*(u_int16_t *)bintval), erp); + if (country) { + int i; + printf("%s: country info", __func__); + for (i = 0; i < country[1]; i++) + printf(" %02x", country[i+2]); + printf("\n"); + } + } +#endif + if (ni == NULL) { + ni = ieee80211_alloc_node(ic, wh->i_addr2); + if (ni == NULL) + return; + ni->ni_esslen = ssid[1]; + memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); + memcpy(ni->ni_essid, ssid + 2, ssid[1]); + allocbs = 1; + } else if (ssid[1] != 0 && (isprobe || ni->ni_esslen == 0)) { + /* + * Update ESSID at probe response to adopt hidden AP by + * Lucent/Cisco, which announces null ESSID in beacon. + */ + ni->ni_esslen = ssid[1]; + memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); + memcpy(ni->ni_essid, ssid + 2, ssid[1]); + allocbs = 0; + } else + allocbs = 0; + IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp)); + ni->ni_intval = letoh16(*(u_int16_t *)bintval); + ni->ni_capinfo = letoh16(*(u_int16_t *)capinfo); + /* XXX validate channel # */ + ni->ni_chan = &ic->ic_channels[chan]; + ni->ni_fhdwell = fhdwell; + ni->ni_fhindex = fhindex; + ni->ni_erp = erp; + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); + /* + * When scanning we record results (nodes) with a zero + * refcnt. Otherwise we want to hold the reference for + * ibss neighbors so the nodes don't get released prematurely. + * Anything else can be discarded (XXX and should be handled + * above so we don't do so much work). + */ + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_unref_node(&ni); /* NB: do not free */ + else if (ic->ic_opmode == IEEE80211_M_IBSS && + allocbs && isprobe) { + /* + * Fake an association so the driver can setup it's + * private state. The rate set has been setup above; + * there is no handshake as in ap/station operation. + */ + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, 1); + /* NB: hold reference */ + } else { + /* XXX optimize to avoid work done above */ + ieee80211_free_node(ic, ni); + } + break; + } + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { + u_int8_t rate; + + if (ic->ic_opmode == IEEE80211_M_STA) + return; + if (ic->ic_state != IEEE80211_S_RUN) + return; + + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + ssid = rates = xrates = NULL; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + if (ssid[1] != 0 && + (ssid[1] != ic->ic_bss->ni_esslen || + memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) { +#ifdef IEEE80211_DEBUG + if (ieee80211_debug) { + printf("%s: ssid mismatch ", __func__); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf(" from %s\n", ether_sprintf(wh->i_addr2)); + } +#endif + ic->ic_stats.is_rx_ssidmismatch++; + return; + } + + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni == NULL) + return; + IEEE80211_DPRINTF(("%s: new req from %s\n", + __func__, ether_sprintf(wh->i_addr2))); + allocbs = 1; + } else + allocbs = 0; + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + rate = ieee80211_setup_rates(ic, ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE + | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (rate & IEEE80211_RATE_BASIC) { + IEEE80211_DPRINTF(("%s: rate negotiation failed: %s\n", + __func__,ether_sprintf(wh->i_addr2))); + } else { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); + } + if (allocbs) + ieee80211_free_node(ic, ni); + break; + } + + case IEEE80211_FC0_SUBTYPE_AUTH: { + u_int16_t algo, seq, status; + /* + * auth frame format + * [2] algorithm + * [2] sequence + * [2] status + * [tlv*] challenge + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6); + algo = letoh16(*(u_int16_t *)frm); + seq = letoh16(*(u_int16_t *)(frm + 2)); + status = letoh16(*(u_int16_t *)(frm + 4)); + IEEE80211_DPRINTF(("%s: auth %d seq %d from %s\n", + __func__, algo, seq, ether_sprintf(wh->i_addr2))); + + if (algo == IEEE80211_AUTH_ALG_SHARED) + ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, + rstamp, seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, + status); + else { + IEEE80211_DPRINTF(("%s: unsupported authentication " + "algorithm %d from %s\n", + __func__, algo, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_auth_unsupported++; + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { + u_int16_t capinfo, bintval; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP || + (ic->ic_state != IEEE80211_S_RUN)) + return; + + if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + reassoc = 1; + resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; + } else { + reassoc = 0; + resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; + } + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); + if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { + IEEE80211_DPRINTF(("%s: ignore other bss from %s\n", + __func__, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_assoc_bss++; + return; + } + capinfo = letoh16(*(u_int16_t *)frm); frm += 2; + bintval = letoh16(*(u_int16_t *)frm); frm += 2; + if (reassoc) + frm += 6; /* ignore current AP info */ + ssid = rates = xrates = NULL; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + if (ssid[1] != ic->ic_bss->ni_esslen || + memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) { +#ifdef IEEE80211_DEBUG + if (ieee80211_debug) { + printf("%s: ssid mismatch ", __func__); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf(" from %s\n", ether_sprintf(wh->i_addr2)); + } +#endif + ic->ic_stats.is_rx_ssidmismatch++; + return; + } + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(("%s: not authenticated for %s\n", + __func__, ether_sprintf(wh->i_addr2))); + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni != NULL) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_NOT_AUTHED); + ieee80211_free_node(ic, ni); + } + ic->ic_stats.is_rx_assoc_notauth++; + return; + } + /* discard challenge after association */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + /* XXX per-node cipher suite */ + /* XXX some stations use the privacy bit for handling APs + that suport both encrypted and unencrypted traffic */ + if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || + (capinfo & IEEE80211_CAPINFO_PRIVACY) != + ((ic->ic_flags & IEEE80211_F_WEPON) ? + IEEE80211_CAPINFO_PRIVACY : 0)) { + IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n", + __func__, capinfo, ether_sprintf(wh->i_addr2))); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + ni->ni_associd = 0; + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_STATUS_CAPINFO); + ic->ic_stats.is_rx_assoc_capmismatch++; + return; + } + ieee80211_setup_rates(ic, ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (ni->ni_rates.rs_nrates == 0) { + IEEE80211_DPRINTF(("%s: rate mismatch for %s\n", + __func__, ether_sprintf(wh->i_addr2))); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + ni->ni_associd = 0; + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_STATUS_BASIC_RATE); + ic->ic_stats.is_rx_assoc_norate++; + return; + } + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + ni->ni_intval = bintval; + ni->ni_capinfo = capinfo; + ni->ni_chan = ic->ic_bss->ni_chan; + ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; + ni->ni_fhindex = ic->ic_bss->ni_fhindex; + if (ni->ni_associd == 0) { + u_int16_t aid; + + /* + * It would be clever to search the bitmap + * more efficiently, but this will do for now. + */ + for (aid = 1; aid < ic->ic_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(aid, + ic->ic_aid_bitmap)) + break; + } + + if (ic->ic_bss->ni_associd >= ic->ic_max_aid) { + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_REASON_ASSOC_TOOMANY); + return; + } else { + ni->ni_associd = aid | 0xc000; + IEEE80211_AID_SET(ni->ni_associd, + ic->ic_aid_bitmap); + newassoc = 1; + } + } else + newassoc = 0; + /* XXX for 11g must turn off short slot time if long + slot time sta associates */ + IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s %s associated at aid %d\n", + (newassoc ? "newly" : "already"), + ether_sprintf(ni->ni_macaddr), + ni->ni_associd & ~0xc000); + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, newassoc); + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { + u_int16_t status; + + if (ic->ic_opmode != IEEE80211_M_STA || + ic->ic_state != IEEE80211_S_ASSOC) + return; + + /* + * asresp frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6); + ni = ic->ic_bss; + ni->ni_capinfo = letoh16(*(u_int16_t *)frm); + frm += 2; + + status = letoh16(*(u_int16_t *)frm); + frm += 2; + if (status != 0) { + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, + "association failed (reason %d) for %s\n", + status, ether_sprintf(wh->i_addr3)); + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + return; + } + ni->ni_associd = letoh16(*(u_int16_t *)frm); + frm += 2; + + rates = xrates = NULL; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + ieee80211_setup_rates(ic, ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (ni->ni_rates.rs_nrates != 0) + ieee80211_new_state(ic, IEEE80211_S_RUN, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + } + + case IEEE80211_FC0_SUBTYPE_DEAUTH: { + u_int16_t reason; + /* + * deauth frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2); + reason = letoh16(*(u_int16_t *)frm); + ic->ic_stats.is_rx_deauth++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + ieee80211_new_state(ic, IEEE80211_S_AUTH, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_HOSTAP: + if (ni != ic->ic_bss) { + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s deauthenticated" + " by peer (reason %d)\n", + ether_sprintf(ni->ni_macaddr), reason); + /* node will be free'd on return */ + ieee80211_unref_node(&ni); + } + break; + default: + break; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_DISASSOC: { + u_int16_t reason; + /* + * disassoc frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2); + reason = letoh16(*(u_int16_t *)frm); + ic->ic_stats.is_rx_disassoc++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_HOSTAP: + if (ni != ic->ic_bss) { + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s disassociated" + " by peer (reason %d)\n", + ether_sprintf(ni->ni_macaddr), reason); + IEEE80211_AID_CLR(ni->ni_associd, + ic->ic_aid_bitmap); + ni->ni_associd = 0; + /* XXX node reclaimed how? */ + } + break; + default: + break; + } + break; + } + default: + IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not " + "handled\n", __func__, subtype)); + ic->ic_stats.is_rx_badsubtype++; + break; + } +} + +static void +ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi, + u_int32_t rstamp) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m; + u_int16_t aid; + + if (ic->ic_set_tim == NULL) /* No powersaving functionality */ + return; + + wh = mtod(m0, struct ieee80211_frame *); + + if ((ni = ieee80211_find_node(ic, wh->i_addr2)) == NULL) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %s sent bogus power save poll\n", + ifp->if_xname, ether_sprintf(wh->i_addr2)); + return; + } + + memcpy(&aid, wh->i_dur, sizeof(wh->i_dur)); + if ((aid & 0xc000) != 0xc000) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %s sent bogus aid %x\n", + ifp->if_xname, ether_sprintf(wh->i_addr2), aid); + return; + } + + if (aid != ni->ni_associd) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %s aid %x doesn't match pspoll " + "aid %x\n", + ifp->if_xname, ether_sprintf(wh->i_addr2), + ni->ni_associd, aid); + return; + } + + /* Okay, take the first queued packet and put it out... */ + + IF_DEQUEUE(&ni->ni_savedq, m); + if (m == NULL) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %s sent pspoll, " + "but no packets are saved\n", + ifp->if_xname, ether_sprintf(wh->i_addr2)); + return; + } + wh = mtod(m, struct ieee80211_frame *); + + /* + * If this is the last packet, turn off the TIM fields. + * If there are more packets, set the more packets bit. + */ + + if (IF_IS_EMPTY(&ni->ni_savedq)) { + if (ic->ic_set_tim) + ic->ic_set_tim(ic, ni->ni_associd, 0); + } else { + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + } + + if (ifp->if_flags & IFF_DEBUG) + printf("%s: enqueued power saving packet for station %s\n", + ifp->if_xname, ether_sprintf(ni->ni_macaddr)); + + IF_ENQUEUE(&ic->ic_pwrsaveq, m); + (*ifp->if_start)(ifp); +} +#undef IEEE80211_VERIFY_LENGTH +#undef IEEE80211_VERIFY_ELEMENT diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c new file mode 100644 index 00000000000..44ab3e16fe1 --- /dev/null +++ b/sys/net80211/ieee80211_ioctl.c @@ -0,0 +1,1384 @@ +/* $OpenBSD: ieee80211_ioctl.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.13 2004/03/30 22:57:57 sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $"); +#endif + +/* + * IEEE 802.11 ioctl support (FreeBSD-specific) + */ + +#ifdef __NetBSD__ +#include "opt_inet.h" +#endif + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/systm.h> +#include <sys/endian.h> +#include <sys/proc.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#endif /* __FreeBSD__ */ +#ifndef __OpenBSD__ +#include <netinet/if_inarp.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_ioctl.h> + +#if defined(__FreeBSD__) +#include <dev/wi/if_wavelan_ieee.h> +#elif defined(__OpenBSD__) +#include <dev/ic/if_wi_ieee.h> +#else +#include <dev/ic/wi_ieee.h> +#endif + +/* + * XXX + * Wireless LAN specific configuration interface, which is compatible + * with wicontrol(8). + */ + +int +ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ieee80211com *ic = (void *)ifp; + int i, j, error; + struct ifreq *ifr = (struct ifreq *)data; + struct wi_req wreq; + struct wi_ltv_keys *keys; + struct wi_apinfo *ap; + struct ieee80211_node *ni; + struct ieee80211_rateset *rs; +#ifdef WICACHE + struct wi_sigcache wsc; +#endif /* WICACHE */ +#if 0 /* TBD */ + struct wi_scan_p2_hdr *p2; + struct wi_scan_res *res; +#endif + + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) + return error; + wreq.wi_len = 0; + switch (wreq.wi_type) { + case WI_RID_SERIALNO: + case WI_RID_STA_IDENTITY: + case WI_RID_CARD_ID: + case WI_RID_PROCFRAME: + case WI_RID_PRISM2: + case WI_RID_SYMBOL_DIVERSITY: + case WI_RID_ENH_SECURITY: + case WI_RID_AUTH_STATION: + /* nothing appropriate */ + break; + case WI_RID_NODENAME: + strlcpy((char *)&wreq.wi_val[1], hostname, + sizeof(wreq.wi_val) - sizeof(wreq.wi_val[0])); + wreq.wi_val[0] = htole16(strlen(hostname)); + wreq.wi_len = (1 + strlen(hostname) + 1) / 2; + break; + case WI_RID_CURRENT_SSID: + if (ic->ic_state != IEEE80211_S_RUN) { + wreq.wi_val[0] = 0; + wreq.wi_len = 1; + break; + } + wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); + memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, + ic->ic_bss->ni_esslen); + wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; + break; + case WI_RID_OWN_SSID: + case WI_RID_DESIRED_SSID: + wreq.wi_val[0] = htole16(ic->ic_des_esslen); + memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); + wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; + break; + case WI_RID_CURRENT_BSSID: + if (ic->ic_state == IEEE80211_S_RUN) + IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); + else + memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); + wreq.wi_len = IEEE80211_ADDR_LEN / 2; + break; + case WI_RID_CHANNEL_LIST: + memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) + if (isset(ic->ic_chan_active, i)) { + setbit((u_int8_t *)wreq.wi_val, j); + wreq.wi_len = j / 16 + 1; + } + break; + case WI_RID_OWN_CHNL: + wreq.wi_val[0] = htole16( + ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); + wreq.wi_len = 1; + break; + case WI_RID_CURRENT_CHAN: + wreq.wi_val[0] = htole16( + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); + wreq.wi_len = 1; + break; + case WI_RID_COMMS_QUALITY: + wreq.wi_val[0] = 0; /* quality */ + wreq.wi_val[1] = + htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss)); + wreq.wi_val[2] = 0; /* noise */ + wreq.wi_len = 3; + break; + case WI_RID_PROMISC: + wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); + wreq.wi_len = 1; + break; + case WI_RID_PORTTYPE: + wreq.wi_val[0] = htole16(ic->ic_opmode); + wreq.wi_len = 1; + break; + case WI_RID_MAC_NODE: + IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); + wreq.wi_len = IEEE80211_ADDR_LEN / 2; + break; + case WI_RID_TX_RATE: + if (ic->ic_fixed_rate == -1) + wreq.wi_val[0] = 0; /* auto */ + else + wreq.wi_val[0] = htole16( + (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & + IEEE80211_RATE_VAL) / 2); + wreq.wi_len = 1; + break; + case WI_RID_CUR_TX_RATE: + wreq.wi_val[0] = htole16( + (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & + IEEE80211_RATE_VAL) / 2); + wreq.wi_len = 1; + break; + case WI_RID_FRAG_THRESH: + wreq.wi_val[0] = htole16(ic->ic_fragthreshold); + wreq.wi_len = 1; + break; + case WI_RID_RTS_THRESH: + wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); + wreq.wi_len = 1; + break; + case WI_RID_CREATE_IBSS: + wreq.wi_val[0] = + htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); + wreq.wi_len = 1; + break; + case WI_RID_MICROWAVE_OVEN: + wreq.wi_val[0] = 0; /* no ... not supported */ + wreq.wi_len = 1; + break; + case WI_RID_ROAMING_MODE: + wreq.wi_val[0] = htole16(1); /* enabled ... not supported */ + wreq.wi_len = 1; + break; + case WI_RID_SYSTEM_SCALE: + wreq.wi_val[0] = htole16(1); /* low density ... not supp */ + wreq.wi_len = 1; + break; + case WI_RID_PM_ENABLED: + wreq.wi_val[0] = + htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); + wreq.wi_len = 1; + break; + case WI_RID_MAX_SLEEP: + wreq.wi_val[0] = htole16(ic->ic_lintval); + wreq.wi_len = 1; + break; + case WI_RID_CUR_BEACON_INT: + wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); + wreq.wi_len = 1; + break; + case WI_RID_WEP_AVAIL: + wreq.wi_val[0] = + htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0); + wreq.wi_len = 1; + break; + case WI_RID_CNFAUTHMODE: + wreq.wi_val[0] = htole16(1); /* TODO: open system only */ + wreq.wi_len = 1; + break; + case WI_RID_ENCRYPTION: + wreq.wi_val[0] = + htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0); + wreq.wi_len = 1; + break; + case WI_RID_TX_CRYPT_KEY: + wreq.wi_val[0] = htole16(ic->ic_wep_txkey); + wreq.wi_len = 1; + break; + case WI_RID_DEFLT_CRYPT_KEYS: + keys = (struct wi_ltv_keys *)&wreq; + /* do not show keys to non-root user */ + error = suser(curproc, 0); + if (error) { + memset(keys, 0, sizeof(*keys)); + error = 0; + break; + } + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + keys->wi_keys[i].wi_keylen = + htole16(ic->ic_nw_keys[i].wk_len); + memcpy(keys->wi_keys[i].wi_keydat, + ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); + } + wreq.wi_len = sizeof(*keys) / 2; + break; + case WI_RID_MAX_DATALEN: + wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ + wreq.wi_len = 1; + break; + case WI_RID_DBM_ADJUST: + /* not supported, we just pass rssi value from driver. */ + break; + case WI_RID_IFACE_STATS: + /* XXX: should be implemented in lower drivers */ + break; + case WI_RID_READ_APS: + if (ic->ic_opmode != IEEE80211_M_HOSTAP) { + /* + * Don't return results until active scan completes. + */ + if (ic->ic_state == IEEE80211_S_SCAN && + (ic->ic_flags & IEEE80211_F_ASCAN)) { + error = EINPROGRESS; + break; + } + } + i = 0; + ap = (void *)((char *)wreq.wi_val + sizeof(i)); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) + break; + memset(ap, 0, sizeof(*ap)); + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); + ap->namelen = ic->ic_des_esslen; + if (ic->ic_des_esslen) + memcpy(ap->name, ic->ic_des_essid, + ic->ic_des_esslen); + } else { + IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); + ap->namelen = ni->ni_esslen; + if (ni->ni_esslen) + memcpy(ap->name, ni->ni_essid, + ni->ni_esslen); + } + ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); + ap->signal = (*ic->ic_node_getrssi)(ic, ni); + ap->capinfo = ni->ni_capinfo; + ap->interval = ni->ni_intval; + rs = &ni->ni_rates; + for (j = 0; j < rs->rs_nrates; j++) { + if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { + ap->rate = (rs->rs_rates[j] & + IEEE80211_RATE_VAL) * 5; /* XXX */ + } + } + i++; + ap++; + } + memcpy(wreq.wi_val, &i, sizeof(i)); + wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; + break; +#if 0 + case WI_RID_PRISM2: + wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */ + wreq.wi_len = sizeof(u_int16_t) / 2; + break; + case WI_RID_SCAN_RES: /* compatibility interface */ + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_state == IEEE80211_S_SCAN && + (ic->ic_flags & IEEE80211_F_ASCAN)) { + error = EINPROGRESS; + break; + } + /* NB: we use the Prism2 format so we can return rate info */ + p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; + res = (void *)&p2[1]; + i = 0; + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1)) + break; + res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); + res->wi_noise = 0; + res->wi_signal = (*ic->ic_node_getrssi)(ic, ni); + IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); + res->wi_interval = ni->ni_intval; + res->wi_capinfo = ni->ni_capinfo; + res->wi_ssid_len = ni->ni_esslen; + memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); + /* NB: assumes wi_srates holds <= ni->ni_rates */ + memcpy(res->wi_srates, ni->ni_rates.rs_rates, + sizeof(res->wi_srates)); + if (ni->ni_rates.rs_nrates < 10) + res->wi_srates[ni->ni_rates.rs_nrates] = 0; + res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; + res->wi_rsvd = 0; + res++, i++; + } + p2->wi_rsvd = 0; + p2->wi_reason = i; + wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2; + break; +#endif /* 0 */ +#ifdef WICACHE + case WI_RID_READ_CACHE: + i = 0; + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1) + break; + IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); + memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); + wsc.signal = (*ic->ic_node_getrssi)(ic, ni); + wsc.noise = 0; + wsc.quality = 0; + memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, + &wsc, sizeof(wsc)); + i++; + } + wreq.wi_len = sizeof(wsc) * i / 2; + break; +#endif /* WICACHE */ + case WI_RID_SCAN_APS: + error = EINVAL; + break; + default: + error = EINVAL; + break; + } + if (error == 0) { + wreq.wi_len++; + error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); + } + return error; +} + +static int +findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +{ +#define IEEERATE(_ic,_m,_i) \ + ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) + int i, nrates = ic->ic_sup_rates[mode].rs_nrates; + for (i = 0; i < nrates; i++) + if (IEEERATE(ic, mode, i) == rate) + return i; + return -1; +#undef IEEERATE +} + +/* + * Prepare to do a user-initiated scan for AP's. If no + * current/default channel is setup or the current channel + * is invalid then pick the first available channel from + * the active list as the place to start the scan. + */ +static int +ieee80211_setupscan(struct ieee80211com *ic) +{ + u_char *chanlist = ic->ic_chan_active; + int i; + + if (ic->ic_ibss_chan == NULL || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(chanlist, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + goto found; + } + return EINVAL; /* no active channels */ +found: + ; + } + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + /* + * XXX don't permit a scan to be started unless we + * know the device is ready. For the moment this means + * the device is marked up as this is the required to + * initialize the hardware. It would be better to permit + * scanning prior to being up but that'll require some + * changes to the infrastructure. + */ + return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET; +} + +int +ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ieee80211com *ic = (void *)ifp; + int i, j, len, error, rate; + struct ifreq *ifr = (struct ifreq *)data; + struct wi_ltv_keys *keys; + struct wi_req wreq; + u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; + + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) + return error; + len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; + switch (wreq.wi_type) { + case WI_RID_SERIALNO: + case WI_RID_NODENAME: + return EPERM; + case WI_RID_CURRENT_SSID: + return EPERM; + case WI_RID_OWN_SSID: + case WI_RID_DESIRED_SSID: + if (letoh16(wreq.wi_val[0]) * 2 > len || + letoh16(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { + error = ENOSPC; + break; + } + memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); + ic->ic_des_esslen = letoh16(wreq.wi_val[0]) * 2; + memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); + error = ENETRESET; + break; + case WI_RID_CURRENT_BSSID: + return EPERM; + case WI_RID_OWN_CHNL: + if (len != 2) + return EINVAL; + i = letoh16(wreq.wi_val[0]); + if (i < 0 || + i > IEEE80211_CHAN_MAX || + isclr(ic->ic_chan_active, i)) + return EINVAL; + ic->ic_ibss_chan = &ic->ic_channels[i]; + if (ic->ic_flags & IEEE80211_F_SIBSS) + error = ENETRESET; + break; + case WI_RID_CURRENT_CHAN: + return EPERM; + case WI_RID_COMMS_QUALITY: + return EPERM; + case WI_RID_PROMISC: + if (len != 2) + return EINVAL; + if (ifp->if_flags & IFF_PROMISC) { + if (wreq.wi_val[0] == 0) { + ifp->if_flags &= ~IFF_PROMISC; + error = ENETRESET; + } + } else { + if (wreq.wi_val[0] != 0) { + ifp->if_flags |= IFF_PROMISC; + error = ENETRESET; + } + } + break; + case WI_RID_PORTTYPE: + if (len != 2) + return EINVAL; + switch (letoh16(wreq.wi_val[0])) { + case IEEE80211_M_STA: + break; + case IEEE80211_M_IBSS: + if (!(ic->ic_caps & IEEE80211_C_IBSS)) + return EINVAL; + break; + case IEEE80211_M_AHDEMO: + if (ic->ic_phytype != IEEE80211_T_DS || + !(ic->ic_caps & IEEE80211_C_AHDEMO)) + return EINVAL; + break; + case IEEE80211_M_HOSTAP: + if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) + return EINVAL; + break; + default: + return EINVAL; + } + if (letoh16(wreq.wi_val[0]) != ic->ic_opmode) { + ic->ic_opmode = letoh16(wreq.wi_val[0]); + error = ENETRESET; + } + break; +#if 0 + case WI_RID_MAC_NODE: + if (len != IEEE80211_ADDR_LEN) + return EINVAL; + IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); + /* if_init will copy lladdr into ic_myaddr */ + error = ENETRESET; + break; +#endif + case WI_RID_TX_RATE: + if (len != 2) + return EINVAL; + if (wreq.wi_val[0] == 0) { + /* auto */ + ic->ic_fixed_rate = -1; + break; + } + rate = 2 * letoh16(wreq.wi_val[0]); + if (ic->ic_curmode == IEEE80211_MODE_AUTO) { + /* + * In autoselect mode search for the rate. We take + * the first instance which may not be right, but we + * are limited by the interface. Note that we also + * lock the mode to insure the rate is meaningful + * when it is used. + */ + for (j = IEEE80211_MODE_11A; + j < IEEE80211_MODE_MAX; j++) { + if ((ic->ic_modecaps & (1<<j)) == 0) + continue; + i = findrate(ic, j, rate); + if (i != -1) { + /* lock mode too */ + ic->ic_curmode = j; + goto setrate; + } + } + } else { + i = findrate(ic, ic->ic_curmode, rate); + if (i != -1) + goto setrate; + } + return EINVAL; + setrate: + ic->ic_fixed_rate = i; + error = ENETRESET; + break; + case WI_RID_CUR_TX_RATE: + return EPERM; + case WI_RID_FRAG_THRESH: + if (len != 2) + return EINVAL; + ic->ic_fragthreshold = letoh16(wreq.wi_val[0]); + error = ENETRESET; + break; + case WI_RID_RTS_THRESH: + if (len != 2) + return EINVAL; + ic->ic_rtsthreshold = letoh16(wreq.wi_val[0]); + error = ENETRESET; + break; + case WI_RID_CREATE_IBSS: + if (len != 2) + return EINVAL; + if (wreq.wi_val[0] != 0) { + if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) + return EINVAL; + if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { + ic->ic_flags |= IEEE80211_F_IBSSON; + if (ic->ic_opmode == IEEE80211_M_IBSS && + ic->ic_state == IEEE80211_S_SCAN) + error = ENETRESET; + } + } else { + if (ic->ic_flags & IEEE80211_F_IBSSON) { + ic->ic_flags &= ~IEEE80211_F_IBSSON; + if (ic->ic_flags & IEEE80211_F_SIBSS) { + ic->ic_flags &= ~IEEE80211_F_SIBSS; + error = ENETRESET; + } + } + } + break; + case WI_RID_MICROWAVE_OVEN: + if (len != 2) + return EINVAL; + if (wreq.wi_val[0] != 0) + return EINVAL; /* not supported */ + break; + case WI_RID_ROAMING_MODE: + if (len != 2) + return EINVAL; + if (letoh16(wreq.wi_val[0]) != 1) + return EINVAL; /* not supported */ + break; + case WI_RID_SYSTEM_SCALE: + if (len != 2) + return EINVAL; + if (letoh16(wreq.wi_val[0]) != 1) + return EINVAL; /* not supported */ + break; + case WI_RID_PM_ENABLED: + if (len != 2) + return EINVAL; + if (wreq.wi_val[0] != 0) { + if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) + return EINVAL; + if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { + ic->ic_flags |= IEEE80211_F_PMGTON; + error = ENETRESET; + } + } else { + if (ic->ic_flags & IEEE80211_F_PMGTON) { + ic->ic_flags &= ~IEEE80211_F_PMGTON; + error = ENETRESET; + } + } + break; + case WI_RID_MAX_SLEEP: + if (len != 2) + return EINVAL; + ic->ic_lintval = letoh16(wreq.wi_val[0]); + if (ic->ic_flags & IEEE80211_F_PMGTON) + error = ENETRESET; + break; + case WI_RID_CUR_BEACON_INT: + return EPERM; + case WI_RID_WEP_AVAIL: + return EPERM; + case WI_RID_CNFAUTHMODE: + if (len != 2) + return EINVAL; + if (letoh16(wreq.wi_val[0]) != 1) + return EINVAL; /* TODO: shared key auth */ + break; + case WI_RID_ENCRYPTION: + if (len != 2) + return EINVAL; + if (wreq.wi_val[0] != 0) { + if ((ic->ic_caps & IEEE80211_C_WEP) == 0) + return EINVAL; + if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { + ic->ic_flags |= IEEE80211_F_WEPON; + error = ENETRESET; + } + } else { + if (ic->ic_flags & IEEE80211_F_WEPON) { + ic->ic_flags &= ~IEEE80211_F_WEPON; + error = ENETRESET; + } + } + break; + case WI_RID_TX_CRYPT_KEY: + if (len != 2) + return EINVAL; + i = letoh16(wreq.wi_val[0]); + if (i >= IEEE80211_WEP_NKID) + return EINVAL; + ic->ic_wep_txkey = i; + break; + case WI_RID_DEFLT_CRYPT_KEYS: + if (len != sizeof(struct wi_ltv_keys)) + return EINVAL; + keys = (struct wi_ltv_keys *)&wreq; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + len = letoh16(keys->wi_keys[i].wi_keylen); + if (len != 0 && len < IEEE80211_WEP_KEYLEN) + return EINVAL; + if (len > sizeof(ic->ic_nw_keys[i].wk_key)) + return EINVAL; + } + memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + len = letoh16(keys->wi_keys[i].wi_keylen); + ic->ic_nw_keys[i].wk_len = len; + memcpy(ic->ic_nw_keys[i].wk_key, + keys->wi_keys[i].wi_keydat, len); + } + error = ENETRESET; + break; + case WI_RID_MAX_DATALEN: + if (len != 2) + return EINVAL; + len = letoh16(wreq.wi_val[0]); + if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) + return EINVAL; + if (len != IEEE80211_MAX_LEN) + return EINVAL; /* TODO: fragment */ + ic->ic_fragthreshold = len; + error = ENETRESET; + break; + case WI_RID_IFACE_STATS: + error = EPERM; + break; + case WI_RID_SCAN_REQ: /* XXX wicontrol */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + break; + error = ieee80211_setupscan(ic); + if (error == 0) + error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case WI_RID_SCAN_APS: + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + break; + len--; /* XXX: tx rate? */ + /* FALLTHRU */ + case WI_RID_CHANNEL_LIST: + memset(chanlist, 0, sizeof(chanlist)); + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { + if ((j / 8) >= len) + break; + if (isclr((u_int8_t *)wreq.wi_val, j)) + continue; + if (isclr(ic->ic_chan_active, i)) { + if (wreq.wi_type != WI_RID_CHANNEL_LIST) + continue; + if (isclr(ic->ic_chan_avail, i)) + return EPERM; + } + setbit(chanlist, i); + } + memcpy(ic->ic_chan_active, chanlist, + sizeof(ic->ic_chan_active)); + error = ieee80211_setupscan(ic); + if (wreq.wi_type == WI_RID_CHANNEL_LIST) { + /* NB: ignore error from ieee80211_setupscan */ + error = ENETRESET; + } else if (error == 0) + error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + default: + error = EINVAL; + break; + } + return error; +} + +#ifdef __FreeBSD__ +int +ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ieee80211com *ic = (void *)ifp; + int error = 0; + u_int kid, len; + struct ieee80211req *ireq; + struct ifreq *ifr; + u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + char tmpssid[IEEE80211_NWID_LEN]; + struct ieee80211_channel *chan; + struct ifaddr *ifa; /* XXX */ + + switch (cmd) { + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, (struct ifreq *) data, + &ic->ic_media, cmd); + break; + case SIOCG80211: + ireq = (struct ieee80211req *) data; + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + ireq->i_len = ic->ic_des_esslen; + memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); + break; + default: + ireq->i_len = ic->ic_bss->ni_esslen; + memcpy(tmpssid, ic->ic_bss->ni_essid, + ireq->i_len); + break; + } + error = copyout(tmpssid, ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + case IEEE80211_IOC_WEP: + if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { + ireq->i_val = IEEE80211_WEP_NOSUP; + } else { + if (ic->ic_flags & IEEE80211_F_WEPON) { + ireq->i_val = + IEEE80211_WEP_MIXED; + } else { + ireq->i_val = + IEEE80211_WEP_OFF; + } + } + break; + case IEEE80211_IOC_WEPKEY: + if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { + error = EINVAL; + break; + } + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) { + error = EINVAL; + break; + } + len = (u_int) ic->ic_nw_keys[kid].wk_len; + /* NB: only root can read WEP keys */ + if (suser(curthread) == 0) { + bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); + } else { + bzero(tmpkey, len); + } + ireq->i_len = len; + error = copyout(tmpkey, ireq->i_data, len); + break; + case IEEE80211_IOC_NUMWEPKEYS: + if ((ic->ic_caps & IEEE80211_C_WEP) == 0) + error = EINVAL; + else + ireq->i_val = IEEE80211_WEP_NKID; + break; + case IEEE80211_IOC_WEPTXKEY: + if ((ic->ic_caps & IEEE80211_C_WEP) == 0) + error = EINVAL; + else + ireq->i_val = ic->ic_wep_txkey; + break; + case IEEE80211_IOC_AUTHMODE: + ireq->i_val = IEEE80211_AUTH_OPEN; + break; + case IEEE80211_IOC_CHANNEL: + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + if (ic->ic_opmode == IEEE80211_M_STA) + chan = ic->ic_des_chan; + else + chan = ic->ic_ibss_chan; + break; + default: + chan = ic->ic_bss->ni_chan; + break; + } + ireq->i_val = ieee80211_chan2ieee(ic, chan); + break; + case IEEE80211_IOC_POWERSAVE: + if (ic->ic_flags & IEEE80211_F_PMGTON) + ireq->i_val = IEEE80211_POWERSAVE_ON; + else + ireq->i_val = IEEE80211_POWERSAVE_OFF; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ireq->i_val = ic->ic_lintval; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + ireq->i_val = ic->ic_rtsthreshold; + break; + case IEEE80211_IOC_PROTMODE: + ireq->i_val = ic->ic_protmode; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + error = EINVAL; + else + ireq->i_val = ic->ic_txpower; + break; + default: + error = EINVAL; + break; + } + break; + case SIOCS80211: + error = suser(curproc, 0); + if (error) + break; + ireq = (struct ieee80211req *) data; + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + error = copyin(ireq->i_data, tmpssid, ireq->i_len); + if (error) + break; + memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); + ic->ic_des_esslen = ireq->i_len; + memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); + error = ENETRESET; + break; + case IEEE80211_IOC_WEP: + /* + * These cards only support one mode so + * we just turn wep on if what ever is + * passed in is not OFF. + */ + if (ireq->i_val == IEEE80211_WEP_OFF) { + ic->ic_flags &= ~IEEE80211_F_WEPON; + } else { + ic->ic_flags |= IEEE80211_F_WEPON; + } + error = ENETRESET; + break; + case IEEE80211_IOC_WEPKEY: + if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { + error = EINVAL; + break; + } + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) { + error = EINVAL; + break; + } + if (ireq->i_len > sizeof(tmpkey)) { + error = EINVAL; + break; + } + memset(tmpkey, 0, sizeof(tmpkey)); + error = copyin(ireq->i_data, tmpkey, ireq->i_len); + if (error) + break; + memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey, + sizeof(tmpkey)); + ic->ic_nw_keys[kid].wk_len = ireq->i_len; + error = ENETRESET; + break; + case IEEE80211_IOC_WEPTXKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) { + error = EINVAL; + break; + } + ic->ic_wep_txkey = kid; + error = ENETRESET; + break; +#if 0 + case IEEE80211_IOC_AUTHMODE: + sc->wi_authmode = ireq->i_val; + break; +#endif + case IEEE80211_IOC_CHANNEL: + /* XXX 0xffff overflows 16-bit signed */ + if (ireq->i_val == 0 || + ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || + isclr(ic->ic_chan_active, ireq->i_val)) { + error = EINVAL; + break; + } else + ic->ic_ibss_chan = ic->ic_des_chan = + &ic->ic_channels[ireq->i_val]; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + error = ENETRESET; + break; + default: + if (ic->ic_opmode == IEEE80211_M_STA) { + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + ic->ic_bss->ni_chan != ic->ic_des_chan) + error = ENETRESET; + } else { + if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) + error = ENETRESET; + } + break; + } + break; + case IEEE80211_IOC_POWERSAVE: + switch (ireq->i_val) { + case IEEE80211_POWERSAVE_OFF: + if (ic->ic_flags & IEEE80211_F_PMGTON) { + ic->ic_flags &= ~IEEE80211_F_PMGTON; + error = ENETRESET; + } + break; + case IEEE80211_POWERSAVE_ON: + if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) + error = EINVAL; + else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { + ic->ic_flags |= IEEE80211_F_PMGTON; + error = ENETRESET; + } + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_IOC_POWERSAVESLEEP: + if (ireq->i_val < 0) { + error = EINVAL; + break; + } + ic->ic_lintval = ireq->i_val; + error = ENETRESET; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + if (!(IEEE80211_RTS_MIN < ireq->i_val && + ireq->i_val <= IEEE80211_RTS_MAX + 1)) { + error = EINVAL; + break; + } + ic->ic_rtsthreshold = ireq->i_val; + error = ENETRESET; + break; + case IEEE80211_IOC_PROTMODE: + if (ireq->i_val > IEEE80211_PROT_RTSCTS) { + error = EINVAL; + break; + } + ic->ic_protmode = ireq->i_val; + /* NB: if not operating in 11g this can wait */ + if (ic->ic_curmode == IEEE80211_MODE_11G) + error = ENETRESET; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) { + error = EINVAL; + break; + } + if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && + ireq->i_val < IEEE80211_TXPOWER_MAX)) { + error = EINVAL; + break; + } + ic->ic_txpower = ireq->i_val; + error = ENETRESET; + break; + default: + error = EINVAL; + break; + } + break; + case SIOCGIFGENERIC: + error = ieee80211_cfgget(ifp, cmd, data); + break; + case SIOCSIFGENERIC: + error = suser(curproc, 0); + if (error) + break; + error = ieee80211_cfgset(ifp, cmd, data); + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return error; +} + +#else /* !__FreeBSD__ */ + +int +ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ieee80211com *ic = (void *)ifp; + struct ifreq *ifr = (struct ifreq *)data; + int i, error = 0; + struct ieee80211_nwid nwid; + struct ieee80211_nwkey *nwkey; + struct ieee80211_power *power; + struct ieee80211_bssid *bssid; + struct ieee80211chanreq *chanreq; + struct ieee80211_channel *chan; + struct ieee80211_wepkey keys[IEEE80211_WEP_NKID]; + static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + switch (cmd) { + case SIOCSIFADDR: + case SIOCGIFADDR: +#ifdef __OpenBSD__ + error = ether_ioctl(ifp, &ic->ic_ac, cmd, data); +#else + error = ether_ioctl(ifp, cmd, data); +#endif + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + case SIOCS80211NWID: + if ((error = copyin(ifr->ifr_data, &nwid, sizeof(nwid))) != 0) + break; + if (nwid.i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); + ic->ic_des_esslen = nwid.i_len; + memcpy(ic->ic_des_essid, nwid.i_nwid, nwid.i_len); + error = ENETRESET; + break; + case SIOCG80211NWID: + memset(&nwid, 0, sizeof(nwid)); + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + nwid.i_len = ic->ic_des_esslen; + memcpy(nwid.i_nwid, ic->ic_des_essid, nwid.i_len); + break; + default: + nwid.i_len = ic->ic_bss->ni_esslen; + memcpy(nwid.i_nwid, ic->ic_bss->ni_essid, nwid.i_len); + break; + } + error = copyout(&nwid, ifr->ifr_data, sizeof(nwid)); + break; + case SIOCS80211NWKEY: + nwkey = (struct ieee80211_nwkey *)data; + if ((ic->ic_caps & IEEE80211_C_WEP) == 0 && + nwkey->i_wepon != IEEE80211_NWKEY_OPEN) { + error = EINVAL; + break; + } + /* check and copy keys */ + memset(keys, 0, sizeof(keys)); + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + keys[i].wk_len = nwkey->i_key[i].i_keylen; + if ((keys[i].wk_len > 0 && + keys[i].wk_len < IEEE80211_WEP_KEYLEN) || + keys[i].wk_len > sizeof(keys[i].wk_key)) { + error = EINVAL; + break; + } + if (keys[i].wk_len <= 0) + continue; + if ((error = copyin(nwkey->i_key[i].i_keydat, + keys[i].wk_key, keys[i].wk_len)) != 0) + break; + } + if (error) + break; + i = nwkey->i_defkid - 1; + if (i < 0 || i >= IEEE80211_WEP_NKID || + keys[i].wk_len == 0 || + (keys[i].wk_len == -1 && ic->ic_nw_keys[i].wk_len == 0)) { + if (nwkey->i_wepon != IEEE80211_NWKEY_OPEN) { + error = EINVAL; + break; + } + } else + ic->ic_wep_txkey = i; + /* save the key */ + if (nwkey->i_wepon == IEEE80211_NWKEY_OPEN) + ic->ic_flags &= ~IEEE80211_F_WEPON; + else + ic->ic_flags |= IEEE80211_F_WEPON; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (keys[i].wk_len < 0) + continue; + ic->ic_nw_keys[i].wk_len = keys[i].wk_len; + memcpy(ic->ic_nw_keys[i].wk_key, keys[i].wk_key, + sizeof(keys[i].wk_key)); + } + error = ENETRESET; + break; + case SIOCG80211NWKEY: + nwkey = (struct ieee80211_nwkey *)data; + if (ic->ic_flags & IEEE80211_F_WEPON) + nwkey->i_wepon = IEEE80211_NWKEY_WEP; + else + nwkey->i_wepon = IEEE80211_NWKEY_OPEN; + nwkey->i_defkid = ic->ic_wep_txkey + 1; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + /* do not show any keys to non-root user */ + if ((error = suser(curproc, 0)) != 0) + break; + nwkey->i_key[i].i_keylen = ic->ic_nw_keys[i].wk_len; + if ((error = copyout(ic->ic_nw_keys[i].wk_key, + nwkey->i_key[i].i_keydat, + ic->ic_nw_keys[i].wk_len)) != 0) + break; + } + break; + case SIOCS80211POWER: + power = (struct ieee80211_power *)data; + ic->ic_lintval = power->i_maxsleep; + if (power->i_enabled != 0) { + if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) + error = EINVAL; + else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { + ic->ic_flags |= IEEE80211_F_PMGTON; + error = ENETRESET; + } + } else { + if (ic->ic_flags & IEEE80211_F_PMGTON) { + ic->ic_flags &= ~IEEE80211_F_PMGTON; + error = ENETRESET; + } + } + break; + case SIOCG80211POWER: + power = (struct ieee80211_power *)data; + power->i_enabled = (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0; + power->i_maxsleep = ic->ic_lintval; + break; + case SIOCS80211BSSID: + bssid = (struct ieee80211_bssid *)data; + if (IEEE80211_ADDR_EQ(bssid->i_bssid, empty_macaddr)) + ic->ic_flags &= ~IEEE80211_F_DESBSSID; + else { + ic->ic_flags |= IEEE80211_F_DESBSSID; + IEEE80211_ADDR_COPY(ic->ic_des_bssid, bssid->i_bssid); + } + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + break; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + error = ENETRESET; + break; + default: + if ((ic->ic_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(ic->ic_des_bssid, + ic->ic_bss->ni_bssid)) + error = ENETRESET; + break; + } + break; + case SIOCG80211BSSID: + bssid = (struct ieee80211_bssid *)data; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + IEEE80211_ADDR_COPY(bssid->i_bssid, + ic->ic_myaddr); + else if (ic->ic_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(bssid->i_bssid, + ic->ic_des_bssid); + else + memset(bssid->i_bssid, 0, IEEE80211_ADDR_LEN); + break; + default: + IEEE80211_ADDR_COPY(bssid->i_bssid, + ic->ic_bss->ni_bssid); + break; + } + break; + case SIOCS80211CHANNEL: + chanreq = (struct ieee80211chanreq *)data; + if (chanreq->i_channel == IEEE80211_CHAN_ANY) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + else if (chanreq->i_channel > IEEE80211_CHAN_MAX || + isclr(ic->ic_chan_active, chanreq->i_channel)) { + error = EINVAL; + break; + } else + ic->ic_ibss_chan = ic->ic_des_chan = + &ic->ic_channels[chanreq->i_channel]; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + error = ENETRESET; + break; + default: + if (ic->ic_opmode == IEEE80211_M_STA) { + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + ic->ic_bss->ni_chan != ic->ic_des_chan) + error = ENETRESET; + } else { + if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) + error = ENETRESET; + } + break; + } + break; + case SIOCG80211CHANNEL: + chanreq = (struct ieee80211chanreq *)data; + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + if (ic->ic_opmode == IEEE80211_M_STA) + chan = ic->ic_des_chan; + else + chan = ic->ic_ibss_chan; + break; + default: + chan = ic->ic_bss->ni_chan; + break; + } + chanreq->i_channel = ieee80211_chan2ieee(ic, chan); + break; + case SIOCGIFGENERIC: + error = ieee80211_cfgget(ifp, cmd, data); + break; + case SIOCSIFGENERIC: + error = suser(curproc, 0); + if (error) + break; + error = ieee80211_cfgset(ifp, cmd, data); + break; + case SIOCG80211STATS: + ifr = (struct ifreq *)data; + copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); + break; + case SIOCSIFMTU: + ifr = (struct ifreq *)data; + if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && + ifr->ifr_mtu <= IEEE80211_MTU_MAX)) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + default: + error = EINVAL; + break; + } + return error; +} +#endif /* !__FreeBSD__ */ diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h new file mode 100644 index 00000000000..7716947b961 --- /dev/null +++ b/sys/net80211/ieee80211_ioctl.h @@ -0,0 +1,211 @@ +/* $OpenBSD: ieee80211_ioctl.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $OpenBSD: ieee80211_ioctl.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.5 2004/03/30 22:57:57 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_IOCTL_H_ +#define _NET80211_IEEE80211_IOCTL_H_ + +/* + * IEEE 802.11 ioctls. + */ + +struct ieee80211_stats { + u_int32_t is_rx_badversion; /* rx frame with bad version */ + u_int32_t is_rx_tooshort; /* rx frame too short */ + u_int32_t is_rx_wrongbss; /* rx from wrong bssid */ + u_int32_t is_rx_dup; /* rx discard 'cuz dup */ + u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ + u_int32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ + u_int32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ + u_int32_t is_rx_nowep; /* rx w/ wep but wep !config */ + u_int32_t is_rx_wepfail; /* rx wep processing failed */ + u_int32_t is_rx_decap; /* rx decapsulation failed */ + u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ + u_int32_t is_rx_ctl; /* rx discard ctrl frames */ + u_int32_t is_rx_rstoobig; /* rx rate set truncated */ + u_int32_t is_rx_elem_missing; /* rx required element missing*/ + u_int32_t is_rx_elem_toobig; /* rx element too big */ + u_int32_t is_rx_elem_toosmall; /* rx element too small */ + u_int32_t is_rx_elem_unknown; /* rx element unknown */ + u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */ + u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */ + u_int32_t is_rx_nodealloc; /* rx frame dropped */ + u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ + u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ + u_int32_t is_rx_auth_fail; /* rx sta auth failure */ + u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ + u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ + u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ + u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ + u_int32_t is_rx_deauth; /* rx deauthentication */ + u_int32_t is_rx_disassoc; /* rx disassociation */ + u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ + u_int32_t is_rx_nombuf; /* rx failed for lack of mbuf */ + u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ + u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ + u_int32_t is_rx_bad_auth; /* rx bad auth request */ + u_int32_t is_tx_nombuf; /* tx failed for lack of mbuf */ + u_int32_t is_tx_nonode; /* tx failed for no node */ + u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ + u_int32_t is_scan_active; /* active scans started */ + u_int32_t is_scan_passive; /* passive scans started */ + u_int32_t is_node_timeout; /* nodes timed out inactivity */ + u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ +}; + +#ifdef __FreeBSD__ +/* + * FreeBSD-style ioctls. + */ +/* the first member must be matched with struct ifreq */ +struct ieee80211req { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + u_int16_t i_type; /* req type */ + int16_t i_val; /* Index or simple value */ + int16_t i_len; /* Index or simple value */ + void *i_data; /* Extra data */ +}; +#define SIOCS80211 _IOW('i', 234, struct ieee80211req) +#define SIOCG80211 _IOWR('i', 235, struct ieee80211req) + +#define IEEE80211_IOC_SSID 1 +#define IEEE80211_IOC_NUMSSIDS 2 +#define IEEE80211_IOC_WEP 3 +#define IEEE80211_WEP_NOSUP -1 +#define IEEE80211_WEP_OFF 0 +#define IEEE80211_WEP_ON 1 +#define IEEE80211_WEP_MIXED 2 +#define IEEE80211_IOC_WEPKEY 4 +#define IEEE80211_IOC_NUMWEPKEYS 5 +#define IEEE80211_IOC_WEPTXKEY 6 +#define IEEE80211_IOC_AUTHMODE 7 +#define IEEE80211_IOC_STATIONNAME 8 +#define IEEE80211_IOC_CHANNEL 9 +#define IEEE80211_IOC_POWERSAVE 10 +#define IEEE80211_POWERSAVE_NOSUP -1 +#define IEEE80211_POWERSAVE_OFF 0 +#define IEEE80211_POWERSAVE_CAM 1 +#define IEEE80211_POWERSAVE_PSP 2 +#define IEEE80211_POWERSAVE_PSP_CAM 3 +#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM +#define IEEE80211_IOC_POWERSAVESLEEP 11 +#define IEEE80211_IOC_RTSTHRESHOLD 12 +#define IEEE80211_IOC_PROTMODE 13 +#define IEEE80211_PROTMODE_OFF 0 +#define IEEE80211_PROTMODE_CTS 1 +#define IEEE80211_PROTMODE_RTSCTS 2 +#define IEEE80211_IOC_TXPOWER 14 + +#ifndef IEEE80211_CHAN_ANY +#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ +#endif + +#define SIOCG80211STATS _IOWR('i', 236, struct ifreq) +#endif /* __FreeBSD__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +/* nwid is pointed at by ifr.ifr_data */ +struct ieee80211_nwid { + u_int8_t i_len; + u_int8_t i_nwid[IEEE80211_NWID_LEN]; +}; + +#define SIOCS80211NWID _IOWR('i', 230, struct ifreq) +#define SIOCG80211NWID _IOWR('i', 231, struct ifreq) + +/* the first member must be matched with struct ifreq */ +struct ieee80211_nwkey { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + int i_wepon; /* wep enabled flag */ + int i_defkid; /* default encrypt key id */ + struct { + int i_keylen; + u_int8_t *i_keydat; + } i_key[IEEE80211_WEP_NKID]; +}; +#define SIOCS80211NWKEY _IOW('i', 232, struct ieee80211_nwkey) +#define SIOCG80211NWKEY _IOWR('i', 233, struct ieee80211_nwkey) +/* i_wepon */ +#define IEEE80211_NWKEY_OPEN 0 /* No privacy */ +#define IEEE80211_NWKEY_WEP 1 /* WEP enabled */ +#define IEEE80211_NWKEY_EAP 2 /* EAP enabled */ +#define IEEE80211_NWKEY_PERSIST 0x100 /* designate persist keyset */ + +/* power management parameters */ +struct ieee80211_power { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + int i_enabled; /* 1 == on, 0 == off */ + int i_maxsleep; /* max sleep in ms */ +}; +#define SIOCS80211POWER _IOW('i', 234, struct ieee80211_power) +#define SIOCG80211POWER _IOWR('i', 235, struct ieee80211_power) + +struct ieee80211_auth { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + int i_authtype; +}; + +#define IEEE80211_AUTH_NONE 0 +#define IEEE80211_AUTH_OPEN 1 +#define IEEE80211_AUTH_SHARED 2 + +#define SIOCS80211AUTH _IOW('i', 236, struct ieee80211_auth) +#define SIOCG80211AUTH _IOWR('i', 237, struct ieee80211_auth) + +struct ieee80211chanreq { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + u_int16_t i_channel; +}; + +#ifndef IEEE80211_CHAN_ANY +#define IEEE80211_CHAN_ANY 0xffff +#endif + +#define SIOCS80211CHANNEL _IOW('i', 238, struct ieee80211chanreq) +#define SIOCG80211CHANNEL _IOWR('i', 239, struct ieee80211chanreq) + +struct ieee80211_bssid { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + u_int8_t i_bssid[IEEE80211_ADDR_LEN]; +}; + +#define SIOCS80211BSSID _IOW('i', 240, struct ieee80211_bssid) +#define SIOCG80211BSSID _IOWR('i', 241, struct ieee80211_bssid) + +#define SIOCG80211STATS _IOWR('i', 242, struct ifreq) +#endif + +#endif /* _NET80211_IEEE80211_IOCTL_H_ */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c new file mode 100644 index 00000000000..f143467a576 --- /dev/null +++ b/sys/net80211/ieee80211_node.c @@ -0,0 +1,877 @@ +/* $OpenBSD $ */ +/* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.22 2004/04/05 04:15:55 sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $"); +#endif + +#if defined(__NetBSD__) +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +#ifdef __OpenBSD__ +#include <dev/rndvar.h> +#endif + +static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); +static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *); +static void ieee80211_node_copy(struct ieee80211com *, + struct ieee80211_node *, const struct ieee80211_node *); +static u_int8_t ieee80211_node_getrssi(struct ieee80211com *, + struct ieee80211_node *); + +static void ieee80211_setup_node(struct ieee80211com *ic, + struct ieee80211_node *ni, u_int8_t *macaddr); +static void _ieee80211_free_node(struct ieee80211com *, + struct ieee80211_node *); + +#ifdef __NetBSD__ +MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); +#else +#define M_80211_NODE M_DEVBUF +#endif + +void +ieee80211_node_attach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + +#ifdef __FreeBSD__ + /* XXX need unit */ + IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname); +#endif + TAILQ_INIT(&ic->ic_node); + ic->ic_node_alloc = ieee80211_node_alloc; + ic->ic_node_free = ieee80211_node_free; + ic->ic_node_copy = ieee80211_node_copy; + ic->ic_node_getrssi = ieee80211_node_getrssi; + ic->ic_scangen = 1; +} + +void +ieee80211_node_lateattach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_node *ni; + + ni = (*ic->ic_node_alloc)(ic); + IASSERT(ni != NULL, ("unable to setup inital BSS node")); + ni->ni_chan = IEEE80211_CHAN_ANYC; + ic->ic_bss = ni; + ic->ic_txpower = IEEE80211_TXPOWER_MAX; +} + +void +ieee80211_node_detach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + if (ic->ic_bss != NULL) + (*ic->ic_node_free)(ic, ic->ic_bss); + ieee80211_free_allnodes(ic); +#ifdef __FreeBSD__ + IEEE80211_NODE_LOCK_DESTROY(ic); +#endif +} + +/* + * AP scanning support. + */ + +/* + * Initialize the active channel set based on the set + * of available channels and the current PHY mode. + */ +static void +ieee80211_reset_scan(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + memcpy(ic->ic_chan_scan, ic->ic_chan_active, + sizeof(ic->ic_chan_active)); + /* NB: hack, setup so next_scan starts with the first channel */ + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) + ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; +} + +/* + * Begin an active scan. + */ +void +ieee80211_begin_scan(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + /* + * In all but hostap mode scanning starts off in + * an active mode before switching to passive. + */ + if (ic->ic_opmode != IEEE80211_M_HOSTAP) { + ic->ic_flags |= IEEE80211_F_ASCAN; + ic->ic_stats.is_scan_active++; + } else + ic->ic_stats.is_scan_passive++; + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "begin %s scan\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? + "active" : "passive"); + /* + * Clear scan state and flush any previously seen + * AP's. Note that the latter assumes we don't act + * as both an AP and a station, otherwise we'll + * potentially flush state of stations associated + * with us. + */ + ieee80211_reset_scan(ifp); + ieee80211_free_allnodes(ic); + + /* Scan the next channel. */ + ieee80211_next_scan(ifp); +} + +/* + * Switch to the next channel marked for scanning. + */ +void +ieee80211_next_scan(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_channel *chan; + + chan = ic->ic_bss->ni_chan; + for (;;) { + if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) + chan = &ic->ic_channels[0]; + if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { + /* + * Honor channels marked passive-only + * during an active scan. + */ + if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || + (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) + break; + } + if (chan == ic->ic_bss->ni_chan) { + ieee80211_end_scan(ifp); + return; + } + } + clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); + IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), + ieee80211_chan2ieee(ic, chan))); + ic->ic_bss->ni_chan = chan; + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); +} + +void +ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) +{ + struct ieee80211_node *ni; + struct ifnet *ifp = &ic->ic_if; + + ni = ic->ic_bss; + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "creating ibss\n"); + ic->ic_flags |= IEEE80211_F_SIBSS; + ni->ni_chan = chan; + ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); + if (ic->ic_opmode == IEEE80211_M_IBSS) + ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ + ni->ni_esslen = ic->ic_des_esslen; + memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); + ni->ni_rssi = 0; + ni->ni_rstamp = 0; + memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); + ni->ni_intval = ic->ic_lintval; + ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; + if (ic->ic_flags & IEEE80211_F_WEPON) + ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; + if (ic->ic_phytype == IEEE80211_T_FH) { + ni->ni_fhdwell = 200; /* XXX */ + ni->ni_fhindex = 1; + } + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); +} + +int +ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + u_int8_t rate; + int fail; + + fail = 0; + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) + fail |= 0x01; + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + ni->ni_chan != ic->ic_des_chan) + fail |= 0x01; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + fail |= 0x02; + } else { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) + fail |= 0x02; + } + if (ic->ic_flags & IEEE80211_F_WEPON) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + fail |= 0x04; + } else { + /* XXX does this mean privacy is supported or required? */ + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + fail |= 0x04; + } + rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); + if (rate & IEEE80211_RATE_BASIC) + fail |= 0x08; + if (ic->ic_des_esslen != 0 && + (ni->ni_esslen != ic->ic_des_esslen || + memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) + fail |= 0x10; + if ((ic->ic_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) + fail |= 0x20; +#ifdef IEEE80211_DEBUG + if (ic->ic_if.if_flags & IFF_DEBUG) { + printf(" %c %s", fail ? '-' : '+', + ether_sprintf(ni->ni_macaddr)); + printf(" %s%c", ether_sprintf(ni->ni_bssid), + fail & 0x20 ? '!' : ' '); + printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), + fail & 0x01 ? '!' : ' '); + printf(" %+4d", ni->ni_rssi); + printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, + fail & 0x08 ? '!' : ' '); + printf(" %4s%c", + (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : + (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : + "????", + fail & 0x02 ? '!' : ' '); + printf(" %3s%c ", + (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? + "wep" : "no", + fail & 0x04 ? '!' : ' '); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + printf("%s\n", fail & 0x10 ? "!" : ""); + } +#endif + return fail; +} + +/* + * Complete a scan of potential channels. + */ +void +ieee80211_end_scan(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_node *ni, *nextbs, *selbs; + int i, fail; + + ic->ic_flags &= ~IEEE80211_F_ASCAN; + ni = TAILQ_FIRST(&ic->ic_node); + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX off stack? */ + u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; + /* + * The passive scan to look for existing AP's completed, + * select a channel to camp on. Identify the channels + * that already have one or more AP's and try to locate + * an unnoccupied one. If that fails, pick a random + * channel from the active set. + */ + for (; ni != NULL; ni = nextbs) { + ieee80211_ref_node(ni); + nextbs = TAILQ_NEXT(ni, ni_list); + setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); + ieee80211_free_node(ic, ni); + } + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) + break; + if (i == IEEE80211_CHAN_MAX) { + fail = arc4random() & 3; /* random 0-3 */ + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i) && fail-- == 0) + break; + } + ieee80211_create_ibss(ic, &ic->ic_channels[i]); + return; + } + if (ni == NULL) { + IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); + notfound: + if (ic->ic_opmode == IEEE80211_M_IBSS && + (ic->ic_flags & IEEE80211_F_IBSSON) && + ic->ic_des_esslen != 0) { + ieee80211_create_ibss(ic, ic->ic_ibss_chan); + return; + } + /* + * Reset the list of channels to scan and start again. + */ + ieee80211_reset_scan(ifp); + ieee80211_next_scan(ifp); + return; + } + selbs = NULL; + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n"); + for (; ni != NULL; ni = nextbs) { + ieee80211_ref_node(ni); + nextbs = TAILQ_NEXT(ni, ni_list); + if (ni->ni_fails) { + /* + * The configuration of the access points may change + * during my scan. So delete the entry for the AP + * and retry to associate if there is another beacon. + */ + if (ni->ni_fails++ > 2) + ieee80211_free_node(ic, ni); + continue; + } + if (ieee80211_match_bss(ic, ni) == 0) { + if (selbs == NULL) + selbs = ni; + else if (ni->ni_rssi > selbs->ni_rssi) { + ieee80211_unref_node(&selbs); + selbs = ni; + } else + ieee80211_unref_node(&ni); + } else { + ieee80211_unref_node(&ni); + } + } + if (selbs == NULL) + goto notfound; + (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); + if (ic->ic_opmode == IEEE80211_M_IBSS) { + ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (ic->ic_bss->ni_rates.rs_nrates == 0) { + selbs->ni_fails++; + ieee80211_unref_node(&selbs); + goto notfound; + } + ieee80211_unref_node(&selbs); + /* + * Discard scan set; the nodes have a refcnt of zero + * and have not asked the driver to setup private + * node state. Let them be repopulated on demand either + * through transmission (ieee80211_find_txnode) or receipt + * of a probe response (to be added). + */ + ieee80211_free_allnodes(ic); + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + } else { + ieee80211_unref_node(&selbs); + ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); + } +} + +int +ieee80211_get_rate(struct ieee80211com *ic) +{ + u_int8_t (*rates)[IEEE80211_RATE_MAXSIZE]; + int rate; + + rates = &ic->ic_bss->ni_rates.rs_rates; + + if (ic->ic_fixed_rate != -1) + rate = (*rates)[ic->ic_fixed_rate]; + else if (ic->ic_state == IEEE80211_S_RUN) + rate = (*rates)[ic->ic_bss->ni_txrate]; + else + rate = 0; + + return rate & IEEE80211_RATE_VAL; +} + +static struct ieee80211_node * +ieee80211_node_alloc(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node), + M_80211_NODE, M_NOWAIT); + memset(ni, 0, sizeof(struct ieee80211_node)); + return ni; +} + +static void +ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + FREE(ni, M_80211_NODE); +} + +static void +ieee80211_node_copy(struct ieee80211com *ic, + struct ieee80211_node *dst, const struct ieee80211_node *src) +{ + *dst = *src; + dst->ni_challenge = NULL; +} + +static u_int8_t +ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + return ni->ni_rssi; +} + +static void +ieee80211_setup_node(struct ieee80211com *ic, + struct ieee80211_node *ni, u_int8_t *macaddr) +{ + int hash; + ieee80211_node_critsec_decl(s); + + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); + hash = IEEE80211_NODE_HASH(macaddr); + ni->ni_refcnt = 1; /* mark referenced */ + ieee80211_node_critsec_begin(ic, s); + TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); + LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); + /* + * Note we don't enable the inactive timer when acting + * as a station. Nodes created in this mode represent + * AP's identified while scanning. If we time them out + * then several things happen: we can't return the data + * to users to show the list of AP's we encountered, and + * more importantly, we'll incorrectly deauthenticate + * ourself because the inactivity timer will kick us off. + */ + if (ic->ic_opmode != IEEE80211_M_STA) + ic->ic_inact_timer = IEEE80211_INACT_WAIT; + ieee80211_node_critsec_end(ic, s); +} + +struct ieee80211_node * +ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + if (ni != NULL) + ieee80211_setup_node(ic, ni, macaddr); + else + ic->ic_stats.is_rx_nodealloc++; + return ni; +} + +struct ieee80211_node * +ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + if (ni != NULL) { + ieee80211_setup_node(ic, ni, macaddr); + /* + * Inherit from ic_bss. + */ + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); + ni->ni_chan = ic->ic_bss->ni_chan; + } else + ic->ic_stats.is_rx_nodealloc++; + return ni; +} + +static struct ieee80211_node * +_ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni; + int hash; + +#ifdef __FreeBSD__ + IEEE80211_NODE_LOCK_ASSERT(ic); +#endif /* __FreeBSD__ */ + + hash = IEEE80211_NODE_HASH(macaddr); + LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { + ieee80211_node_incref(ni); /* mark referenced */ + return ni; + } + } + return NULL; +} + +struct ieee80211_node * +ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni; + ieee80211_node_critsec_decl(s); + + ieee80211_node_critsec_begin(ic, s); + ni = _ieee80211_find_node(ic, macaddr); + ieee80211_node_critsec_end(ic, s); + return ni; +} + +/* + * Return a reference to the appropriate node for sending + * a data frame. This handles node discovery in adhoc networks. + */ +struct ieee80211_node * +ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni; + ieee80211_node_critsec_decl(s); + + /* + * The destination address should be in the node table + * unless we are operating in station mode or this is a + * multicast/broadcast frame. + */ + if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) + return ic->ic_bss; + + /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ + ieee80211_node_critsec_begin(ic, s); + ni = _ieee80211_find_node(ic, macaddr); + ieee80211_node_critsec_end(ic, s); + if (ni == NULL && + (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO)) { + /* + * Fake up a node; this handles node discovery in + * adhoc mode. Note that for the driver's benefit + * we we treat this like an association so the driver + * has an opportunity to setup it's private state. + * + * XXX need better way to handle this; issue probe + * request so we can deduce rate set, etc. + */ + ni = ieee80211_dup_bss(ic, macaddr); + if (ni != NULL) { + /* XXX no rate negotiation; just dup */ + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, 1); + } + } + return ni; +} + +/* + * For some types of packet and for some operating modes, it is + * desirable to process a Rx packet using its sender's node-record + * instead of the BSS record, when that is possible. + * + * - AP mode: it is desirable to keep a node-record for every + * authenticated/associated station *in the BSS*. For future use, + * we also track neighboring APs, since they might belong to the + * same ESSID. + * + * - IBSS mode: it is desirable to keep a node-record for every + * station *in the BSS*. + * + * - monitor mode: it is desirable to keep a node-record for every + * sender, regardless of BSS. + * + * - STA mode: the only available node-record is the BSS record, + * ic->ic_bss. + * + * Of all the 802.11 Control packets, only the node-records for + * RTS packets node-record can be looked up. + * + * Return non-zero if the packet's node-record is kept, zero + * otherwise. + */ +static __inline int +ieee80211_needs_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh, + u_int8_t **bssid) +{ + struct ieee80211_node *bss = ic->ic_bss; + int needsnode, rc = 0; + + if (ic->ic_opmode == IEEE80211_M_STA) + return 0; + + needsnode = (ic->ic_opmode == IEEE80211_M_MONITOR); + + *bssid = NULL; + + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_CTL: + return (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_RTS; + + case IEEE80211_FC0_TYPE_MGT: + *bssid = wh->i_addr3; + rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid); + break; + case IEEE80211_FC0_TYPE_DATA: + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + *bssid = wh->i_addr3; + if (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO) + rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid); + break; + case IEEE80211_FC1_DIR_TODS: + *bssid = wh->i_addr1; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid); + break; + case IEEE80211_FC1_DIR_FROMDS: + case IEEE80211_FC1_DIR_DSTODS: + *bssid = wh->i_addr2; + rc = (ic->ic_opmode == IEEE80211_M_HOSTAP); + break; + } + break; + } + return needsnode || rc; +} + +struct ieee80211_node * +ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh) +{ + struct ieee80211_node *ni; + const static u_int8_t zero[IEEE80211_ADDR_LEN]; + u_int8_t *bssid; + ieee80211_node_critsec_decl(s); + + if (!ieee80211_needs_rxnode(ic, wh, &bssid)) + return ieee80211_ref_node(ic->ic_bss); + + ieee80211_node_critsec_begin(ic, s); + ni = _ieee80211_find_node(ic, wh->i_addr2); + ieee80211_node_critsec_end(ic, s); + + if (ni == NULL) { + if (ic->ic_opmode != IEEE80211_M_HOSTAP) { + if ((ni = ieee80211_dup_bss(ic, wh->i_addr2)) != NULL) + IEEE80211_ADDR_COPY(ni->ni_bssid, + (bssid != NULL) ? bssid : zero); + + /* XXX see remarks in ieee80211_find_txnode */ + if (ni != NULL) { + /* XXX no rate negotiation; just dup */ + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, 1); + } + IEEE80211_DPRINTF(("%s: faked-up node %p for %s\n", + __func__, ni, ether_sprintf(wh->i_addr2))); + } + ni = ieee80211_ref_node((ni == NULL) ? ic->ic_bss : ni); + } + IASSERT(ni != NULL, ("%s: null node", __func__)); + return ni; +} + +/* + * Like find but search based on the channel too. + */ +struct ieee80211_node * +ieee80211_lookup_node(struct ieee80211com *ic, + u_int8_t *macaddr, struct ieee80211_channel *chan) +{ + struct ieee80211_node *ni; + int hash; + ieee80211_node_critsec_decl(s); + + hash = IEEE80211_NODE_HASH(macaddr); + ieee80211_node_critsec_begin(ic, s); + LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && + ni->ni_chan == chan) { + ieee80211_node_incref(ni);/* mark referenced */ + break; + } + } + ieee80211_node_critsec_end(ic, s); + return ni; +} + +static void +_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + IASSERT(ni != ic->ic_bss, ("freeing bss node")); + + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + TAILQ_REMOVE(&ic->ic_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + if (!IF_IS_EMPTY(&ni->ni_savedq)) { + IF_PURGE(&ni->ni_savedq); + if (ic->ic_set_tim) + ic->ic_set_tim(ic, ni->ni_associd, 0); + } + if (TAILQ_EMPTY(&ic->ic_node)) + ic->ic_inact_timer = 0; + (*ic->ic_node_free)(ic, ni); +} + +void +ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ieee80211_node_critsec_decl(s); + + IASSERT(ni != ic->ic_bss, ("freeing ic_bss")); + + if (ieee80211_node_decref(ni) == 0) { + ieee80211_node_critsec_begin(ic, s); + _ieee80211_free_node(ic, ni); + ieee80211_node_critsec_end(ic, s); + } +} + +void +ieee80211_free_allnodes(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + ieee80211_node_critsec_decl(s); + + ieee80211_node_critsec_begin(ic, s); + while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) + _ieee80211_free_node(ic, ni); + ieee80211_node_critsec_end(ic, s); +} + +/* + * Timeout inactive nodes. Note that we cannot hold the node + * lock while sending a frame as this would lead to a LOR. + * Instead we use a generation number to mark nodes that we've + * scanned and drop the lock and restart a scan if we have to + * time out a node. Since we are single-threaded by virtue of + * controlling the inactivity timer we can be sure this will + * process each node only once. + */ +void +ieee80211_timeout_nodes(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + ieee80211_node_critsec_decl(s); + u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/ + +restart: + ieee80211_node_critsec_begin(ic, s); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (ni->ni_scangen == gen) /* previously handled */ + continue; + ni->ni_scangen = gen; + if (++ni->ni_inact > ieee80211_inact_max) { + IEEE80211_DPRINTF(("station %s timed out " + "due to inactivity (%u secs)\n", + ether_sprintf(ni->ni_macaddr), + ni->ni_inact)); + /* + * Send a deauthenticate frame. + * + * Drop the node lock before sending the + * deauthentication frame in case the driver takes + * a lock, as this will result in a LOR between the + * node lock and the driver lock. + */ + ieee80211_node_critsec_end(ic, s); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + ieee80211_free_node(ic, ni); + ic->ic_stats.is_node_timeout++; + goto restart; + } + } + if (!TAILQ_EMPTY(&ic->ic_node)) + ic->ic_inact_timer = IEEE80211_INACT_WAIT; + ieee80211_node_critsec_end(ic, s); +} + +void +ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) +{ + struct ieee80211_node *ni; + ieee80211_node_critsec_decl(s); + + ieee80211_node_critsec_begin(ic, s); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) + (*f)(arg, ni); + ieee80211_node_critsec_end(ic, s); +} diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h new file mode 100644 index 00000000000..e1592c8303e --- /dev/null +++ b/sys/net80211/ieee80211_node.h @@ -0,0 +1,206 @@ +/* $OpenBSD: ieee80211_node.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.10 2004/04/05 22:10:26 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_NODE_H_ +#define _NET80211_IEEE80211_NODE_H_ + +#define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */ +#define IEEE80211_TRANS_WAIT 5 /* transition wait */ +#define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */ +#define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT) + +#define IEEE80211_NODE_HASHSIZE 32 +/* simple hash is enough for variation of macaddr */ +#define IEEE80211_NODE_HASH(addr) \ + (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE) + +#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ +#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ + +struct ieee80211_rateset { + u_int8_t rs_nrates; + u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; +}; + +/* + * Node specific information. Note that drivers are expected + * to derive from this structure to add device-specific per-node + * state. This is done by overriding the ic_node_* methods in + * the ieee80211com structure. + */ +struct ieee80211_node { + TAILQ_ENTRY(ieee80211_node) ni_list; + LIST_ENTRY(ieee80211_node) ni_hash; + u_int ni_refcnt; + u_int ni_scangen; /* gen# for timeout scan */ + + /* hardware */ + u_int32_t ni_rstamp; /* recv timestamp */ + u_int8_t ni_rssi; /* recv ssi */ + + /* header */ + u_int8_t ni_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t ni_bssid[IEEE80211_ADDR_LEN]; + + /* beacon, probe response */ + u_int8_t ni_tstamp[8]; /* from last rcv'd beacon */ + u_int16_t ni_intval; /* beacon interval */ + u_int16_t ni_capinfo; /* capabilities */ + u_int8_t ni_esslen; + u_int8_t ni_essid[IEEE80211_NWID_LEN]; + struct ieee80211_rateset ni_rates; /* negotiated rate set */ + u_int8_t *ni_country; /* country information XXX */ + struct ieee80211_channel *ni_chan; + u_int16_t ni_fhdwell; /* FH only */ + u_int8_t ni_fhindex; /* FH only */ + u_int8_t ni_erp; /* 11g only */ + +#ifdef notyet + /* DTIM and contention free period (CFP) */ + u_int8_t ni_dtimperiod; + u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */ + u_int16_t ni_cfpduremain; /* remaining cfp duration */ + u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */ + u_int16_t ni_nextdtim; /* time to next DTIM */ + u_int16_t ni_timoffset; +#endif + + /* power saving mode */ + + u_int8_t ni_pwrsave; + struct ifqueue ni_savedq; /* packets queued for pspoll */ + + /* others */ + u_int16_t ni_associd; /* assoc response */ + u_int16_t ni_txseq; /* seq to be transmitted */ + u_int16_t ni_rxseq; /* seq previous received */ + int ni_fails; /* failure count to associate */ + int ni_inact; /* inactivity mark count */ + int ni_txrate; /* index to ni_rates[] */ + u_int32_t *ni_challenge; /* shared-key challenge */ +}; + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define ieee80211_node_incref(ni) \ + do { \ + int _s = splnet(); \ + (ni)->ni_refcnt++; \ + splx(_s); \ + } while (0) + +static __inline int +ieee80211_node_decref(struct ieee80211_node *ni) +{ + int refcnt, s; + s = splnet(); + refcnt = --ni->ni_refcnt; + splx(s); + return refcnt; +} + +#else +#define ieee80211_node_incref(ni) atomic_add_int(&(ni)->ni_refcnt, 1) +static __inline int +ieee80211_node_decref(struct ieee80211_node *ni) +{ + int orefcnt; + do { + orefcnt = ni->ni_refcnt; + } while (atomic_cmpset_int(&ni->ni_refcnt, orefcnt, orefcnt - 1) == 0); + return orefcnt - 1; +} +#endif + +static __inline struct ieee80211_node * +ieee80211_ref_node(struct ieee80211_node *ni) +{ + ieee80211_node_incref(ni); + return ni; +} + +static __inline void +ieee80211_unref_node(struct ieee80211_node **ni) +{ + ieee80211_node_decref(*ni); + *ni = NULL; /* guard against use */ +} + +#define IEEE80211_NODE_LOCK_INIT(_ic, _name) \ + mtx_init(&(_ic)->ic_nodelock, _name, "802.11 node table", MTX_DEF) +#define IEEE80211_NODE_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_LOCK(_ic) mtx_lock(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_LOCK_ASSERT(_ic) \ + mtx_assert(&(_ic)->ic_nodelock, MA_OWNED) + +struct ieee80211com; + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_80211_NODE); +#endif + +extern void ieee80211_node_attach(struct ifnet *); +extern void ieee80211_node_lateattach(struct ifnet *); +extern void ieee80211_node_detach(struct ifnet *); + +extern void ieee80211_begin_scan(struct ifnet *); +extern void ieee80211_next_scan(struct ifnet *); +extern void ieee80211_end_scan(struct ifnet *); +extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *, + struct ieee80211_frame *); +extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *, + u_int8_t *macaddr, struct ieee80211_channel *); +extern void ieee80211_free_node(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_free_allnodes(struct ieee80211com *); +typedef void ieee80211_iter_func(void *, struct ieee80211_node *); +extern void ieee80211_iterate_nodes(struct ieee80211com *ic, + ieee80211_iter_func *, void *); +extern void ieee80211_timeout_nodes(struct ieee80211com *); + +extern int ieee80211_match_bss(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_create_ibss(struct ieee80211com* , + struct ieee80211_channel *); +#endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c new file mode 100644 index 00000000000..7a236d349c5 --- /dev/null +++ b/sys/net80211/ieee80211_output.c @@ -0,0 +1,671 @@ +/* $OpenBSD: ieee80211_output.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.10 2004/04/02 23:25:39 sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $"); +#endif + +#ifdef __NetBSD__ +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +/* + * Send a management frame to the specified node. The node pointer + * must have a reference as the pointer will be passed to the driver + * and potentially held for a long time. If the frame is successfully + * dispatched to the driver, then it is responsible for freeing the + * reference (and potentially free'ing up any associated storage). + */ +static int +ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, + struct mbuf *m, int type) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_frame *wh; + + IASSERT(ni != NULL, ("null node")); + ni->ni_inact = 0; + + /* + * Yech, hack alert! We want to pass the node down to the + * driver's start routine. If we don't do so then the start + * routine must immediately look it up again and that can + * cause a lock order reversal if, for example, this frame + * is being sent because the station is being timedout and + * the frame being sent is a DEAUTH message. We could stick + * this in an m_tag and tack that on to the mbuf. However + * that's rather expensive to do for every frame so instead + * we stuff it in the rcvif field since outbound frames do + * not (presently) use this. + */ + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) + return ENOMEM; +#ifdef __FreeBSD__ + KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); +#endif + m->m_pkthdr.rcvif = (void *)ni; + + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)&wh->i_dur[0] = 0; + *(u_int16_t *)&wh->i_seq[0] = + htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseq++; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + + if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { + m->m_flags &= ~M_LINK0; + IEEE80211_DPRINTF(("%s: encrypting frame for %s\n", __func__, + ether_sprintf(wh->i_addr1))); + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } + + if (ifp->if_flags & IFF_DEBUG) { + /* avoid to print too many frames */ + if (ic->ic_opmode == IEEE80211_M_IBSS || +#ifdef IEEE80211_DEBUG + ieee80211_debug > 1 || +#endif + (type & IEEE80211_FC0_SUBTYPE_MASK) != + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + if_printf(ifp, "sending %s to %s on channel %u\n", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(ni->ni_macaddr), + ieee80211_chan2ieee(ic, ni->ni_chan)); + } + + IF_ENQUEUE(&ic->ic_mgtq, m); + ifp->if_timer = 1; + (*ifp->if_start)(ifp); + return 0; +} + +/* + * Encapsulate an outbound data frame. The mbuf chain is updated and + * a reference to the destination node is returned. If an error is + * encountered NULL is returned and the node reference will also be NULL. + * + * NB: The caller is responsible for free'ing a returned node reference. + * The convention is ic_bss is not reference counted; the caller must + * maintain that. + */ +struct mbuf * +ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) +{ + struct ieee80211com *ic = (void *)ifp; + struct ether_header eh; + struct ieee80211_frame *wh; + struct ieee80211_node *ni = NULL; + struct llc *llc; + + if (m->m_len < sizeof(struct ether_header)) { + m = m_pullup(m, sizeof(struct ether_header)); + if (m == NULL) { + ic->ic_stats.is_tx_nombuf++; + goto bad; + } + } + memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); + + ni = ieee80211_find_txnode(ic, eh.ether_dhost); + if (ni == NULL) { + IEEE80211_DPRINTF(("%s: no node for dst %s, discard frame\n", + __func__, ether_sprintf(eh.ether_dhost))); + ic->ic_stats.is_tx_nonode++; + goto bad; + } + ni->ni_inact = 0; + + m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh.ether_type; + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) { + ic->ic_stats.is_tx_nombuf++; + goto bad; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; + *(u_int16_t *)&wh->i_dur[0] = 0; + *(u_int16_t *)&wh->i_seq[0] = + htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseq++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + break; + case IEEE80211_M_HOSTAP: + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); + break; + case IEEE80211_M_MONITOR: + goto bad; + } + if (ic->ic_flags & IEEE80211_F_WEPON) + wh->i_fc[1] |= IEEE80211_FC1_WEP; + *pni = ni; + return m; +bad: + if (m != NULL) + m_freem(m); + if (ni && ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + *pni = NULL; + return NULL; +} + +/* + * Add a supported rates element id to a frame. + */ +u_int8_t * +ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + int nrates; + + *frm++ = IEEE80211_ELEMID_RATES; + nrates = rs->rs_nrates; + if (nrates > IEEE80211_RATE_SIZE) + nrates = IEEE80211_RATE_SIZE; + *frm++ = nrates; + memcpy(frm, rs->rs_rates, nrates); + return frm + nrates; +} + +/* + * Add an extended supported rates element id to a frame. + */ +u_int8_t * +ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + /* + * Add an extended supported rates element if operating in 11g mode. + */ + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; + *frm++ = IEEE80211_ELEMID_XRATES; + *frm++ = nrates; + memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); + frm += nrates; + } + return frm; +} + +/* + * Add an ssid elemet to a frame. + */ +static u_int8_t * +ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = len; + memcpy(frm, ssid, len); + return frm + len; +} + +static struct mbuf * +ieee80211_getmbuf(int flags, int type, u_int pktlen) +{ + struct mbuf *m; + + IASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen)); +#ifdef __FreeBSD__ + if (pktlen <= MHLEN) + MGETHDR(m, flags, type); + else + m = m_getcl(flags, type, M_PKTHDR); +#else + MGETHDR(m, flags, type); + if (m != NULL && pktlen > MHLEN) + MCLGET(m, flags); +#endif + return m; +} + +/* + * Send a management frame. The node is for the destination (or ic_bss + * when in station mode). Nodes other than ic_bss have their reference + * count bumped to reflect our use for an indeterminant time. + */ +int +ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + int type, int arg) +{ +#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) + struct ifnet *ifp = &ic->ic_if; + struct mbuf *m; + u_int8_t *frm; + enum ieee80211_phymode mode; + u_int16_t capinfo; + int has_challenge, is_shared_key, ret, timer; + + IASSERT(ni != NULL, ("null node")); + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + if (ni != ic->ic_bss) + ieee80211_ref_node(ni); + timer = 0; + switch (type) { + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + 2 + ic->ic_des_esslen + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); + mode = ieee80211_chan2mode(ic, ni->ni_chan); + frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); + frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (FH/DS) + * [tlv] parameter set (IBSS) + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + 8 + 2 + 2 + 2 + + 2 + ni->ni_esslen + + 2 + IEEE80211_RATE_SIZE + + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3) + + 6 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + + memset(frm, 0, 8); /* timestamp should be filled later */ + frm += 8; + *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); + frm += 2; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, + ic->ic_bss->ni_esslen); + frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); + + if (ic->ic_phytype == IEEE80211_T_FH) { + *frm++ = IEEE80211_ELEMID_FHPARMS; + *frm++ = 5; + *frm++ = ni->ni_fhdwell & 0x00ff; + *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; + *frm++ = IEEE80211_FH_CHANSET( + ieee80211_chan2ieee(ic, ni->ni_chan)); + *frm++ = IEEE80211_FH_CHANPAT( + ieee80211_chan2ieee(ic, ni->ni_chan)); + *frm++ = ni->ni_fhindex; + } else { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + } else { /* IEEE80211_M_HOSTAP */ + /* TODO: TIM */ + *frm++ = IEEE80211_ELEMID_TIM; + *frm++ = 4; /* length */ + *frm++ = 0; /* DTIM count */ + *frm++ = 1; /* DTIM period */ + *frm++ = 0; /* bitmap control */ + *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ + } + frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + break; + + case IEEE80211_FC0_SUBTYPE_AUTH: + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + + has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + ni->ni_challenge != NULL); + + is_shared_key = has_challenge || (ni->ni_challenge != NULL && + arg == IEEE80211_AUTH_SHARED_PASS); + + if (has_challenge) { + MH_ALIGN(m, 2 * 3 + 2 + IEEE80211_CHALLENGE_LEN); + m->m_pkthdr.len = m->m_len = + 2 * 3 + 2 + IEEE80211_CHALLENGE_LEN; + } else { + MH_ALIGN(m, 2 * 3); + m->m_pkthdr.len = m->m_len = 2 * 3; + } + frm = mtod(m, u_int8_t *); + ((u_int16_t *)frm)[0] = + (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) + : htole16(IEEE80211_AUTH_ALG_OPEN); + ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ + ((u_int16_t *)frm)[2] = 0; /* status */ + + if (has_challenge) { + ((u_int16_t *)frm)[3] = + htole16((IEEE80211_CHALLENGE_LEN << 8) | + IEEE80211_ELEMID_CHALLENGE); + memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { + IEEE80211_DPRINTF(( + "%s: request encrypt frame\n", __func__)); + m->m_flags |= M_LINK0; /* WEP-encrypt, please */ + } + } + if (ic->ic_opmode == IEEE80211_M_STA) + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s deauthenticate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg); + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + MH_ALIGN(m, 2); + m->m_pkthdr.len = m->m_len = 2; + *mtod(m, u_int16_t *) = htole16(arg); /* reason */ + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + sizeof(capinfo) + + sizeof(u_int16_t) + + IEEE80211_ADDR_LEN + + 2 + ni->ni_esslen + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + + capinfo = 0; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo |= IEEE80211_CAPINFO_IBSS; + else /* IEEE80211_M_STA */ + capinfo |= IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + /* + * NB: Some 11a AP's reject the request when + * short premable is set. + */ + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + *(u_int16_t *)frm = htole16(ic->ic_lintval); + frm += 2; + + if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); + frm += IEEE80211_ADDR_LEN; + } + + frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + /* + * asreq frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + sizeof(capinfo) + + sizeof(u_int16_t) + + sizeof(u_int16_t) + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + *(u_int16_t *)frm = htole16(arg); /* status */ + frm += 2; + + if (arg == IEEE80211_STATUS_SUCCESS) + *(u_int16_t *)frm = htole16(ni->ni_associd); + frm += 2; + + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + break; + + case IEEE80211_FC0_SUBTYPE_DISASSOC: + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s disassociate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg); + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + senderr(ENOMEM, is_tx_nombuf); + MH_ALIGN(m, 2); + m->m_pkthdr.len = m->m_len = 2; + *mtod(m, u_int16_t *) = htole16(arg); /* reason */ + break; + + default: + IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n", + __func__, type)); + senderr(EINVAL, is_tx_unknownmgt); + /* NOTREACHED */ + } + + ret = ieee80211_mgmt_output(ifp, ni, m, type); + if (ret == 0) { + if (timer) + ic->ic_mgt_timer = timer; + } else { +bad: + if (ni != ic->ic_bss) /* remove ref we added */ + ieee80211_free_node(ic, ni); + } + return ret; +#undef senderr +} + +void +ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m) +{ + /* Store the new packet on our queue, changing the TIM if necessary */ + + if (IF_IS_EMPTY(&ni->ni_savedq)) { + ic->ic_set_tim(ic, ni->ni_associd, 1); + } + if (ni->ni_savedq.ifq_len >= IEEE80211_PS_MAX_QUEUE) { + IF_DROP(&ni->ni_savedq); + m_freem(m); + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: station %s power save queue overflow" + " of size %d drops %d\n", + ic->ic_if.if_xname, + ether_sprintf(ni->ni_macaddr), + IEEE80211_PS_MAX_QUEUE, + ni->ni_savedq.ifq_drops); + } else { + /* Similar to ieee80211_mgmt_output, store the node in + * the rcvif field. + */ + IF_ENQUEUE(&ni->ni_savedq, m); + m->m_pkthdr.rcvif = (void *)ni; + } +} diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c new file mode 100644 index 00000000000..faaee044985 --- /dev/null +++ b/sys/net80211/ieee80211_proto.c @@ -0,0 +1,566 @@ +/* $OpenBSD: ieee80211_proto.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(__FreeBSD__) +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.8 2004/04/02 20:22:25 + sam Exp $"); +#elif defined(__NetBSD__) +__KERNEL_RCSID(0, "$NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $"); +#endif + +/* + * IEEE 802.11 protocol support. + */ + +#if defined(__NetBSD__) +#include "opt_inet.h" +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/proc.h> +#include <sys/sysctl.h> + +#ifdef __FreeBSD__ +#include <machine/atomic.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#if defined(__FreeBSD__) +#include <net/ethernet.h> +#elif defined(__NetBSD__) +#include <net/if_ether.h> +#endif +#include <net/if_llc.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_compat.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +const char *ieee80211_mgt_subtype_name[] = { + "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", + "probe_req", "probe_resp", "reserved#6", "reserved#7", + "beacon", "atim", "disassoc", "auth", + "deauth", "reserved#13", "reserved#14", "reserved#15" +}; +const char *ieee80211_state_name[IEEE80211_S_MAX] = { + "INIT", /* IEEE80211_S_INIT */ + "SCAN", /* IEEE80211_S_SCAN */ + "AUTH", /* IEEE80211_S_AUTH */ + "ASSOC", /* IEEE80211_S_ASSOC */ + "RUN" /* IEEE80211_S_RUN */ +}; + +static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); + +void +ieee80211_proto_attach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + + ifp->if_hdrlen = sizeof(struct ieee80211_frame); + +#ifdef notdef + ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; +#else + ic->ic_rtsthreshold = IEEE80211_RTS_MAX; +#endif + ic->ic_fragthreshold = 2346; /* XXX not used yet */ + ic->ic_fixed_rate = -1; /* no fixed rate */ + ic->ic_protmode = IEEE80211_PROT_CTSONLY; + +#ifdef __FreeBSD__ + mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); +#endif + + /* protocol state change handler */ + ic->ic_newstate = ieee80211_newstate; + + /* initialize management frame handlers */ + ic->ic_recv_mgmt = ieee80211_recv_mgmt; + ic->ic_send_mgmt = ieee80211_send_mgmt; +} + +void +ieee80211_proto_detach(struct ifnet *ifp) +{ + struct ieee80211com *ic = (void *)ifp; + +#ifdef __FreeBSD__ + IF_DRAIN(&ic->ic_mgtq); + mtx_destroy(&ic->ic_mgtq.ifq_mtx); +#else + IF_PURGE(&ic->ic_mgtq); + IF_PURGE(&ic->ic_pwrsaveq); +#endif +} + +void +ieee80211_print_essid(u_int8_t *essid, int len) +{ + int i; + u_int8_t *p; + + if (len > IEEE80211_NWID_LEN) + len = IEEE80211_NWID_LEN; + /* determine printable or not */ + for (i = 0, p = essid; i < len; i++, p++) { + if (*p < ' ' || *p > 0x7e) + break; + } + if (i == len) { + printf("\""); + for (i = 0, p = essid; i < len; i++, p++) + printf("%c", *p); + printf("\""); + } else { + printf("0x"); + for (i = 0, p = essid; i < len; i++, p++) + printf("%02x", *p); + } +} + +void +ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) +{ + struct ieee80211_frame *wh; + int i; + + wh = (struct ieee80211_frame *)buf; + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + printf("NODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_TODS: + printf("TODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s)", ether_sprintf(wh->i_addr1)); + break; + case IEEE80211_FC1_DIR_FROMDS: + printf("FRDS %s", ether_sprintf(wh->i_addr3)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr2)); + break; + case IEEE80211_FC1_DIR_DSTODS: + printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s", ether_sprintf(wh->i_addr2)); + printf("->%s)", ether_sprintf(wh->i_addr1)); + break; + } + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_DATA: + printf(" data"); + break; + case IEEE80211_FC0_TYPE_MGT: + printf(" %s", ieee80211_mgt_subtype_name[ + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT]); + break; + default: + printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); + break; + } + if (wh->i_fc[1] & IEEE80211_FC1_WEP) + printf(" WEP"); + if (rate >= 0) + printf(" %dM", rate / 2); + if (rssi >= 0) + printf(" +%d", rssi); + printf("\n"); + if (len > 0) { + for (i = 0; i < len; i++) { + if ((i & 1) == 0) + printf(" "); + printf("%02x", buf[i]); + } + printf("\n"); + } +} + +int +ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags) +{ +#define RV(v) ((v) & IEEE80211_RATE_VAL) + int i, j, ignore, error; + int okrate, badrate; + struct ieee80211_rateset *srs, *nrs; + u_int8_t r; + + error = 0; + okrate = badrate = 0; + srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + nrs = &ni->ni_rates; + for (i = 0; i < nrs->rs_nrates; ) { + ignore = 0; + if (flags & IEEE80211_F_DOSORT) { + /* + * Sort rates. + */ + for (j = i + 1; j < nrs->rs_nrates; j++) { + if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { + r = nrs->rs_rates[i]; + nrs->rs_rates[i] = nrs->rs_rates[j]; + nrs->rs_rates[j] = r; + } + } + } + r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; + badrate = r; + if (flags & IEEE80211_F_DOFRATE) { + /* + * Apply fixed rate constraint. Note that we do + * not apply the constraint to basic rates as + * otherwise we may not be able to associate if + * the rate set we submit to the AP is invalid + * (e.g. fix rate at 36Mb/s which is not a basic + * rate for 11a operation). + */ + if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 && + ic->ic_fixed_rate >= 0 && + r != RV(srs->rs_rates[ic->ic_fixed_rate])) + ignore++; + } + if (flags & IEEE80211_F_DONEGO) { + /* + * Check against supported rates. + */ + for (j = 0; j < srs->rs_nrates; j++) { + if (r == RV(srs->rs_rates[j])) { + /* + * Overwrite with the supported rate + * value so any basic rate bit is set. + * This insures that response we send + * to stations have the necessary basic + * rate bit set. + */ + nrs->rs_rates[i] = srs->rs_rates[j]; + break; + } + } + if (j == srs->rs_nrates) { + /* + * A rate in the node's rate set is not + * supported. If this is a basic rate and we + * are operating as an AP then this is an error. + * Otherwise we just discard/ignore the rate. + * Note that this is important for 11b stations + * when they want to associate with an 11g AP. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) + error++; + ignore++; + } + } + if (flags & IEEE80211_F_DODEL) { + /* + * Delete unacceptable rates. + */ + if (ignore) { + nrs->rs_nrates--; + for (j = i; j < nrs->rs_nrates; j++) + nrs->rs_rates[j] = nrs->rs_rates[j + 1]; + nrs->rs_rates[j] = 0; + continue; + } + } + if (!ignore) + okrate = nrs->rs_rates[i]; + i++; + } + if (okrate == 0 || error != 0) + return badrate | IEEE80211_RATE_BASIC; + else + return RV(okrate); +#undef RV +} + +static int +ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + ieee80211_node_critsec_decl(s); + + ostate = ic->ic_state; + IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate])); + ic->ic_state = nstate; /* state transition */ + ni = ic->ic_bss; /* NB: no reference held */ + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_INIT: + break; + case IEEE80211_S_RUN: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + break; + case IEEE80211_M_HOSTAP: + ieee80211_node_critsec_begin(ic, s); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (ni->ni_associd == 0) + continue; + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + } + ieee80211_node_critsec_end(ic, s); + break; + default: + break; + } + /* FALLTHRU */ + case IEEE80211_S_ASSOC: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + break; + case IEEE80211_M_HOSTAP: + ieee80211_node_critsec_begin(ic, s); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + } + ieee80211_node_critsec_end(ic, s); + break; + default: + break; + } + /* FALLTHRU */ + case IEEE80211_S_AUTH: + case IEEE80211_S_SCAN: + ic->ic_mgt_timer = 0; +#ifdef __FreeBSD__ + IF_DRAIN(&ic->ic_mgtq); +#else + IF_PURGE(&ic->ic_mgtq); + IF_PURGE(&ic->ic_pwrsaveq); +#endif + if (ic->ic_wep_ctx != NULL) { + free(ic->ic_wep_ctx, M_DEVBUF); + ic->ic_wep_ctx = NULL; + } + ieee80211_free_allnodes(ic); + break; + } + break; + case IEEE80211_S_SCAN: + ic->ic_flags &= ~IEEE80211_F_SIBSS; + /* initialize bss for probe request */ +#if defined(__OpenBSD__) + IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr); +#else + IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr); +#endif + ni->ni_rates = ic->ic_sup_rates[ + ieee80211_chan2mode(ic, ni->ni_chan)]; + ni->ni_associd = 0; + ni->ni_rstamp = 0; + switch (ostate) { + case IEEE80211_S_INIT: + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * AP operation and we already have a channel; + * bypass the scan and startup immediately. + */ + ieee80211_create_ibss(ic, ic->ic_des_chan); + } else { + ieee80211_begin_scan(ifp); + } + break; + case IEEE80211_S_SCAN: + /* scan next */ + if (ic->ic_flags & IEEE80211_F_ASCAN) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); + } + break; + case IEEE80211_S_RUN: + /* beacon miss */ + if (ifp->if_flags & IFF_DEBUG) { + /* XXX bssid clobbered above */ + if_printf(ifp, "no recent beacons from %s;" + " rescanning\n", + ether_sprintf(ic->ic_bss->ni_bssid)); + } + ieee80211_free_allnodes(ic); + /* FALLTHRU */ + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + /* timeout restart scan */ + ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + if (ni != NULL) { + ni->ni_fails++; + ieee80211_unref_node(&ni); + } + ieee80211_begin_scan(ifp); + break; + } + break; + case IEEE80211_S_AUTH: + switch (ostate) { + case IEEE80211_S_INIT: + IEEE80211_DPRINTF(("%s: invalid transition\n", + __func__)); + break; + case IEEE80211_S_SCAN: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + switch (mgt) { + case IEEE80211_FC0_SUBTYPE_AUTH: + /* ??? */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + /* ignore and retry scan on timeout */ + break; + } + break; + case IEEE80211_S_RUN: + switch (mgt) { + case IEEE80211_FC0_SUBTYPE_AUTH: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + ic->ic_state = ostate; /* stay RUN */ + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + /* try to reauth */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + } + break; + } + break; + case IEEE80211_S_ASSOC: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + case IEEE80211_S_ASSOC: + IEEE80211_DPRINTF(("%s: invalid transition\n", + __func__)); + break; + case IEEE80211_S_AUTH: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + break; + case IEEE80211_S_RUN: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + break; + } + break; + case IEEE80211_S_RUN: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_AUTH: + case IEEE80211_S_RUN: + IEEE80211_DPRINTF(("%s: invalid transition\n", + __func__)); + break; + case IEEE80211_S_SCAN: /* adhoc/hostap mode */ + case IEEE80211_S_ASSOC: /* infra mode */ + IASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, + ("%s: bogus xmit rate %u setup\n", __func__, + ni->ni_txrate)); + if (ifp->if_flags & IFF_DEBUG) { + if_printf(ifp, " "); + if (ic->ic_opmode == IEEE80211_M_STA) + printf("associated "); + else + printf("synchronized "); + printf("with %s ssid ", + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(ic->ic_bss->ni_essid, + ni->ni_esslen); + printf(" channel %d start %uMb\n", + ieee80211_chan2ieee(ic, ni->ni_chan), + IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); + } + ic->ic_mgt_timer = 0; + (*ifp->if_start)(ifp); + break; + } + break; + } + return 0; +} diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h new file mode 100644 index 00000000000..22f88d2994e --- /dev/null +++ b/sys/net80211/ieee80211_proto.h @@ -0,0 +1,83 @@ +/* $OpenBSD: ieee80211_proto.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_PROTO_H_ +#define _NET80211_IEEE80211_PROTO_H_ + +/* + * 802.11 protocol implementation definitions. + */ + +enum ieee80211_state { + IEEE80211_S_INIT = 0, /* default state */ + IEEE80211_S_SCAN = 1, /* scanning */ + IEEE80211_S_AUTH = 2, /* try to authenticate */ + IEEE80211_S_ASSOC = 3, /* try to assoc */ + IEEE80211_S_RUN = 4, /* associated */ +}; +#define IEEE80211_S_MAX (IEEE80211_S_RUN+1) + +#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \ + ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg)) + +extern const char *ieee80211_mgt_subtype_name[]; + +extern void ieee80211_proto_attach(struct ifnet *); +extern void ieee80211_proto_detach(struct ifnet *); + +struct ieee80211_node; +extern void ieee80211_input(struct ifnet *, struct mbuf *, + struct ieee80211_node *, int, u_int32_t); +extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *, int, int, u_int32_t); +extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, + int, int); +extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *, + struct ieee80211_node **); +extern void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, + struct mbuf *); +extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *); +extern u_int8_t *ieee80211_add_rates(u_int8_t *frm, + const struct ieee80211_rateset *); +#define ieee80211_new_state(_ic, _nstate, _arg) \ + (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) +extern u_int8_t *ieee80211_add_xrates(u_int8_t *frm, + const struct ieee80211_rateset *); +extern void ieee80211_print_essid(u_int8_t *, int); +extern void ieee80211_dump_pkt(u_int8_t *, int, int, int); + +extern const char *ieee80211_state_name[IEEE80211_S_MAX]; +#endif /* _NET80211_IEEE80211_PROTO_H_ */ diff --git a/sys/net80211/ieee80211_radiotap.h b/sys/net80211/ieee80211_radiotap.h new file mode 100644 index 00000000000..6e12236cafa --- /dev/null +++ b/sys/net80211/ieee80211_radiotap.h @@ -0,0 +1,213 @@ +/* $OpenBSD: ieee80211_radiotap.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.3 2004/04/05 22:13:21 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.9 2004/06/06 04:13:28 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#ifndef _NET_IF_IEEE80211RADIOTAP_H_ +#define _NET_IF_IEEE80211RADIOTAP_H_ + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ +#ifdef _KERNEL +#ifndef DLT_IEEE802_11_RADIO +#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ +#endif +#endif /* _KERNEL */ + +/* XXX tcpdump/libpcap do not tolerate variable-length headers, + * yet, so we pad every radiotap header to 64 bytes. Ugh. + */ +#define IEEE80211_RADIOTAP_HDRLEN 64 + +/* The radio capture header precedes the 802.11 header. */ +struct ieee80211_radiotap_header { + u_int8_t it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + u_int8_t it_pad; + u_int16_t it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + u_int32_t it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +} __attribute__((__packed__)); + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT u_int64_t microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x u_int16_t MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS u_int16_t see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u_int8_t 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u_int8_t decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u_int8_t decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_BARKER_CODE_LOCK u_int16_t unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION u_int16_t unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u_int8_t bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u_int8_t antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_FCS u_int32_t data + * + * FCS from frame in network byte order. + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_FCS = 14, + IEEE80211_RADIOTAP_EXT = 31, +}; + +#ifndef _KERNEL +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ +#endif /* !_KERNEL */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ + +#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */ diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c new file mode 100644 index 00000000000..6d99cc28fd2 --- /dev/null +++ b/sys/net80211/ieee80211_rssadapt.c @@ -0,0 +1,409 @@ +/* $OpenBSD: ieee80211_rssadapt.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_rssadapt.c,v 1.7 2004/05/25 04:33:59 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> +#ifdef __FreeBSD__ +#include <net/ethernet.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <netinet/if_ether.h> +#else +#include <net/if_ether.h> +#endif +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_compat.h> +#include <net80211/ieee80211_rssadapt.h> + +#ifdef interpolate +#undef interpolate +#endif +#define interpolate(parm, old, new) ((parm##_old * (old) + \ + (parm##_denom - parm##_old) * (new)) / \ + parm##_denom) + +#ifdef IEEE80211_DEBUG +static struct timeval lastrateadapt; /* time of last rate adaptation msg */ +static int currssadaptps = 0; /* rate-adaptation msgs this second */ +static int ieee80211_adaptrate = 4; /* rate-adaptation max msgs/sec */ + +#define RSSADAPT_DO_PRINT() \ + ((ieee80211_rssadapt_debug > 0) && \ + ppsratecheck(&lastrateadapt, &currssadaptps, ieee80211_adaptrate)) +#define RSSADAPT_PRINTF(X) \ + if (RSSADAPT_DO_PRINT()) \ + printf X + +int ieee80211_rssadapt_debug = 0; + +#else +#define RSSADAPT_DO_PRINT() (0) +#define RSSADAPT_PRINTF(X) +#endif + +static struct ieee80211_rssadapt_expavgctl master_expavgctl = { + rc_decay_denom : 16, + rc_decay_old : 15, + rc_thresh_denom : 8, + rc_thresh_old : 4, + rc_avgrssi_denom : 8, + rc_avgrssi_old : 4 +}; + +#ifdef __NetBSD__ +#ifdef IEEE80211_DEBUG +/* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */ +static int +sysctl_ieee80211_rssadapt_debug(SYSCTLFN_ARGS) +{ + int error, t; + struct sysctlnode node; + + node = *rnode; + t = *(int*)rnode->sysctl_data; + node.sysctl_data = &t; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + IEEE80211_DPRINTF(("%s: t = %d, nodenum = %d, rnodenum = %d\n", + __func__, t, node.sysctl_num, rnode->sysctl_num)); + + if (t < 0 || t > 2) + return (EINVAL); + *(int*)rnode->sysctl_data = t; + + return (0); +} +#endif /* IEEE80211_DEBUG */ + +/* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */ +static int +sysctl_ieee80211_rssadapt_expavgctl(SYSCTLFN_ARGS) +{ + struct ieee80211_rssadapt_expavgctl rc; + int error; + struct sysctlnode node; + + node = *rnode; + rc = *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data; + node.sysctl_data = &rc; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + IEEE80211_DPRINTF(("%s: decay = %d/%d, thresh = %d/%d, " + "avgrssi = %d/%d, nodenum = %d, rnodenum = %d\n", + __func__, rc.rc_decay_old, rc.rc_decay_denom, + rc.rc_thresh_old, rc.rc_thresh_denom, + rc.rc_avgrssi_old, rc.rc_avgrssi_denom, + node.sysctl_num, rnode->sysctl_num)); + + if (rc.rc_decay_old < 0 || + rc.rc_decay_denom < rc.rc_decay_old) + return (EINVAL); + + if (rc.rc_thresh_old < 0 || + rc.rc_thresh_denom < rc.rc_thresh_old) + return (EINVAL); + + if (rc.rc_avgrssi_old < 0 || + rc.rc_avgrssi_denom < rc.rc_avgrssi_old) + return (EINVAL); + + *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data = rc; + + return (0); +} + +/* + * Setup sysctl(3) MIB, net.ieee80211.* + * + * TBD condition CTLFLAG_PERMANENT on being an LKM or not + */ +SYSCTL_SETUP(sysctl_ieee80211_rssadapt, + "sysctl ieee80211 rssadapt subtree setup") +{ + int rc; + struct sysctlnode *node; + + if ((rc = sysctl_createv(clog, 0, NULL, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "net", NULL, + NULL, 0, NULL, 0, CTL_NET, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &node, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "link", NULL, + NULL, 0, NULL, 0, PF_LINK, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &node, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211", NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &node, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "rssadapt", + SYSCTL_DESCR("Received Signal Strength adaptation controls"), + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + +#ifdef IEEE80211_DEBUG + /* control debugging printfs */ + if ((rc = sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "debug", + SYSCTL_DESCR("Enable RSS adaptation debugging output"), + sysctl_ieee80211_rssadapt_debug, 0, &ieee80211_rssadapt_debug, 0, + CTL_CREATE, CTL_EOL)) != 0) + goto err; +#endif /* IEEE80211_DEBUG */ + + /* control rate of decay for exponential averages */ + if ((rc = sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT, + "expavgctl", SYSCTL_DESCR("RSS exponential averaging control"), + sysctl_ieee80211_rssadapt_expavgctl, 0, + &master_expavgctl, sizeof(master_expavgctl), CTL_CREATE, + CTL_EOL)) != 0) + goto err; + + return; +err: + printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); +} +#endif /* __NetBSD__ */ + +int +ieee80211_rssadapt_choose(struct ieee80211_rssadapt *ra, + struct ieee80211_rateset *rs, struct ieee80211_frame *wh, u_int len, + int fixed_rate, const char *dvname, int do_not_adapt) +{ + u_int16_t (*thrs)[IEEE80211_RATE_SIZE]; + int flags = 0, i, rateidx = 0, thridx, top; + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) + flags |= IEEE80211_RATE_BASIC; + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thridx = i; + if (len <= top) + break; + } + + thrs = &ra->ra_rate_thresh[thridx]; + + if (fixed_rate != -1) { + if ((rs->rs_rates[fixed_rate] & flags) == flags) { + rateidx = fixed_rate; + goto out; + } + flags |= IEEE80211_RATE_BASIC; + i = fixed_rate; + } else + i = rs->rs_nrates; + + while (--i >= 0) { + rateidx = i; + if ((rs->rs_rates[i] & flags) != flags) + continue; + if (do_not_adapt) + break; + if ((*thrs)[i] < ra->ra_avg_rssi) + break; + } + +out: +#ifdef IEEE80211_DEBUG + if (ieee80211_rssadapt_debug && dvname != NULL) { + printf("%s: dst %s threshold[%d, %d.%d] %d < %d\n", + dvname, ether_sprintf(wh->i_addr1), len, + (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) / 2, + (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) * 5 % 10, + (*thrs)[rateidx], ra->ra_avg_rssi); + } +#endif /* IEEE80211_DEBUG */ + return rateidx; +} + +void +ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt *ra) +{ + long interval; + + ra->ra_pktrate = + (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2; + ra->ra_nfail = ra->ra_nok = 0; + + /* a node is eligible for its rate to be raised every 1/10 to 10 + * seconds, more eligible in proportion to recent packet rates. + */ + interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate)); + ra->ra_raise_interval.tv_sec = interval / (1000 * 1000); + ra->ra_raise_interval.tv_usec = interval % (1000 * 1000); +} + +void +ieee80211_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_rssadapt *ra, int rssi) +{ +#ifdef IEEE80211_DEBUG + int last_avg_rssi = ra->ra_avg_rssi; +#endif + + ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi, + ra->ra_avg_rssi, (rssi << 8)); + + RSSADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n", + ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr), + rssi, last_avg_rssi, ra->ra_avg_rssi)); +} + +/* + * Adapt the data rate to suit the conditions. When a transmitted + * packet is dropped after IEEE80211_RSSADAPT_RETRY_LIMIT retransmissions, + * raise the RSS threshold for transmitting packets of similar length at + * the same data rate. + */ +void +ieee80211_rssadapt_lower_rate(struct ieee80211com *ic, + struct ieee80211_node *ni, struct ieee80211_rssadapt *ra, + struct ieee80211_rssdesc *id) +{ + struct ieee80211_rateset *rs = &ni->ni_rates; + u_int16_t last_thr; + u_int i, thridx, top; + + ra->ra_nfail++; + + if (id->id_rateidx >= rs->rs_nrates) { + RSSADAPT_PRINTF(("ieee80211_rssadapt_lower_rate: " + "%s rate #%d > #%d out of bounds\n", + ether_sprintf(ni->ni_macaddr), id->id_rateidx, + rs->rs_nrates - 1)); + return; + } + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thridx = i; + if (id->id_len <= top) + break; + } + + last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx]; + ra->ra_rate_thresh[thridx][id->id_rateidx] = + interpolate(master_expavgctl.rc_thresh, last_thr, + (id->id_rssi << 8)); + + RSSADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n", + ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr), + id->id_rssi, id->id_len, + (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) / 2, + (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) * 5 % 10, + last_thr, ra->ra_rate_thresh[thridx][id->id_rateidx])); +} + +void +ieee80211_rssadapt_raise_rate(struct ieee80211com *ic, + struct ieee80211_rssadapt *ra, struct ieee80211_rssdesc *id) +{ + u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr; + struct ieee80211_node *ni = id->id_node; + struct ieee80211_rateset *rs = &ni->ni_rates; + int i, rate, top; +#ifdef IEEE80211_DEBUG + int j; +#endif + + ra->ra_nok++; + + if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval)) + return; + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thrs = &ra->ra_rate_thresh[i]; + if (id->id_len <= top) + break; + } + + if (id->id_rateidx + 1 < rs->rs_nrates && + (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) { + rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL); + + RSSADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d ", + ic->ic_if.if_xname, + IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER* i), + rate / 2, rate * 5 % 10, (*thrs)[id->id_rateidx + 1])); + oldthr = (*thrs)[id->id_rateidx + 1]; + if ((*thrs)[id->id_rateidx] == 0) + newthr = ra->ra_avg_rssi; + else + newthr = (*thrs)[id->id_rateidx]; + (*thrs)[id->id_rateidx + 1] = + interpolate(master_expavgctl.rc_decay, oldthr, newthr); + + RSSADAPT_PRINTF(("-> %d\n", (*thrs)[id->id_rateidx + 1])); + } + +#ifdef IEEE80211_DEBUG + if (RSSADAPT_DO_PRINT()) { + printf("%s: dst %s thresholds\n", ic->ic_if.if_xname, + ether_sprintf(ni->ni_macaddr)); + for (i = 0; i < IEEE80211_RSSADAPT_BKTS; i++) { + printf("%d-byte", IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER * i)); + for (j = 0; j < rs->rs_nrates; j++) { + rate = (rs->rs_rates[j] & IEEE80211_RATE_VAL); + printf(", T[%d.%d] = %d", rate / 2, + rate * 5 % 10, ra->ra_rate_thresh[i][j]); + } + printf("\n"); + } + } +#endif /* IEEE80211_DEBUG */ +} diff --git a/sys/net80211/ieee80211_rssadapt.h b/sys/net80211/ieee80211_rssadapt.h new file mode 100644 index 00000000000..37816630557 --- /dev/null +++ b/sys/net80211/ieee80211_rssadapt.h @@ -0,0 +1,102 @@ +/* $OpenBSD: ieee80211_rssadapt.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_rssadapt.h,v 1.3 2004/05/06 03:03:20 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* Data-rate adaptation loosely based on "Link Adaptation Strategy + * for IEEE 802.11 WLAN via Received Signal Strength Measurement" + * by Javier del Prado Pavon and Sunghyun Choi. + */ + +/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */ +#define IEEE80211_RSSADAPT_BKTS 3 +#define IEEE80211_RSSADAPT_BKT0 128 +#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */ + +#define ieee80211_rssadapt_thresh_new \ + (ieee80211_rssadapt_thresh_denom - ieee80211_rssadapt_thresh_old) +#define ieee80211_rssadapt_decay_new \ + (ieee80211_rssadapt_decay_denom - ieee80211_rssadapt_decay_old) +#define ieee80211_rssadapt_avgrssi_new \ + (ieee80211_rssadapt_avgrssi_denom - ieee80211_rssadapt_avgrssi_old) + +struct ieee80211_rssadapt_expavgctl { + /* RSS threshold decay. */ + u_int rc_decay_denom; + u_int rc_decay_old; + /* RSS threshold update. */ + u_int rc_thresh_denom; + u_int rc_thresh_old; + /* RSS average update. */ + u_int rc_avgrssi_denom; + u_int rc_avgrssi_old; +}; + +struct ieee80211_rssadapt { + /* exponential average RSSI << 8 */ + u_int16_t ra_avg_rssi; + /* Tx failures in this update interval */ + u_int32_t ra_nfail; + /* Tx successes in this update interval */ + u_int32_t ra_nok; + /* exponential average packets/second */ + u_int32_t ra_pktrate; + /* RSSI threshold for each Tx rate */ + u_int16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS] + [IEEE80211_RATE_SIZE]; + struct timeval ra_last_raise; + struct timeval ra_raise_interval; +}; + +/* Properties of a Tx packet, for link adaptation. */ +struct ieee80211_rssdesc { + u_int id_len; /* Tx packet length */ + u_int id_rateidx; /* index into ni->ni_rates */ + struct ieee80211_node *id_node; /* destination STA MAC */ + u_int8_t id_rssi; /* destination STA avg RSS @ + * Tx time + */ +}; + +void ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt *); +void ieee80211_rssadapt_input(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_rssadapt *, int); +void ieee80211_rssadapt_lower_rate(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_rssadapt *, + struct ieee80211_rssdesc *); +void ieee80211_rssadapt_raise_rate(struct ieee80211com *, + struct ieee80211_rssadapt *, struct ieee80211_rssdesc *); +int ieee80211_rssadapt_choose(struct ieee80211_rssadapt *, + struct ieee80211_rateset *, struct ieee80211_frame *, u_int, int, + const char *, int); +#ifdef IEEE80211_DEBUG +extern int ieee80211_rssadapt_debug; +#endif /* IEEE80211_DEBUG */ diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h new file mode 100644 index 00000000000..e2a483ca267 --- /dev/null +++ b/sys/net80211/ieee80211_var.h @@ -0,0 +1,320 @@ +/* $OpenBSD: ieee80211_var.h,v 1.1 2004/06/22 22:53:52 millert Exp $ */ +/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */ + +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.15 2004/04/05 22:10:26 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_VAR_H_ +#define _NET80211_IEEE80211_VAR_H_ + +/* + * Definitions for IEEE 802.11 drivers. + */ + +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */ +#include <net80211/ieee80211_node.h> +#include <net80211/ieee80211_proto.h> + +#define IEEE80211_CHAN_MAX 255 +#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ +#define IEEE80211_CHAN_ANYC \ + ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) + +#define IEEE80211_TXPOWER_MAX 100 /* max power */ +#define IEEE80211_TXPOWER_MIN 0 /* kill radio (if possible) */ + +enum ieee80211_phytype { + IEEE80211_T_DS, /* direct sequence spread spectrum */ + IEEE80211_T_FH, /* frequency hopping */ + IEEE80211_T_OFDM, /* frequency division multiplexing */ + IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ +}; +#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ + +/* XXX not really a mode; there are really multiple PHY's */ +enum ieee80211_phymode { + IEEE80211_MODE_AUTO = 0, /* autoselect */ + IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ + IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ + IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ + IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ + IEEE80211_MODE_TURBO = 5, /* 5GHz, OFDM, 2x clock */ +}; +#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1) + +enum ieee80211_opmode { + IEEE80211_M_STA = 1, /* infrastructure station */ + IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ + IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ + IEEE80211_M_HOSTAP = 6, /* Software Access Point */ + IEEE80211_M_MONITOR = 8 /* Monitor mode */ +}; + +/* + * 802.11g protection mode. + */ +enum ieee80211_protmode { + IEEE80211_PROT_NONE = 0, /* no protection */ + IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ + IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +/* + * Channels are specified by frequency and attributes. + */ +struct ieee80211_channel { + u_int16_t ic_freq; /* setting in Mhz */ + u_int16_t ic_flags; /* see below */ +}; + +/* bits 0-3 are for private use by drivers */ +/* channel attributes */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* + * Useful combinations of channel characteristics. + */ +#define IEEE80211_CHAN_FHSS \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) +#define IEEE80211_CHAN_A \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_B \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) +#define IEEE80211_CHAN_PUREG \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) +#define IEEE80211_CHAN_T \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) + +#define IEEE80211_IS_CHAN_FHSS(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) +#define IEEE80211_IS_CHAN_A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) +#define IEEE80211_IS_CHAN_B(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) +#define IEEE80211_IS_CHAN_PUREG(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) +#define IEEE80211_IS_CHAN_G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) +#define IEEE80211_IS_CHAN_T(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) + +#define IEEE80211_IS_CHAN_2GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) +#define IEEE80211_IS_CHAN_5GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) +#define IEEE80211_IS_CHAN_OFDM(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) +#define IEEE80211_IS_CHAN_CCK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) +#define IEEE80211_IS_CHAN_GFSK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) + +/* ni_chan encoding for FH phy */ +#define IEEE80211_FH_CHANMOD 80 +#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) +#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) +#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) + +#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ + +#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ + +struct ieee80211com { +#ifdef __NetBSD__ + struct ethercom ic_ec; +#else + struct arpcom ic_ac; +#endif + void (*ic_recv_mgmt)(struct ieee80211com *, + struct mbuf *, struct ieee80211_node *, + int, int, u_int32_t); + int (*ic_send_mgmt)(struct ieee80211com *, + struct ieee80211_node *, int, int); + int (*ic_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*ic_newassoc)(struct ieee80211com *, + struct ieee80211_node *, int); + int (*ic_set_tim)(struct ieee80211com *, int, int); + u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; + struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; + struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; + u_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)]; + u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)]; + u_char ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)]; + struct ifqueue ic_mgtq; + struct ifqueue ic_pwrsaveq; + u_int32_t ic_flags; /* state flags */ + u_int32_t ic_caps; /* capabilities */ + u_int16_t ic_modecaps; /* set of mode capabilities */ + u_int16_t ic_curmode; /* current mode */ + enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ + enum ieee80211_opmode ic_opmode; /* operation mode */ + enum ieee80211_state ic_state; /* 802.11 state */ + u_int32_t ic_aid_bitmap[IEEE80211_MAX_AID / 32 + 1]; + u_int16_t ic_max_aid; + enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ + struct ifmedia ic_media; /* interface media config */ +#ifdef __FreeBSD__ + struct bpf_if *ic_rawbpf; /* packet filter structure */ +#else + caddr_t ic_rawbpf; /* packet filter structure */ +#endif + struct ieee80211_node *ic_bss; /* information for this node */ + struct ieee80211_channel *ic_ibss_chan; + int ic_fixed_rate; /* index to ic_sup_rates[] */ + u_int16_t ic_rtsthreshold; + u_int16_t ic_fragthreshold; +#ifdef __FreeBSD__ + struct mtx ic_nodelock; /* on node table */ +#endif + u_int ic_scangen; /* gen# for timeout scan */ + struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *); + void (*ic_node_free)(struct ieee80211com *, + struct ieee80211_node *); + void (*ic_node_copy)(struct ieee80211com *, + struct ieee80211_node *, + const struct ieee80211_node *); + u_int8_t (*ic_node_getrssi)(struct ieee80211com *, + struct ieee80211_node *); + TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */ + LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE]; + u_int16_t ic_lintval; /* listen interval */ + u_int16_t ic_holdover; /* PM hold over duration */ + u_int16_t ic_txmin; /* min tx retry count */ + u_int16_t ic_txmax; /* max tx retry count */ + u_int16_t ic_txlifetime; /* tx lifetime */ + u_int16_t ic_txpower; /* tx power setting (dbM) */ + u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ + int ic_mgt_timer; /* mgmt timeout */ + int ic_inact_timer; /* inactivity timer wait */ + int ic_des_esslen; + u_int8_t ic_des_essid[IEEE80211_NWID_LEN]; + struct ieee80211_channel *ic_des_chan; /* desired channel */ + u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN]; + struct ieee80211_wepkey ic_nw_keys[IEEE80211_WEP_NKID]; + int ic_wep_txkey; /* default tx key index */ + void *ic_wep_ctx; /* wep crypt context */ + u_int32_t ic_iv; /* initial vector for wep */ + struct ieee80211_stats ic_stats; /* statistics */ +}; +#ifdef __NetBSD__ +#define ic_if ic_ec.ec_if +#else +#define ic_if ic_ac.ac_if +#endif +#define ic_softc ic_if.if_softc + +#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) +#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) + +/* ic_flags */ +#define IEEE80211_F_ASCAN 0x00000001 /* STATUS: active scan */ +#define IEEE80211_F_SIBSS 0x00000002 /* STATUS: start IBSS */ +#define IEEE80211_F_WEPON 0x00000100 /* CONF: WEP enabled */ +#define IEEE80211_F_IBSSON 0x00000200 /* CONF: IBSS creation enable */ +#define IEEE80211_F_PMGTON 0x00000400 /* CONF: Power mgmt enable */ +#define IEEE80211_F_DESBSSID 0x00000800 /* CONF: des_bssid is set */ +#define IEEE80211_F_SCANAP 0x00001000 /* CONF: Scanning AP */ +#define IEEE80211_F_ROAMING 0x00002000 /* CONF: roaming enabled */ +#define IEEE80211_F_SWRETRY 0x00004000 /* CONF: sw tx retry enabled */ +#define IEEE80211_F_TXPMGT 0x00018000 /* STATUS: tx power */ +#define IEEE80211_F_TXPOW_OFF 0x00000000 /* TX Power: radio disabled */ +#define IEEE80211_F_TXPOW_FIXED 0x00008000 /* TX Power: fixed rate */ +#define IEEE80211_F_TXPOW_AUTO 0x00010000 /* TX Power: undefined */ +#define IEEE80211_F_SHSLOT 0x00020000 /* CONF: short slot time */ +#define IEEE80211_F_SHPREAMBLE 0x00040000 /* CONF: short preamble */ +#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ +#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ + +/* ic_caps */ +#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ +#define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */ +#define IEEE80211_C_PMGT 0x00000004 /* CAPABILITY: Power mgmt */ +#define IEEE80211_C_HOSTAP 0x00000008 /* CAPABILITY: HOSTAP avail */ +#define IEEE80211_C_AHDEMO 0x00000010 /* CAPABILITY: Old Adhoc Demo */ +#define IEEE80211_C_SWRETRY 0x00000020 /* CAPABILITY: sw tx retry */ +#define IEEE80211_C_TXPMGT 0x00000040 /* CAPABILITY: tx power mgmt */ +#define IEEE80211_C_SHSLOT 0x00000080 /* CAPABILITY: short slottime */ +#define IEEE80211_C_SHPREAMBLE 0x00000100 /* CAPABILITY: short preamble */ +#define IEEE80211_C_MONITOR 0x00000200 /* CAPABILITY: monitor mode */ + +/* flags for ieee80211_fix_rate() */ +#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ +#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ +#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ +#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ + +void ieee80211_ifattach(struct ifnet *); +void ieee80211_ifdetach(struct ifnet *); +void ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t); +int ieee80211_media_change(struct ifnet *); +void ieee80211_media_status(struct ifnet *, struct ifmediareq *); +int ieee80211_ioctl(struct ifnet *, u_long, caddr_t); +int ieee80211_get_rate(struct ieee80211com *); +int ieee80211_cfgget(struct ifnet *, u_long, caddr_t); +int ieee80211_cfgset(struct ifnet *, u_long, caddr_t); +void ieee80211_watchdog(struct ifnet *); +int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int); +int ieee80211_rate2media(struct ieee80211com *, int, + enum ieee80211_phymode); +int ieee80211_media2rate(int); +u_int ieee80211_mhz2ieee(u_int, u_int); +u_int ieee80211_chan2ieee(struct ieee80211com *, struct ieee80211_channel *); +u_int ieee80211_ieee2mhz(u_int, u_int); +int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); +enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, + struct ieee80211_channel *); + +#ifdef IEEE80211_DEBUG +extern int ieee80211_debug; +#define IEEE80211_DPRINTF(X) if (ieee80211_debug) printf X +#define IEEE80211_DPRINTF2(X) if (ieee80211_debug>1) printf X +#else +#define IEEE80211_DPRINTF(X) +#define IEEE80211_DPRINTF2(X) +#endif + +extern int ieee80211_inact_max; + +#endif /* _NET80211_IEEE80211_VAR_H_ */ |