summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2003-01-15 20:01:50 +0000
committersam <sam@FreeBSD.org>2003-01-15 20:01:50 +0000
commit5e8fe5ba3ba7e2d747553ebaa9a0e402c8a3f8ed (patch)
treeaafc6152480bffea77d23cba373ce983b8a20a16 /sys/net
parent1f74260ee21c17a2a92b6e82137994d7e8403dbb (diff)
downloadFreeBSD-src-5e8fe5ba3ba7e2d747553ebaa9a0e402c8a3f8ed.zip
FreeBSD-src-5e8fe5ba3ba7e2d747553ebaa9a0e402c8a3f8ed.tar.gz
802.11 link layer support. This code implements the basic 802.11
state machine to provide station and host ap functionality for drivers. More work will follow to split out the state machine and protocol support from the ioctl interfaces to ease portability/sharing with NetBSD and forthcoming ports to other systems. Reviewed by: imp Obtained from: NetBSD (originally)
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if_ieee80211.h495
-rw-r--r--sys/net/if_ieee80211subr.c3412
2 files changed, 3830 insertions, 77 deletions
diff --git a/sys/net/if_ieee80211.h b/sys/net/if_ieee80211.h
index 0e67c58..7e5d04b 100644
--- a/sys/net/if_ieee80211.h
+++ b/sys/net/if_ieee80211.h
@@ -1,30 +1,82 @@
-/* $NetBSD: if_ieee80211.h,v 1.5 2000/07/21 04:47:40 onoe Exp $ */
-/* $FreeBSD$ */
+/* $NetBSD: if_ieee80211.h,v 1.23 2002/10/15 08:51:50 onoe Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by 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.
+ */
#ifndef _NET_IF_IEEE80211_H_
#define _NET_IF_IEEE80211_H_
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#define IEEE80211_ADDR_LEN ETHER_ADDR_LEN
+
/*
* 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[ETHER_ADDR_LEN];
- u_int8_t i_addr2[ETHER_ADDR_LEN];
- u_int8_t i_addr3[ETHER_ADDR_LEN];
+ 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[ETHER_ADDR_LEN]; */
-};
+ /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+ /* see below */
+} __attribute__((__packed__));
+
+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__));
#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
@@ -45,7 +97,7 @@ struct ieee80211_frame {
#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_DATA 0x00
#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10
#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20
#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30
@@ -67,46 +119,58 @@ struct ieee80211_frame {
#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
/*
* BEACON management packets
*
- * octect timestamp[8]
- * octect beacon interval[2]
- * octect capability information[2]
+ * octet timestamp[8]
+ * octet beacon interval[2]
+ * octet capability information[2]
* information element
- * octect elemid
- * octect length
- * octect information[length[
+ * octet elemid
+ * octet length
+ * octet information[length]
*/
-typedef u_int8_t * ieee80211_mgt_beacon_t;
-#define IEEE80211_BEACON_INTERVAL(beacon) \
- (beacon[8] + (beacon[9] << 8))
-#define IEEE80211_BEACON_CAPABILITY(beacon) \
- (beacon[10] + (beacon[11] << 8))
+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 0x01
-#define IEEE80211_CAPINFO_IBSS 0x02
-#define IEEE80211_CAPINFO_CF_POLLABLE 0x04
-#define IEEE80211_CAPINFO_CF_POLLREQ 0x08
-#define IEEE80211_CAPINFO_PRIVACY 0x10
-#define IEEE80211_CAPINFO_BITS "\20\01ESS\02IBSS\03POLLABLE\04POLLREQ\5PRIVACY"
+#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
+
+#define IEEE80211_RATE_BASIC 0x80
+#define IEEE80211_RATE_VAL 0x7f
/*
* Management information elements
*/
+
struct ieee80211_information {
char ssid[IEEE80211_NWID_LEN+1];
struct rates {
- u_int8_t *p;
+ u_int8_t *p;
} rates;
struct fh {
- u_int16_t dwell;
- u_int8_t set;
- u_int8_t pattern;
- u_int8_t index;
+ u_int16_t dwell;
+ u_int8_t set;
+ u_int8_t pattern;
+ u_int8_t index;
} fh;
struct ds {
u_int8_t channel;
@@ -118,16 +182,16 @@ struct ieee80211_information {
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 never needs to use this */
+ 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;
+ u_int16_t atim;
} ibss;
struct challenge {
- u_int8_t *p;
+ u_int8_t *p;
u_int8_t len;
} challenge;
};
@@ -144,38 +208,40 @@ struct ieee80211_information {
/*
* AUTH management packets
*
- * octect algo[2]
- * octect seq[2]
- * octect status[2]
- * octect chal.id
- * octect chal.length
- * octect chal.text[253]
+ * 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))
+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_OPEN_REQUEST 1
-#define IEEE80211_AUTH_OPEN_RESPONSE 2
+#define IEEE80211_AUTH_OPEN_REQUEST 1
+#define IEEE80211_AUTH_OPEN_RESPONSE 2
-#define IEEE80211_AUTH_SHARED_REQUEST 1
-#define IEEE80211_AUTH_SHARED_CHALLENGE 2
-#define IEEE80211_AUTH_SHARED_RESPONSE 3
-#define IEEE80211_AUTH_SHARED_PASS 4
+#define IEEE80211_AUTH_SHARED_REQUEST 1
+#define IEEE80211_AUTH_SHARED_CHALLENGE 2
+#define IEEE80211_AUTH_SHARED_RESPONSE 3
+#define IEEE80211_AUTH_SHARED_PASS 4
/*
* Reason codes
*
* Unlisted codes are reserved
*/
+
#define IEEE80211_REASON_UNSPECIFIED 1
#define IEEE80211_REASON_AUTH_EXPIRE 2
#define IEEE80211_REASON_AUTH_LEAVE 3
@@ -186,12 +252,7 @@ typedef u_int8_t * ieee80211_mgt_auth_t;
#define IEEE80211_REASON_ASSOC_LEAVE 8
#define IEEE80211_REASON_ASSOC_NOT_AUTHED 9
-/*
- * Status code
- *
- * Unlisted codes are reserved
- */
-#define IEEE80211_STATUS_SUCCESS 0x0000
+#define IEEE80211_STATUS_SUCCESS 0
#define IEEE80211_STATUS_UNSPECIFIED 1
#define IEEE80211_STATUS_CAPINFO 10
#define IEEE80211_STATUS_NOT_ASSOCED 11
@@ -200,8 +261,13 @@ typedef u_int8_t * ieee80211_mgt_auth_t;
#define IEEE80211_STATUS_SEQUENCE 14
#define IEEE80211_STATUS_CHALLENGE 15
#define IEEE80211_STATUS_TIMEOUT 16
-#define IEEE80211_STATUS_TOO_MANY_STATIONS 17
-#define IEEE80211_STATUS_RATES 18
+#define IEEE80211_STATUS_TOOMANY 17
+#define IEEE80211_STATUS_BASIC_RATE 18
+#define IEEE80211_STATUS_SP_REQUIRED 19
+#define IEEE80211_STATUS_PBCC_REQUIRED 20
+#define IEEE80211_STATUS_CA_REQUIRED 21
+#define IEEE80211_STATUS_TOO_MANY_STATIONS 22
+#define IEEE80211_STATUS_RATES 23
#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
#define IEEE80211_WEP_IVLEN 3 /* 24bit */
@@ -209,6 +275,16 @@ typedef u_int8_t * ieee80211_mgt_auth_t;
#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
#define IEEE80211_WEP_NKID 4 /* number of key ids */
+#define IEEE80211_CRC_LEN 4
+
+#define IEEE80211_MTU 1500
+#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \
+ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+
+/*
+ * ioctls
+ */
+
/* nwid is pointed at by ifr.ifr_data */
struct ieee80211_nwid {
u_int8_t i_len;
@@ -230,23 +306,59 @@ struct ieee80211_nwkey {
};
#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 */
+};
+#ifdef __NetBSD__
+#define SIOCS80211POWER _IOW('i', 234, struct ieee80211_power)
+#define SIOCG80211POWER _IOWR('i', 235, struct ieee80211_power)
+#else
+#define SIOCS80211POWER _IOW('i', 242, struct ieee80211_power)
+#define SIOCG80211POWER _IOWR('i', 243, struct ieee80211_power)
+#endif
+
+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 ieee80211_channel {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ u_int16_t i_channel;
+};
-#define IEEE80211_WEP_NOSUP -1
-#define IEEE80211_WEP_OFF 0
-#define IEEE80211_WEP_ON 1
-#define IEEE80211_WEP_MIXED 2
+#define IEEE80211_CHAN_ANY 0xffff
-#define IEEE80211_AUTH_NONE 0
-#define IEEE80211_AUTH_OPEN 1
-#define IEEE80211_AUTH_SHARED 2
+#define SIOCS80211CHANNEL _IOW('i', 238, struct ieee80211_channel)
+#define SIOCG80211CHANNEL _IOWR('i', 239, struct ieee80211_channel)
-#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
+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)
+/*
+ * FreeBSD-style ioctls.
+ */
/* the first member must be matched with struct ifreq */
struct ieee80211req {
char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
@@ -255,12 +367,21 @@ struct ieee80211req {
int16_t i_len; /* Index or simple value */
void *i_data; /* Extra data */
};
+#ifdef __FreeBSD__
#define SIOCS80211 _IOW('i', 234, struct ieee80211req)
#define SIOCG80211 _IOWR('i', 235, struct ieee80211req)
+#else
+#define SIOCS80211 _IOW('i', 242, struct ieee80211req)
+#define SIOCG80211 _IOWR('i', 243, struct ieee80211req)
+#endif
#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
@@ -268,6 +389,226 @@ struct ieee80211req {
#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
-#endif /* !_NET_IF_IEEE80211_H_ */
+#ifdef _KERNEL
+
+#define IEEE80211_ASCAN_WAIT 2 /* active scan wait */
+#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)
+
+/*
+ * Structure for IEEE 802.11 drivers.
+ */
+
+#define IEEE80211_CHAN_MAX 255
+#define IEEE80211_RATE_SIZE 12
+#define IEEE80211_KEYBUF_SIZE 16
+#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)
+
+enum ieee80211_phytype {
+ IEEE80211_T_DS, /* direct sequence spread spectrum */
+ IEEE80211_T_FH, /* frequency hopping */
+ IEEE80211_T_OFDM, /* frequency division multiplexing */
+ IEEE80211_T_HRDS /* high rate DS */
+};
+
+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 */
+};
+
+enum ieee80211_state {
+ IEEE80211_S_INIT, /* default state */
+ IEEE80211_S_SCAN, /* scanning */
+ IEEE80211_S_AUTH, /* try to authenticate */
+ IEEE80211_S_ASSOC, /* try to assoc */
+ IEEE80211_S_RUN /* associated */
+};
+
+/*
+ * Node specific information.
+ */
+struct ieee80211_node {
+ TAILQ_ENTRY(ieee80211_node) ni_list;
+ LIST_ENTRY(ieee80211_node) ni_hash;
+
+ /* hardware */
+ u_int8_t ni_rssi;
+ u_int32_t ni_rstamp;
+
+ /* 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];
+ u_int16_t ni_intval;
+ u_int16_t ni_capinfo;
+ u_int8_t ni_esslen;
+ u_int8_t ni_essid[IEEE80211_NWID_LEN];
+ int ni_nrate;
+ u_int8_t ni_rates[IEEE80211_RATE_SIZE];
+ u_int8_t ni_chan;
+ u_int16_t ni_fhdwell; /* FH only */
+ u_int8_t ni_fhindex; /* FH only */
+
+ /* DTIM and contention free period (CFP) */
+ u_int8_t ni_dtimperiod;
+ u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */
+ u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */
+ u_int16_t ni_cfpduremain; /* remaining duration */
+ u_int16_t ni_timoffset;
+
+ /* 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[] */
+ void *ni_private; /* driver private */
+};
+
+/* 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)
+
+struct ieee80211_wepkey {
+ int wk_len;
+ u_int8_t wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+struct ieee80211com {
+#ifdef __NetBSD__
+ struct ethercom ic_ec;
+#endif
+#ifdef __FreeBSD__
+ struct arpcom ic_ac;
+ struct mtx ic_mtx;
+#endif
+ void (*ic_recv_mgmt[16])(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+ int (*ic_send_mgmt[16])(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+ int (*ic_newstate)(void *, enum ieee80211_state);
+ int (*ic_chancheck)(void *, u_char *);
+ u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ u_int8_t ic_sup_rates[IEEE80211_RATE_SIZE];
+ u_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)];
+ u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)];
+ struct ifqueue ic_mgtq;
+ int ic_flags;
+ enum ieee80211_phytype ic_phytype;
+ enum ieee80211_opmode ic_opmode;
+ enum ieee80211_state ic_state;
+ struct bpf_if *ic_rawbpf; /* packet filter structure */
+ struct ieee80211_node ic_bss; /* information for this node */
+ int ic_node_privlen;/* size for ni_private */
+ void (*ic_node_free)(struct ieee80211com *,
+ struct ieee80211_node *); /* callback */
+ u_int8_t ic_ibss_chan;
+ int ic_fixed_rate; /* index to ic_sup_rates[] */
+ 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 */
+ int ic_mgt_timer; /* mgmt timeout */
+ int ic_scan_timer; /* scant wait */
+ int ic_inact_timer; /* inactivity timer wait */
+ int ic_des_esslen;
+ u_int8_t ic_des_essid[IEEE80211_NWID_LEN];
+ int 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 */
+};
+#ifdef __NetBSD__
+#define ic_if ic_ec.ec_if
+#define IEEE80211_LOCK(_ic) do { s = splnet(); } while (0)
+#define IEEE80211_UNLOCK(_ic) splx(s)
+#endif
+#ifdef __FreeBSD__
+#define ic_if ic_ac.ac_if
+#define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_mtx)
+#define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_mtx)
+#endif
+#define ic_softc ic_if.if_softc
+
+#define IEEE80211_SEND_MGMT(ic,ni,type,arg) do { \
+ if ((ic)->ic_send_mgmt[(type)>>IEEE80211_FC0_SUBTYPE_SHIFT] != NULL) \
+ (*(ic)->ic_send_mgmt[(type)>>IEEE80211_FC0_SUBTYPE_SHIFT]) \
+ (ic,ni,type,arg); \
+} while (0)
+
+#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)
+
+#define IEEE80211_IS_MULTICAST(a) ETHER_IS_MULTICAST(a)
+
+/* 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_HASWEP 0x00010000 /* CAPABILITY: WEP available */
+#define IEEE80211_F_HASIBSS 0x00020000 /* CAPABILITY: IBSS available */
+#define IEEE80211_F_HASPMGT 0x00040000 /* CAPABILITY: Power mgmt */
+#define IEEE80211_F_HASHOSTAP 0x00080000 /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_F_HASAHDEMO 0x00100000 /* CAPABILITY: Old Adhoc Demo */
+
+/* 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_input(struct ifnet *, struct mbuf *, int, u_int32_t);
+int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *,
+ struct mbuf *, int);
+struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *);
+struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);
+int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
+void ieee80211_print_essid(u_int8_t *, int);
+void ieee80211_dump_pkt(u_int8_t *, int, int, int);
+void ieee80211_watchdog(struct ifnet *);
+void ieee80211_next_scan(struct ifnet *);
+void ieee80211_end_scan(struct ifnet *);
+struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, u_int8_t *,
+ int);
+struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, u_int8_t *);
+void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_free_allnodes(struct ieee80211com *);
+int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int);
+int ieee80211_new_state(struct ifnet *, enum ieee80211_state, int);
+struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int);
+int ieee80211_rate2media(int, enum ieee80211_phytype);
+int ieee80211_media2rate(int, enum ieee80211_phytype);
+
+int ieee80211_cfgget(struct ifnet *, u_long, caddr_t);
+int ieee80211_cfgset(struct ifnet *, u_long, caddr_t);
+
+#endif /* _KERNEL */
+
+#endif /* _NET_IF_IEEE80211_H_ */
diff --git a/sys/net/if_ieee80211subr.c b/sys/net/if_ieee80211subr.c
new file mode 100644
index 0000000..cc87455
--- /dev/null
+++ b/sys/net/if_ieee80211subr.c
@@ -0,0 +1,3412 @@
+/* $NetBSD: if_ieee80211subr.c,v 1.22 2002/10/16 11:29:30 onoe Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Atsushi Onoe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * IEEE 802.11 generic handler
+ */
+
+#include <sys/cdefs.h>
+
+#include "opt_inet.h"
+#define NBPFILTER 1
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <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)
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+#include <net/if_ieee80211.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/wi/if_wavelan_ieee.h>
+
+#define IEEE80211_DEBUG
+#ifdef IEEE80211_DEBUG
+int ieee80211_debug = 0;
+#define DPRINTF(X) if (ieee80211_debug) printf X
+#define DPRINTF2(X) if (ieee80211_debug>1) printf X
+
+SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
+ 0, "IEEE 802.11 media debugging printfs");
+#else
+#define DPRINTF(X)
+#define DPRINTF2(X)
+#endif
+
+/* XXX belongs elsewhere */
+#ifndef ALIGNED_POINTER
+/*
+ * ALIGNED_POINTER is a boolean macro that checks whether an address
+ * is valid to fetch data elements of type t from on this architecture.
+ * This does not reflect the optimal alignment, just the possibility
+ * (within reasonable limits).
+ *
+ */
+#define ALIGNED_POINTER(p,t) 1
+#endif
+
+static int ieee80211_send_prreq(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+static int ieee80211_send_prresp(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+static int ieee80211_send_auth(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+static int ieee80211_send_deauth(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+static int ieee80211_send_asreq(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+static int ieee80211_send_asresp(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+static int ieee80211_send_disassoc(struct ieee80211com *,
+ struct ieee80211_node *, int, int);
+
+static void ieee80211_recv_beacon(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+static void ieee80211_recv_prreq(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+static void ieee80211_recv_auth(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+static void ieee80211_recv_asreq(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+static void ieee80211_recv_asresp(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+static void ieee80211_recv_disassoc(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+static void ieee80211_recv_deauth(struct ieee80211com *,
+ struct mbuf *, int, u_int32_t);
+
+static void ieee80211_crc_init(void);
+static u_int32_t ieee80211_crc_update(u_int32_t, u_int8_t *, int);
+
+static const char *ieee80211_mgt_subtype_name[] = {
+ "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
+ "probe_req", "probe_resp", "reserved#6", "reserved#7",
+ "beacon", "atim", "disassoc", "auth",
+ "deauth", "reserved#13", "reserved#14", "reserved#15"
+};
+
+void
+ieee80211_ifattach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ int i, rate;
+
+ /* XXX need unit */
+ mtx_init(&ic->ic_mtx, ifp->if_name, "802.11 link layer", MTX_DEF);
+
+ ether_ifattach(ifp, ic->ic_myaddr);
+#if NBPFILTER > 0
+ bpfattach2(ifp, DLT_IEEE802_11,
+ sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
+#endif
+ ieee80211_crc_init();
+ ic->ic_iv = arc4random();
+ memcpy(ic->ic_chan_active, ic->ic_chan_avail,
+ sizeof(ic->ic_chan_active));
+ if (isclr(ic->ic_chan_active, ic->ic_ibss_chan)) {
+ for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
+ if (isset(ic->ic_chan_active, i)) {
+ ic->ic_ibss_chan = i;
+ break;
+ }
+ }
+ }
+ ic->ic_des_chan = IEEE80211_CHAN_ANY;
+ ic->ic_fixed_rate = -1;
+ if (ic->ic_lintval == 0)
+ ic->ic_lintval = 100; /* default sleep */
+ TAILQ_INIT(&ic->ic_node);
+ mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_name, "mgmt send q", MTX_DEF);
+
+ rate = 0;
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
+ if (ic->ic_sup_rates[i] != 0)
+ rate = (ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) / 2;
+ }
+ if (rate)
+ ifp->if_baudrate = IF_Mbps(rate);
+ ifp->if_hdrlen = sizeof(struct ieee80211_frame);
+
+ /* initialize management frame handler */
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_BEACON
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_prreq;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_AUTH
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_auth;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asreq;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_asresp;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_deauth;
+ ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_disassoc;
+
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_REQ
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prreq;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_prresp;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_AUTH
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_auth;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_DEAUTH
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_deauth;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_REQ
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_REQ
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asreq;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_ASSOC_RESP
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_REASSOC_RESP
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_asresp;
+ ic->ic_send_mgmt[IEEE80211_FC0_SUBTYPE_DISASSOC
+ >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_send_disassoc;
+}
+
+void
+ieee80211_ifdetach(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ IEEE80211_LOCK(ic);
+ IF_DRAIN(&ic->ic_mgtq);
+ mtx_destroy(&ic->ic_mgtq.ifq_mtx);
+ if (ic->ic_wep_ctx != NULL) {
+ free(ic->ic_wep_ctx, M_DEVBUF);
+ ic->ic_wep_ctx = NULL;
+ }
+ ieee80211_free_allnodes(ic);
+#if NBPFILTER > 0
+ bpfdetach(ifp);
+#endif
+ ether_ifdetach(ifp);
+ IEEE80211_UNLOCK(ic);
+ mtx_destroy(&ic->ic_mtx);
+}
+
+void
+ieee80211_input(struct ifnet *ifp, struct mbuf *m, int rssi, u_int32_t rstamp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni = NULL;
+ struct ieee80211_frame *wh;
+ struct ether_header *eh;
+ void (*rh)(struct ieee80211com *, struct mbuf *, int, u_int);
+ struct mbuf *m1;
+ int len;
+ u_int8_t dir, subtype;
+ u_int8_t *bssid;
+ u_int16_t rxseq;
+
+ /* trim CRC here for WEP can find its own CRC at the end of packet. */
+ if (m->m_flags & M_HASFCS) {
+ m_adj(m, -IEEE80211_CRC_LEN);
+ m->m_flags &= ~M_HASFCS;
+ }
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "receive packet with wrong version: %x\n",
+ wh->i_fc[0]);
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+
+ if (ic->ic_state != IEEE80211_S_SCAN) {
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ ni = &ic->ic_bss;
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
+ DPRINTF2(("ieee80211_input: other bss %s\n",
+ ether_sprintf(wh->i_addr2)));
+ /* not interested in */
+ goto out;
+ }
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ if (dir == IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr3;
+ else
+ bssid = wh->i_addr1;
+ if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss.ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ DPRINTF2(("ieee80211_input: other bss %s\n",
+ ether_sprintf(wh->i_addr3)));
+ goto out;
+ }
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni == NULL) {
+ DPRINTF2(("ieee80211_input: unknown src %s\n",
+ ether_sprintf(wh->i_addr2)));
+ ni = &ic->ic_bss; /* XXX allocate? */
+ }
+ break;
+ }
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ rxseq = ni->ni_rxseq;
+ ni->ni_rxseq =
+ le16toh(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+ /* TODO: fragment */
+ if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ rxseq == ni->ni_rxseq) {
+ /* duplicate, silently discarded */
+ goto out;
+ }
+ ni->ni_inact = 0;
+ }
+
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_DATA:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ if (dir != IEEE80211_FC1_DIR_FROMDS)
+ goto out;
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
+ /*
+ * In IEEE802.11 network, multicast packet
+ * sent from me is broadcasted from AP.
+ * It should be silently discarded for
+ * SIMPLEX interface.
+ */
+ goto out;
+ }
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ goto out;
+ break;
+ case IEEE80211_M_HOSTAP:
+ if (dir != IEEE80211_FC1_DIR_TODS)
+ goto out;
+ /* check if source STA is associated */
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni == NULL) {
+ DPRINTF(("ieee80211_input: "
+ "data from unknown src %s\n",
+ ether_sprintf(wh->i_addr2)));
+ if ((ni = ieee80211_alloc_node(ic, wh->i_addr2,
+ 1)) != NULL) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_AUTHED);
+ ieee80211_free_node(ic, ni);
+ }
+ goto err;
+ }
+ if (ni->ni_associd == 0) {
+ DPRINTF(("ieee80211_input: "
+ "data from unassoc src %s\n",
+ ether_sprintf(wh->i_addr2)));
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_NOT_ASSOCED);
+ goto err;
+ }
+ break;
+ }
+ 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) {
+#ifdef ALTQ
+ if (ALTQ_IS_ENABLED(&ifp->if_snd))
+ altq_etherclassify(&ifp->if_snd, m1,
+ &pktattr);
+#endif
+ len = m1->m_pkthdr.len;
+ IF_ENQUEUE(&ifp->if_snd, m1);
+ if (m != NULL)
+ ifp->if_omcasts++;
+ ifp->if_obytes += len;
+ }
+ }
+ if (m != NULL)
+ (*ifp->if_input)(ifp, m);
+ return;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ goto err;
+ if (ic->ic_opmode == IEEE80211_M_AHDEMO)
+ goto out;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+ /* drop frames without interest */
+ if (ic->ic_state == IEEE80211_S_SCAN) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
+ subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ goto out;
+ } else {
+ if (ic->ic_opmode != IEEE80211_M_IBSS &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON)
+ goto out;
+ }
+
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* avoid to print too many frames */
+ int doprint = 0;
+
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ doprint = 1;
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ doprint = 1;
+ break;
+ default:
+ doprint = 1;
+ break;
+ }
+#ifdef IEEE80211_DEBUG
+ doprint += ieee80211_debug;
+#endif
+ if (doprint)
+ 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
+ rh = ic->ic_recv_mgmt[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT];
+ if (rh != NULL)
+ (*rh)(ic, m, rssi, rstamp);
+ m_freem(m);
+ return;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ default:
+ DPRINTF(("ieee80211_input: bad type %x\n", wh->i_fc[0]));
+ /* should not come here */
+ break;
+ }
+ err:
+ ifp->if_ierrors++;
+ out:
+ if (m != NULL) {
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf)
+ bpf_mtap(ic->ic_rawbpf, m);
+#endif
+ m_freem(m);
+ }
+}
+
+int
+ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
+ struct mbuf *m, int type)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_frame *wh;
+
+ if (ni == NULL)
+ ni = &ic->ic_bss;
+ ni->ni_inact = 0;
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL)
+ return ENOMEM;
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)wh->i_dur = 0;
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseq++;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* avoid to print too many frames */
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+#ifdef IEEE80211_DEBUG
+ ieee80211_debug > 1 ||
+#endif
+ (type & IEEE80211_FC0_SUBTYPE_MASK) !=
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ if_printf(ifp, "sending %s to %s\n",
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK)
+ >> IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(ni->ni_macaddr));
+ }
+ IF_ENQUEUE(&ic->ic_mgtq, m);
+ ifp->if_timer = 1;
+ (*ifp->if_start)(ifp);
+ return 0;
+}
+
+struct mbuf *
+ieee80211_encap(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ether_header eh;
+ struct ieee80211_frame *wh;
+ struct llc *llc;
+ struct ieee80211_node *ni;
+
+ if (m->m_len < sizeof(struct ether_header)) {
+ m = m_pullup(m, sizeof(struct ether_header));
+ if (m == NULL)
+ return NULL;
+ }
+ memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+
+ if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) &&
+ (ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_HOSTAP)) {
+ ni = ieee80211_find_node(ic, eh.ether_dhost);
+ if (ni == NULL)
+ ni = &ic->ic_bss; /*XXX*/
+ } else
+ ni = &ic->ic_bss;
+ ni->ni_inact = 0;
+
+ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
+ llc = mtod(m, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ llc->llc_snap.org_code[0] = 0;
+ llc->llc_snap.org_code[1] = 0;
+ llc->llc_snap.org_code[2] = 0;
+ llc->llc_snap.ether_type = eh.ether_type;
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ if (m == NULL)
+ return NULL;
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
+ *(u_int16_t *)wh->i_dur = 0;
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseq++;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+ break;
+ case IEEE80211_M_HOSTAP:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
+ break;
+ }
+ 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;
+ int error = 0;
+ u_int kid, len;
+ struct ieee80211req *ireq;
+ u_int8_t tmpkey[IEEE80211_WEP_KEYLEN];
+ char tmpssid[IEEE80211_NWID_LEN];
+
+ switch (cmd) {
+ 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_flags & IEEE80211_F_HASWEP) == 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_flags & IEEE80211_F_HASWEP) == 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)) {
+ 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_flags & IEEE80211_F_HASWEP) == 0)
+ error = EINVAL;
+ else
+ ireq->i_val = IEEE80211_WEP_NKID;
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ if ((ic->ic_flags & IEEE80211_F_HASWEP) == 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)
+ ireq->i_val = ic->ic_des_chan;
+ else
+ ireq->i_val = ic->ic_ibss_chan;
+ break;
+ default:
+ ireq->i_val = ic->ic_bss.ni_chan;
+ break;
+ }
+ 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;
+ default:
+ error = EINVAL;
+ }
+ break;
+ case SIOCS80211:
+ error = suser(curthread);
+ 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 what ever is
+ * passed in if it's 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_flags & IEEE80211_F_HASWEP) == 0) {
+ error = EINVAL;
+ break;
+ }
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID) {
+ error = EINVAL;
+ break;
+ }
+ if ((u_int) ireq->i_len > IEEE80211_WEP_KEYLEN) {
+ error = EINVAL;
+ break;
+ }
+ memset(tmpkey, 0, IEEE80211_WEP_KEYLEN);
+ error = copyin(ireq->i_data, tmpkey, ireq->i_len);
+ if (error)
+ break;
+ memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
+ IEEE80211_WEP_KEYLEN);
+ 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 == (int16_t) IEEE80211_CHAN_ANY)
+ ic->ic_des_chan = IEEE80211_CHAN_ANY;
+ 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 = 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_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 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_flags & IEEE80211_F_HASPMGT) == 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;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case SIOCGIFGENERIC:
+ error = ieee80211_cfgget(ifp, cmd, data);
+ break;
+ case SIOCSIFGENERIC:
+ error = suser(curthread);
+ if (error)
+ break;
+ error = ieee80211_cfgset(ifp, cmd, data);
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+ return error;
+}
+
+void
+ieee80211_print_essid(u_int8_t *essid, int len)
+{
+ int i;
+ u_int8_t *p;
+
+ if (len > IEEE80211_NWID_LEN)
+ len = IEEE80211_NWID_LEN;
+ /* determine printable or not */
+ for (i = 0, p = essid; i < len; i++, p++) {
+ if (*p < ' ' || *p > 0x7e)
+ break;
+ }
+ if (i == len) {
+ printf("\"");
+ for (i = 0, p = essid; i < len; i++, p++)
+ printf("%c", *p);
+ printf("\"");
+ } else {
+ printf("0x");
+ for (i = 0, p = essid; i < len; i++, p++)
+ printf("%02x", *p);
+ }
+}
+
+void
+ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
+{
+ struct ieee80211_frame *wh;
+ int i;
+
+ wh = (struct ieee80211_frame *)buf;
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ printf("NODS %s", ether_sprintf(wh->i_addr2));
+ printf("->%s", ether_sprintf(wh->i_addr1));
+ printf("(%s)", ether_sprintf(wh->i_addr3));
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ printf("TODS %s", ether_sprintf(wh->i_addr2));
+ printf("->%s", ether_sprintf(wh->i_addr3));
+ printf("(%s)", ether_sprintf(wh->i_addr1));
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ printf("FRDS %s", ether_sprintf(wh->i_addr3));
+ printf("->%s", ether_sprintf(wh->i_addr1));
+ printf("(%s)", ether_sprintf(wh->i_addr2));
+ break;
+ case IEEE80211_FC1_DIR_DSTODS:
+ printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
+ printf("->%s", ether_sprintf(wh->i_addr3));
+ printf("(%s", ether_sprintf(wh->i_addr2));
+ printf("->%s)", ether_sprintf(wh->i_addr1));
+ break;
+ }
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_DATA:
+ printf(" data");
+ break;
+ case IEEE80211_FC0_TYPE_MGT:
+ printf(" %s", ieee80211_mgt_subtype_name[
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
+ >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ break;
+ default:
+ printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
+ break;
+ }
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+ printf(" WEP");
+ if (rate >= 0)
+ printf(" %dM", rate / 2);
+ if (rssi >= 0)
+ printf(" +%d", rssi);
+ printf("\n");
+ if (len > 0) {
+ for (i = 0; i < len; i++) {
+ if ((i & 1) == 0)
+ printf(" ");
+ printf("%02x", buf[i]);
+ }
+ printf("\n");
+ }
+}
+
+void
+ieee80211_watchdog(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni, *nextbs;
+
+ if (ic->ic_scan_timer) {
+ if (--ic->ic_scan_timer == 0) {
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ ieee80211_end_scan(ifp);
+ }
+ }
+ if (ic->ic_mgt_timer) {
+ if (--ic->ic_mgt_timer == 0)
+ ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
+ }
+ if (ic->ic_inact_timer) {
+ if (--ic->ic_inact_timer == 0) {
+ for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL; ) {
+ if (++ni->ni_inact <= IEEE80211_INACT_MAX) {
+ ni = TAILQ_NEXT(ni, ni_list);
+ continue;
+ }
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s deauthenticate"
+ " (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr),
+ IEEE80211_REASON_AUTH_EXPIRE);
+ nextbs = TAILQ_NEXT(ni, ni_list);
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_EXPIRE);
+ ieee80211_free_node(ic, ni);
+ ni = nextbs;
+ }
+ if (!TAILQ_EMPTY(&ic->ic_node))
+ ic->ic_inact_timer = IEEE80211_INACT_WAIT;
+ }
+ }
+ if (ic->ic_scan_timer != 0 || ic->ic_mgt_timer != 0 ||
+ ic->ic_inact_timer != 0)
+ ifp->if_timer = 1;
+}
+
+void
+ieee80211_next_scan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ int chan;
+
+ chan = ic->ic_bss.ni_chan;
+ for (;;) {
+ chan = (chan + 1) % (IEEE80211_CHAN_MAX + 1);
+ if (isset(ic->ic_chan_active, chan))
+ break;
+ if (chan == ic->ic_bss.ni_chan) {
+ DPRINTF(("ieee80211_next_scan: no chan available\n"));
+ return;
+ }
+ }
+ DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
+ ic->ic_bss.ni_chan, chan));
+ ic->ic_bss.ni_chan = chan;
+ ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
+}
+
+void
+ieee80211_end_scan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni, *nextbs, *selbs;
+ void *p;
+ u_int8_t rate;
+ int i, fail;
+
+ ni = TAILQ_FIRST(&ic->ic_node);
+ if (ni == NULL) {
+ DPRINTF(("ieee80211_end_scan: no scan candidate\n"));
+ notfound:
+ if (ic->ic_opmode == IEEE80211_M_IBSS &&
+ (ic->ic_flags & IEEE80211_F_IBSSON) &&
+ ic->ic_des_esslen != 0) {
+ ni = &ic->ic_bss;
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "creating ibss\n");
+ ic->ic_flags |= IEEE80211_F_SIBSS;
+ ni->ni_nrate = 0;
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
+ if (ic->ic_sup_rates[i])
+ ni->ni_rates[ni->ni_nrate++] =
+ ic->ic_sup_rates[i];
+ }
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
+ ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */
+ ni->ni_esslen = ic->ic_des_esslen;
+ memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
+ ni->ni_rssi = 0;
+ ni->ni_rstamp = 0;
+ memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
+ ni->ni_intval = ic->ic_lintval;
+ ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ ni->ni_chan = ic->ic_ibss_chan;
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ ni->ni_fhdwell = 200; /* XXX */
+ ni->ni_fhindex = 1;
+ }
+ ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
+ return;
+ }
+ if (ic->ic_flags & IEEE80211_F_ASCAN) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "entering passive scan mode\n");
+ ic->ic_flags &= ~IEEE80211_F_ASCAN;
+ }
+ 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) {
+ nextbs = TAILQ_NEXT(ni, ni_list);
+ if (ni->ni_fails) {
+ /*
+ * The configuration of the access points may change
+ * during my scan. So delete the entry for the AP
+ * and retry to associate if there is another beacon.
+ */
+ if (ni->ni_fails++ > 2)
+ ieee80211_free_node(ic, ni);
+ continue;
+ }
+ fail = 0;
+ if (isclr(ic->ic_chan_active, ni->ni_chan))
+ fail |= 0x01;
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANY &&
+ ni->ni_chan != ic->ic_des_chan)
+ fail |= 0x01;
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ fail |= 0x02;
+ } else {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
+ fail |= 0x02;
+ }
+ if (ic->ic_flags & IEEE80211_F_WEPON) {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+ fail |= 0x04;
+ } else {
+ if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
+ fail |= 0x04;
+ }
+ rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
+ if (rate & IEEE80211_RATE_BASIC)
+ fail |= 0x08;
+ if (ic->ic_des_esslen != 0 &&
+ (ni->ni_esslen != ic->ic_des_esslen ||
+ memcmp(ni->ni_essid, ic->ic_des_essid,
+ ic->ic_des_esslen != 0)))
+ fail |= 0x10;
+ if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ fail |= 0x20;
+ if (ifp->if_flags & IFF_DEBUG) {
+ printf(" %c %s", fail ? '-' : '+',
+ ether_sprintf(ni->ni_macaddr));
+ printf(" %s%c", ether_sprintf(ni->ni_bssid),
+ fail & 0x20 ? '!' : ' ');
+ printf(" %3d%c", ni->ni_chan, fail & 0x01 ? '!' : ' ');
+ printf(" %+4d", ni->ni_rssi);
+ printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
+ fail & 0x08 ? '!' : ' ');
+ printf(" %4s%c",
+ (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
+ (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
+ "????",
+ fail & 0x02 ? '!' : ' ');
+ printf(" %3s%c ",
+ (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
+ "wep" : "no",
+ fail & 0x04 ? '!' : ' ');
+ ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
+ printf("%s\n", fail & 0x10 ? "!" : "");
+ }
+ if (!fail) {
+ if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi)
+ selbs = ni;
+ }
+ }
+ if (selbs == NULL)
+ goto notfound;
+ p = ic->ic_bss.ni_private;
+ ic->ic_bss = *selbs;
+ ic->ic_bss.ni_private = p;
+ if (p != NULL && ic->ic_node_privlen)
+ memcpy(p, selbs->ni_private, ic->ic_node_privlen);
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ ieee80211_fix_rate(ic, &ic->ic_bss, IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ic->ic_bss.ni_nrate == 0) {
+ selbs->ni_fails++;
+ goto notfound;
+ }
+ ieee80211_new_state(ifp, IEEE80211_S_RUN, -1);
+ } else
+ ieee80211_new_state(ifp, IEEE80211_S_AUTH, -1);
+}
+
+struct ieee80211_node *
+ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr, int copy)
+{
+ struct ieee80211_node *ni;
+ int hash;
+
+ 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);
+ IEEE80211_LOCK(ic);
+ TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
+ LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
+ IEEE80211_UNLOCK(ic);
+ 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;
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ IEEE80211_LOCK(ic);
+ LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr))
+ break;
+ }
+ IEEE80211_UNLOCK(ic);
+ return ni;
+}
+
+void
+ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ IEEE80211_LOCK(ic);
+ if (ic->ic_node_free != NULL)
+ (*ic->ic_node_free)(ic, ni);
+ TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
+ LIST_REMOVE(ni, ni_hash);
+ IEEE80211_UNLOCK(ic);
+ free(ni, M_DEVBUF);
+ if (TAILQ_EMPTY(&ic->ic_node))
+ ic->ic_inact_timer = 0;
+}
+
+void
+ieee80211_free_allnodes(struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni;
+
+ while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
+ ieee80211_free_node(ic, ni);
+}
+
+int
+ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
+{
+ int i, j, ignore, error;
+ int okrate, badrate;
+ u_int8_t r;
+
+ error = 0;
+ okrate = badrate = 0;
+ for (i = 0; i < ni->ni_nrate; ) {
+ ignore = 0;
+ if (flags & IEEE80211_F_DOSORT) {
+ for (j = i + 1; j < ni->ni_nrate; j++) {
+ if ((ni->ni_rates[i] & IEEE80211_RATE_VAL) >
+ (ni->ni_rates[j] & IEEE80211_RATE_VAL)) {
+ r = ni->ni_rates[i];
+ ni->ni_rates[i] = ni->ni_rates[j];
+ ni->ni_rates[j] = r;
+ }
+ }
+ }
+ r = ni->ni_rates[i] & IEEE80211_RATE_VAL;
+ badrate = r;
+ if (flags & IEEE80211_F_DOFRATE) {
+ if (ic->ic_fixed_rate >= 0 &&
+ r != (ic->ic_sup_rates[ic->ic_fixed_rate] &
+ IEEE80211_RATE_VAL))
+ ignore++;
+ }
+ if (flags & IEEE80211_F_DONEGO) {
+ for (j = 0; j < IEEE80211_RATE_SIZE; j++) {
+ if (r ==
+ (ic->ic_sup_rates[j] & IEEE80211_RATE_VAL))
+ break;
+ }
+ if (j == IEEE80211_RATE_SIZE) {
+ if (ni->ni_rates[i] & IEEE80211_RATE_BASIC)
+ error++;
+ ignore++;
+ }
+ }
+ if (flags & IEEE80211_F_DODEL) {
+ if (ignore) {
+ ni->ni_nrate--;
+ for (j = i; j < ni->ni_nrate; j++)
+ ni->ni_rates[j] = ni->ni_rates[j + 1];
+ ni->ni_rates[j] = 0;
+ continue;
+ }
+ }
+ if (!ignore)
+ okrate = ni->ni_rates[i];
+ i++;
+ }
+ if (okrate == 0 || error != 0)
+ return badrate | IEEE80211_RATE_BASIC;
+ return okrate & IEEE80211_RATE_VAL;
+}
+
+static int
+ieee80211_send_prreq(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int dummy)
+{
+ int i, ret;
+ struct mbuf *m;
+ u_int8_t *frm;
+
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ *frm++ = IEEE80211_ELEMID_SSID;
+ *frm++ = ic->ic_des_esslen;
+ memcpy(frm, ic->ic_des_essid, ic->ic_des_esslen);
+ frm += ic->ic_des_esslen;
+
+ *frm++ = IEEE80211_ELEMID_RATES;
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
+ if (ic->ic_sup_rates[i] != 0)
+ frm[i + 1] = ic->ic_sup_rates[i];
+ }
+ *frm++ = i;
+ frm += i;
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
+ ic->ic_mgt_timer = IEEE80211_TRANS_WAIT;
+ return ret;
+}
+
+static int
+ieee80211_send_prresp(struct ieee80211com *ic, struct ieee80211_node *bs0,
+ int type, int dummy)
+{
+ struct mbuf *m;
+ u_int8_t *frm;
+ struct ieee80211_node *ni = &ic->ic_bss;
+ u_int16_t capinfo;
+
+ /*
+ * probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (IBSS)
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ memset(frm, 0, 8); /* timestamp should be filled later */
+ frm += 8;
+ *(u_int16_t *)frm = htole16(ni->ni_intval);
+ frm += 2;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo = IEEE80211_CAPINFO_IBSS;
+ else
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+ *frm++ = IEEE80211_ELEMID_SSID;
+ *frm++ = ni->ni_esslen;
+ memcpy(frm, ni->ni_essid, ni->ni_esslen);
+ frm += ni->ni_esslen;
+ *frm++ = IEEE80211_ELEMID_RATES;
+ *frm++ = ni->ni_nrate;
+ memcpy(frm, ni->ni_rates, ni->ni_nrate);
+ frm += ni->ni_nrate;
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ } else { /* IEEE80211_M_HOSTAP */
+ /* TODO: TIM */
+ *frm++ = IEEE80211_ELEMID_TIM;
+ *frm++ = 4; /* length */
+ *frm++ = 0; /* DTIM count */
+ *frm++ = 1; /* DTIM period */
+ *frm++ = 0; /* bitmap control */
+ *frm++ = 0; /* Partial Virtual Bitmap (variable length) */
+ }
+ /* TODO: check MHLEN limit */
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ return ieee80211_mgmt_output(&ic->ic_if, bs0, m, type);
+}
+
+static int
+ieee80211_send_auth(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int seq)
+{
+ struct mbuf *m;
+ u_int16_t *frm;
+ int ret;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ MH_ALIGN(m, 2 * 3);
+ m->m_pkthdr.len = m->m_len = 6;
+ frm = mtod(m, u_int16_t *);
+ /* TODO: shared key auth */
+ frm[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
+ frm[1] = htole16(seq);
+ frm[2] = 0; /* status */
+ ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ ic->ic_mgt_timer = IEEE80211_TRANS_WAIT;
+ return ret;
+}
+
+static int
+ieee80211_send_deauth(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int reason)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct mbuf *m;
+
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s deauthenticate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ MH_ALIGN(m, 2);
+ m->m_pkthdr.len = m->m_len = 2;
+ *mtod(m, u_int16_t *) = htole16(reason);
+ return ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
+}
+
+static int
+ieee80211_send_asreq(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int dummy)
+{
+ struct mbuf *m;
+ u_int8_t *frm, *rates;
+ u_int16_t capinfo;
+ int i, ret;
+
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ capinfo = 0;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo |= IEEE80211_CAPINFO_IBSS;
+ else /* IEEE80211_M_STA */
+ capinfo |= IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ *(u_int16_t *)frm = htole16(ic->ic_lintval);
+ frm += 2;
+
+ if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ IEEE80211_ADDR_COPY(frm, ic->ic_bss.ni_bssid);
+ frm += IEEE80211_ADDR_LEN;
+ }
+
+ *frm++ = IEEE80211_ELEMID_SSID;
+ *frm++ = ni->ni_esslen;
+ memcpy(frm, ni->ni_essid, ni->ni_esslen);
+ frm += ni->ni_esslen;
+
+ *frm++ = IEEE80211_ELEMID_RATES;
+ rates = frm++; /* update later */
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
+ if (ni->ni_rates[i] != 0)
+ *frm++ = ni->ni_rates[i];
+ }
+ *rates = frm - (rates + 1);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ ret = ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
+ ic->ic_mgt_timer = IEEE80211_TRANS_WAIT;
+ return ret;
+}
+
+static int
+ieee80211_send_asresp(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int status)
+{
+ struct mbuf *m;
+ u_int8_t *frm, *rates, *r;
+ u_int16_t capinfo;
+ int i;
+
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ m->m_data += sizeof(struct ieee80211_frame);
+ frm = mtod(m, u_int8_t *);
+
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_WEPON)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ *(u_int16_t *)frm = htole16(status);
+ frm += 2;
+
+ if (status == IEEE80211_STATUS_SUCCESS && ni != NULL)
+ *(u_int16_t *)frm = htole16(ni->ni_associd);
+ else
+ *(u_int16_t *)frm = htole16(0);
+ frm += 2;
+
+ *frm++ = IEEE80211_ELEMID_RATES;
+ rates = frm++; /* update later */
+ if (ni != NULL)
+ r = ni->ni_rates;
+ else
+ r = ic->ic_bss.ni_rates;
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++, r++) {
+ if (*r != 0)
+ *frm++ = *r;
+ }
+ *rates = frm - (rates + 1);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+ return ieee80211_mgmt_output(&ic->ic_if, ni, m, type);
+}
+
+static int
+ieee80211_send_disassoc(struct ieee80211com *ic, struct ieee80211_node *ni,
+ int type, int reason)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct mbuf *m;
+
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s disassociate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOMEM;
+ MH_ALIGN(m, 2);
+ m->m_pkthdr.len = m->m_len = 2;
+ *mtod(m, u_int16_t *) = htole16(reason);
+ return ieee80211_mgmt_output(&ic->ic_if, ni, m,
+ IEEE80211_FC0_SUBTYPE_DISASSOC);
+}
+
+static void
+ieee80211_recv_beacon(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ u_int8_t *frm, *efrm, *tstamp, *bintval, *capinfo, *ssid, *rates;
+ u_int8_t chan, fhindex;
+ u_int16_t fhdwell;
+
+ if (ic->ic_opmode != IEEE80211_M_IBSS &&
+ ic->ic_state != IEEE80211_S_SCAN) {
+ /* XXX: may be useful for background scan */
+ return;
+ }
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ /*
+ * beacon frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (FH/DS)
+ */
+ tstamp = frm; frm += 8;
+ bintval = frm; frm += 2;
+ capinfo = frm; frm += 2;
+ ssid = rates = NULL;
+ chan = ic->ic_bss.ni_chan;
+ fhdwell = 0;
+ fhindex = 0;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ fhdwell = (frm[3] << 8) | frm[2];
+ chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+ fhindex = frm[6];
+ }
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
+ if (ic->ic_phytype == IEEE80211_T_DS)
+ chan = frm[2];
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ if (ssid == NULL || rates == NULL) {
+ DPRINTF(("ieee80211_recv_beacon: ssid=%p, rates=%p, chan=%d\n",
+ ssid, rates, chan));
+ return;
+ }
+ if (ssid[1] > IEEE80211_NWID_LEN) {
+ DPRINTF(("ieee80211_recv_beacon: bad ssid len %d from %s\n",
+ ssid[1], ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug &&
+ (ieee80211_debug > 1 || ni == NULL ||
+ ic->ic_state == IEEE80211_S_SCAN)) {
+ printf("ieee80211_recv_prreq: %sbeacon on chan %u (bss chan %u) ",
+ (ni == NULL ? "new " : ""), 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) {
+ /*
+ * Update ESSID at probe response to adopt hidden AP by
+ * Lucent/Cisco, which announces null ESSID in beacon.
+ */
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ ni->ni_esslen = ssid[1];
+ memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
+ memcpy(ni->ni_essid, ssid + 2, ssid[1]);
+ }
+ }
+ }
+ IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+ memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
+ ni->ni_nrate = rates[1];
+ memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
+ ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT);
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
+ ni->ni_intval = le16toh(*(u_int16_t *)bintval);
+ ni->ni_capinfo = le16toh(*(u_int16_t *)capinfo);
+ ni->ni_chan = chan;
+ ni->ni_fhdwell = fhdwell;
+ ni->ni_fhindex = fhindex;
+ if (ic->ic_state == IEEE80211_S_SCAN && ic->ic_scan_timer == 0)
+ ieee80211_end_scan(&ic->ic_if);
+}
+
+static void
+ieee80211_recv_prreq(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ u_int8_t *frm, *efrm, *ssid, *rates;
+ u_int8_t rate;
+ int allocbs;
+
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ return;
+ if (ic->ic_state != IEEE80211_S_RUN)
+ return;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ */
+ ssid = rates = NULL;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ if (ssid == NULL || rates == NULL) {
+ DPRINTF(("ieee80211_recv_prreq: ssid=%p, rates=%p\n",
+ ssid, rates));
+ return;
+ }
+ if (ssid[1] != 0 &&
+ (ssid[1] != ic->ic_bss.ni_esslen ||
+ memcmp(ssid + 2, ic->ic_bss.ni_essid, ic->ic_bss.ni_esslen) != 0)) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ printf("ieee80211_recv_prreq: ssid unmatch ");
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf(" from %s\n", ether_sprintf(wh->i_addr2));
+ }
+#endif
+ return;
+ }
+
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni == NULL) {
+ if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 1)) == NULL)
+ return;
+ DPRINTF(("ieee80211_recv_prreq: new req from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ allocbs = 1;
+ } else
+ allocbs = 0;
+ memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
+ ni->ni_nrate = rates[1];
+ memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT |
+ IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ DPRINTF(("ieee80211_recv_prreq: rate negotiation failed: %s\n",
+ ether_sprintf(wh->i_addr2)));
+ } else {
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP,
+ 0);
+ }
+ if (allocbs && (ic->ic_opmode == IEEE80211_M_HOSTAP))
+ ieee80211_free_node(ic, ni);
+}
+
+static void
+ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ u_int8_t *frm, *efrm;
+ u_int16_t algo, seq, status;
+ int allocbs;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ if (frm + 6 > efrm) {
+ DPRINTF(("ieee80211_recv_auth: too short from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ algo = le16toh(*(u_int16_t *)frm);
+ seq = le16toh(*(u_int16_t *)(frm + 2));
+ status = le16toh(*(u_int16_t *)(frm + 4));
+ if (algo != IEEE80211_AUTH_ALG_OPEN) {
+ /* TODO: shared key auth */
+ DPRINTF(("ieee80211_recv_auth: unsupported auth %d from %s\n",
+ algo, ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_IBSS:
+ if (ic->ic_state != IEEE80211_S_RUN || seq != 1)
+ return;
+ ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+
+ case IEEE80211_M_AHDEMO:
+ /* 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)
+ if_printf(ifp, "station %s %s authenticated\n",
+ (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) {
+ if_printf(&ic->ic_if,
+ "authentication failed (reason %d) for %s\n",
+ status,
+ ether_sprintf(wh->i_addr3));
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni != NULL)
+ ni->ni_fails++;
+ return;
+ }
+ ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ }
+}
+
+static void
+ieee80211_recv_asreq(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni = &ic->ic_bss;
+ u_int8_t *frm, *efrm, *ssid, *rates;
+ u_int16_t capinfo, bintval;
+ int reassoc, resp, newassoc;
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
+ (ic->ic_state != IEEE80211_S_RUN))
+ return;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ reassoc = 1;
+ resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+ } else {
+ reassoc = 0;
+ resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
+ }
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ */
+ if (frm + (reassoc ? 10 : 4) > efrm) {
+ DPRINTF(("ieee80211_recv_asreq: too short from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ return;
+ }
+
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss.ni_bssid)) {
+ DPRINTF(("ieee80211_recv_asreq: ignore other bss from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ capinfo = le16toh(*(u_int16_t *)frm); frm += 2;
+ bintval = le16toh(*(u_int16_t *)frm); frm += 2;
+ if (reassoc)
+ frm += 6; /* ignore current AP info */
+ ssid = rates = NULL;
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ if (ssid == NULL || rates == NULL) {
+ DPRINTF(("ieee80211_recv_asreq: ssid=%p, rates=%p\n",
+ ssid, rates));
+ return;
+ }
+ if (ssid[1] > IEEE80211_NWID_LEN) {
+ DPRINTF(("ieee80211_recv_asreq: bad ssid len %d from %s\n",
+ ssid[1], ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ if (ssid[1] != ic->ic_bss.ni_esslen ||
+ memcmp(ssid + 2, ic->ic_bss.ni_essid, ssid[1]) != 0) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ printf("ieee80211_recv_asreq: ssid unmatch ");
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf(" from %s\n", ether_sprintf(wh->i_addr2));
+ }
+#endif
+ return;
+ }
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni == NULL) {
+ DPRINTF(("ieee80211_recv_asreq: not authenticated for %s\n",
+ ether_sprintf(wh->i_addr2)));
+ if ((ni = ieee80211_alloc_node(ic, wh->i_addr2, 1)) == NULL)
+ return;
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED);
+ ieee80211_free_node(ic, ni);
+ return;
+ }
+ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
+ (capinfo & IEEE80211_CAPINFO_PRIVACY) !=
+ ((ic->ic_flags & IEEE80211_F_WEPON) ?
+ IEEE80211_CAPINFO_PRIVACY : 0)) {
+ DPRINTF(("ieee80211_recv_asreq: capability unmatch %x for %s\n",
+ capinfo, ether_sprintf(wh->i_addr2)));
+ ni->ni_associd = 0;
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
+ return;
+ }
+ memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
+ ni->ni_nrate = rates[1];
+ memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
+ ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ni->ni_nrate == 0) {
+ DPRINTF(("ieee80211_recv_asreq: rate unmatch for %s\n",
+ ether_sprintf(wh->i_addr2)));
+ ni->ni_associd = 0;
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
+ return;
+ }
+ ni->ni_rssi = rssi;
+ ni->ni_rstamp = rstamp;
+ ni->ni_intval = bintval;
+ ni->ni_capinfo = capinfo;
+ ni->ni_chan = ic->ic_bss.ni_chan;
+ ni->ni_fhdwell = ic->ic_bss.ni_fhdwell;
+ ni->ni_fhindex = ic->ic_bss.ni_fhindex;
+ if (ni->ni_associd == 0) {
+ ni->ni_associd = 0xc000 | ic->ic_bss.ni_associd++;
+ newassoc = 1;
+ } else
+ newassoc = 0;
+ IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s %s associated\n",
+ (newassoc ? "newly" : "already"),
+ ether_sprintf(ni->ni_macaddr));
+}
+
+static void
+ieee80211_recv_asresp(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni = &ic->ic_bss;
+ u_int8_t *frm, *efrm, *rates;
+ int status;
+
+ if (ic->ic_opmode != IEEE80211_M_STA ||
+ ic->ic_state != IEEE80211_S_ASSOC)
+ return;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ /*
+ * asresp frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ */
+ if (frm + 6 > efrm) {
+ DPRINTF(("ieee80211_recv_asresp: too short from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ return;
+ }
+
+ ni->ni_capinfo = le16toh(*(u_int16_t *)frm);
+ frm += 2;
+
+ status = le16toh(*(u_int16_t *)frm);
+ frm += 2;
+ if (status != 0) {
+ if_printf(ifp, "association failed (reason %d) for %s\n",
+ status, ether_sprintf(wh->i_addr3));
+ ni = ieee80211_find_node(ic, wh->i_addr2);
+ if (ni != NULL)
+ ni->ni_fails++;
+ return;
+ }
+ ni->ni_associd = le16toh(*(u_int16_t *)frm);
+ frm += 2;
+ rates = frm;
+
+ memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
+ ni->ni_nrate = rates[1];
+ memcpy(ni->ni_rates, rates + 2, ni->ni_nrate);
+ ieee80211_fix_rate(ic, ni, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ni->ni_nrate == 0)
+ return;
+ ieee80211_new_state(ifp, IEEE80211_S_RUN,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+}
+
+static void
+ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ u_int8_t *frm, *efrm;
+ u_int16_t reason;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ /*
+ * disassoc frame format
+ * [2] reason
+ */
+ if (frm + 2 > efrm) {
+ DPRINTF(("ieee80211_recv_disassoc: too short from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ reason = le16toh(*(u_int16_t *)frm);
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ ieee80211_new_state(&ic->ic_if, IEEE80211_S_ASSOC,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_M_HOSTAP:
+ if ((ni = ieee80211_find_node(ic, wh->i_addr2)) != NULL) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s disassociated"
+ " by peer (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ ni->ni_associd = 0;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m0, int rssi,
+ u_int32_t rstamp)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ u_int8_t *frm, *efrm;
+ u_int16_t reason;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ /*
+ * dauth frame format
+ * [2] reason
+ */
+ if (frm + 2 > efrm) {
+ DPRINTF(("ieee80211_recv_deauth: too short from %s\n",
+ ether_sprintf(wh->i_addr2)));
+ return;
+ }
+ reason = le16toh(*(u_int16_t *)frm);
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ ieee80211_new_state(&ic->ic_if, IEEE80211_S_AUTH,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_M_HOSTAP:
+ if ((ni = ieee80211_find_node(ic, wh->i_addr2)) != NULL) {
+ if (ifp->if_flags & IFF_DEBUG)
+ if_printf(ifp, "station %s deauthenticated"
+ " by peer (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ ieee80211_free_node(ic, ni);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+int
+ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_node *ni = &ic->ic_bss;
+ int i, error, ostate;
+#ifdef IEEE80211_DEBUG
+ static const char *stname[] =
+ { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" };
+#endif
+
+ ostate = ic->ic_state;
+ DPRINTF(("ieee80211_new_state: %s -> %s\n",
+ stname[ostate], stname[nstate]));
+ if (ic->ic_newstate) {
+ error = (*ic->ic_newstate)(ic->ic_softc, nstate);
+ if (error == EINPROGRESS)
+ return 0;
+ if (error != 0)
+ return error;
+ }
+
+ /* state transition */
+ ic->ic_state = nstate;
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ break;
+ case IEEE80211_S_RUN:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ break;
+ case IEEE80211_M_HOSTAP:
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ if (ni->ni_associd == 0)
+ continue;
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ }
+ break;
+ default:
+ break;
+ }
+ /* FALLTHRU */
+ case IEEE80211_S_ASSOC:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ break;
+ case IEEE80211_M_HOSTAP:
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ }
+ break;
+ default:
+ break;
+ }
+ /* FALLTHRU */
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_SCAN:
+ ic->ic_scan_timer = 0;
+ ic->ic_mgt_timer = 0;
+ IF_DRAIN(&ic->ic_mgtq);
+ if (ic->ic_wep_ctx != NULL) {
+ free(ic->ic_wep_ctx, M_DEVBUF);
+ ic->ic_wep_ctx = NULL;
+ }
+ ieee80211_free_allnodes(ic);
+ break;
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ ic->ic_flags &= ~IEEE80211_F_SIBSS;
+ ni = &ic->ic_bss;
+ /* initialize bss for probe request */
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
+ ni->ni_nrate = 0;
+ memset(ni->ni_rates, 0, IEEE80211_RATE_SIZE);
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
+ if (ic->ic_sup_rates[i] != 0)
+ ni->ni_rates[ni->ni_nrate++] =
+ ic->ic_sup_rates[i];
+ }
+ ni->ni_associd = 0;
+ ni->ni_rstamp = 0;
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ic->ic_flags |= IEEE80211_F_ASCAN;
+ ic->ic_scan_timer = IEEE80211_ASCAN_WAIT;
+ /* use lowest rate */
+ ni->ni_txrate = 0;
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+ break;
+ case IEEE80211_S_SCAN:
+ /* scan next */
+ if (ic->ic_flags & IEEE80211_F_ASCAN) {
+ if (ic->ic_scan_timer == 0)
+ ic->ic_scan_timer =
+ IEEE80211_ASCAN_WAIT;
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+ } else {
+ if (ic->ic_scan_timer == 0)
+ ic->ic_scan_timer =
+ IEEE80211_PSCAN_WAIT;
+ ifp->if_timer = 1;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ /* beacon miss */
+ if (ifp->if_flags & IFF_DEBUG) {
+ /* 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++;
+ ic->ic_flags |= IEEE80211_F_ASCAN;
+ ic->ic_scan_timer = IEEE80211_ASCAN_WAIT;
+ IEEE80211_SEND_MGMT(ic, &ic->ic_bss,
+ IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+ break;
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ DPRINTF(("ieee80211_new_state: invalid transition\n"));
+ break;
+ case IEEE80211_S_SCAN:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (mgt) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ /* ??? */
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ /* ignore and retry scan on timeout */
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (mgt) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ ic->ic_state = ostate; /* stay RUN */
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ /* try to reauth */
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ }
+ break;
+ }
+ break;
+ case IEEE80211_S_ASSOC:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_ASSOC:
+ DPRINTF(("ieee80211_new_state: invalid transition\n"));
+ break;
+ case IEEE80211_S_AUTH:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_RUN:
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_RUN:
+ DPRINTF(("ieee80211_new_state: invalid transition\n"));
+ break;
+ case IEEE80211_S_SCAN: /* adhoc mode */
+ case IEEE80211_S_ASSOC: /* infra mode */
+ if (ifp->if_flags & IFF_DEBUG) {
+ if_printf(ifp, " ");
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ printf("associated ");
+ else
+ printf("synchronized ");
+ printf("with %s ssid ",
+ ether_sprintf(ic->ic_bss.ni_bssid));
+ ieee80211_print_essid(ic->ic_bss.ni_essid,
+ ic->ic_bss.ni_esslen);
+ printf(" channel %d\n", ic->ic_bss.ni_chan);
+ }
+ /* start with highest negotiated rate */
+ ic->ic_bss.ni_txrate = ic->ic_bss.ni_nrate - 1;
+ ic->ic_mgt_timer = 0;
+ (*ifp->if_start)(ifp);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+struct mbuf *
+ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ struct mbuf *m, *n, *n0;
+ struct ieee80211_frame *wh;
+ int i, left, len, moff, noff, kid;
+ u_int32_t iv, crc;
+ u_int8_t *ivp;
+ void *ctx;
+ u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+ u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
+
+ n0 = NULL;
+ if ((ctx = ic->ic_wep_ctx) == NULL) {
+ ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT);
+ if (ctx == NULL)
+ goto fail;
+ ic->ic_wep_ctx = ctx;
+ }
+ m = m0;
+ left = m->m_pkthdr.len;
+ MGET(n, M_DONTWAIT, m->m_type);
+ n0 = n;
+ if (n == NULL)
+ goto fail;
+ M_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;
+ iv = ic->ic_iv;
+ /*
+ * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
+ * (B, 255, N) with 3 <= B < 8
+ */
+ if (iv >= 0x03ff00 &&
+ (iv & 0xf8ff00) == 0x00ff00)
+ iv += 0x000100;
+ ic->ic_iv = iv + 1;
+ /* put iv in little endian to prepare 802.11i */
+ ivp = mtod(n, u_int8_t *) + noff;
+ for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
+ ivp[i] = iv & 0xff;
+ iv >>= 8;
+ }
+ ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */
+ noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ } else {
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ ivp = mtod(m, u_int8_t *) + moff;
+ kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
+ moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ }
+ memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
+ memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
+ ic->ic_nw_keys[kid].wk_len);
+ arc4_setkey(ctx, keybuf,
+ IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
+
+ /* encrypt with calculating CRC */
+ crc = ~0;
+ while (left > 0) {
+ len = m->m_len - moff;
+ if (len == 0) {
+ m = m->m_next;
+ moff = 0;
+ continue;
+ }
+ if (len > n->m_len - noff) {
+ len = n->m_len - noff;
+ if (len == 0) {
+ MGET(n->m_next, M_DONTWAIT, n->m_type);
+ if (n->m_next == NULL)
+ goto fail;
+ n = n->m_next;
+ n->m_len = MLEN;
+ if (left >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ noff = 0;
+ continue;
+ }
+ }
+ if (len > left)
+ len = left;
+ arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
+ mtod(m, caddr_t) + moff, len);
+ if (txflag)
+ crc = ieee80211_crc_update(crc,
+ mtod(m, u_int8_t *) + moff, len);
+ else
+ crc = ieee80211_crc_update(crc,
+ mtod(n, u_int8_t *) + noff, len);
+ left -= len;
+ moff += len;
+ noff += len;
+ }
+ crc = ~crc;
+ if (txflag) {
+ *(u_int32_t *)crcbuf = htole32(crc);
+ if (n->m_len >= noff + sizeof(crcbuf))
+ n->m_len = noff + sizeof(crcbuf);
+ else {
+ n->m_len = noff;
+ MGET(n->m_next, M_DONTWAIT, n->m_type);
+ if (n->m_next == NULL)
+ goto fail;
+ n = n->m_next;
+ n->m_len = sizeof(crcbuf);
+ noff = 0;
+ }
+ arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
+ sizeof(crcbuf));
+ } else {
+ n->m_len = noff;
+ for (noff = 0; noff < sizeof(crcbuf); noff += len) {
+ len = sizeof(crcbuf) - noff;
+ if (len > m->m_len - moff)
+ len = m->m_len - moff;
+ if (len > 0)
+ arc4_encrypt(ctx, crcbuf + noff,
+ mtod(m, caddr_t) + moff, len);
+ m = m->m_next;
+ moff = 0;
+ }
+ if (crc != le32toh(*(u_int32_t *)crcbuf)) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug) {
+ if_printf(ifp, "decrypt CRC error\n");
+ if (ieee80211_debug > 1)
+ ieee80211_dump_pkt(n0->m_data,
+ n0->m_len, -1, -1);
+ }
+#endif
+ goto fail;
+ }
+ }
+ m_freem(m0);
+ return n0;
+
+ fail:
+ m_freem(m0);
+ m_freem(n0);
+ return NULL;
+}
+
+/*
+ * CRC 32 -- routine from RFC 2083
+ */
+
+/* Table of CRCs of all 8-bit messages */
+static u_int32_t ieee80211_crc_table[256];
+
+/* Make the table for a fast CRC. */
+static void
+ieee80211_crc_init(void)
+{
+ u_int32_t c;
+ int n, k;
+
+ for (n = 0; n < 256; n++) {
+ c = (u_int32_t)n;
+ for (k = 0; k < 8; k++) {
+ if (c & 1)
+ c = 0xedb88320UL ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ ieee80211_crc_table[n] = c;
+ }
+}
+
+/*
+ * Update a running CRC with the bytes buf[0..len-1]--the CRC
+ * should be initialized to all 1's, and the transmitted value
+ * is the 1's complement of the final running CRC
+ */
+
+static u_int32_t
+ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len)
+{
+ u_int8_t *endbuf;
+
+ for (endbuf = buf + len; buf < endbuf; buf++)
+ crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+ return crc;
+}
+
+/*
+ * convert IEEE80211 rate value to ifmedia subtype.
+ * ieee80211 rate is in unit of 0.5Mbps.
+ */
+
+int
+ieee80211_rate2media(int rate, enum ieee80211_phytype phytype)
+{
+ int mword;
+
+ mword = 0;
+ switch (phytype) {
+ case IEEE80211_T_FH:
+ switch (rate & IEEE80211_RATE_VAL) {
+ case 0:
+ mword = IFM_AUTO;
+ break;
+ case 2:
+ mword = IFM_IEEE80211_FH1;
+ break;
+ case 4:
+ mword = IFM_IEEE80211_FH2;
+ break;
+ default:
+ mword = IFM_NONE;
+ break;
+ }
+ break;
+
+ case IEEE80211_T_DS:
+ switch (rate & IEEE80211_RATE_VAL) {
+ case 0:
+ mword = IFM_AUTO;
+ break;
+ case 2:
+ mword = IFM_IEEE80211_DS1;
+ break;
+ case 4:
+ mword = IFM_IEEE80211_DS2;
+ break;
+ case 11:
+ mword = IFM_IEEE80211_DS5;
+ break;
+ case 22:
+ mword = IFM_IEEE80211_DS11;
+ break;
+ default:
+ mword = IFM_NONE;
+ break;
+ }
+ break;
+
+ case IEEE80211_T_OFDM:
+ switch (rate & IEEE80211_RATE_VAL) {
+ case 0:
+ mword = IFM_AUTO;
+ break;
+ case 12:
+ mword = IFM_IEEE80211_ODFM6;
+ break;
+ case 18:
+ mword = IFM_IEEE80211_ODFM9;
+ break;
+ case 24:
+ mword = IFM_IEEE80211_ODFM12;
+ break;
+ case 36:
+ mword = IFM_IEEE80211_ODFM18;
+ break;
+ case 48:
+ mword = IFM_IEEE80211_ODFM24;
+ break;
+ case 72:
+ mword = IFM_IEEE80211_ODFM36;
+ break;
+ case 108:
+ mword = IFM_IEEE80211_ODFM54;
+ break;
+ case 144:
+ mword = IFM_IEEE80211_ODFM72;
+ 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;
+ struct wi_sigcache wsc;
+
+ error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
+ if (error)
+ return error;
+ wreq.wi_len = 0;
+ switch (wreq.wi_type) {
+ case WI_RID_SERIALNO:
+ /* nothing appropriate */
+ break;
+ case WI_RID_NODENAME:
+ strcpy((char *)&wreq.wi_val[1], hostname);
+ wreq.wi_val[0] = htole16(strlen(hostname));
+ wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
+ break;
+ case WI_RID_CURRENT_SSID:
+ if (ic->ic_state != IEEE80211_S_RUN) {
+ wreq.wi_val[0] = 0;
+ wreq.wi_len = 1;
+ break;
+ }
+ wreq.wi_val[0] = htole16(ic->ic_bss.ni_esslen);
+ memcpy(&wreq.wi_val[1], ic->ic_bss.ni_essid,
+ ic->ic_bss.ni_esslen);
+ wreq.wi_len = (1 + ic->ic_bss.ni_esslen + 1) / 2;
+ break;
+ case WI_RID_OWN_SSID:
+ case WI_RID_DESIRED_SSID:
+ wreq.wi_val[0] = htole16(ic->ic_des_esslen);
+ memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
+ wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
+ break;
+ case WI_RID_CURRENT_BSSID:
+ if (ic->ic_state == IEEE80211_S_RUN)
+ IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss.ni_bssid);
+ else
+ memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
+ wreq.wi_len = IEEE80211_ADDR_LEN / 2;
+ break;
+ case WI_RID_CHANNEL_LIST:
+ memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
+ /*
+ * Since channel 0 is not available for DS, channel 1
+ * is assigned to LSB on WaveLAN.
+ */
+ if (ic->ic_phytype == IEEE80211_T_DS)
+ i = 1;
+ else
+ i = 0;
+ for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
+ if (isset(ic->ic_chan_active, i)) {
+ setbit((u_int8_t *)wreq.wi_val, j);
+ wreq.wi_len = j / 16 + 1;
+ }
+ }
+ break;
+ case WI_RID_OWN_CHNL:
+ wreq.wi_val[0] = htole16(ic->ic_ibss_chan);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_CURRENT_CHAN:
+ wreq.wi_val[0] = htole16(ic->ic_bss.ni_chan);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_COMMS_QUALITY:
+ wreq.wi_val[0] = 0; /* quality */
+ wreq.wi_val[1] = htole16(ic->ic_bss.ni_rssi); /* signal */
+ wreq.wi_val[2] = 0; /* noise */
+ wreq.wi_len = 3;
+ break;
+ case WI_RID_PROMISC:
+ wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_PORTTYPE:
+ wreq.wi_val[0] = htole16(ic->ic_opmode);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_MAC_NODE:
+ IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr);
+ wreq.wi_len = IEEE80211_ADDR_LEN / 2;
+ break;
+ case WI_RID_TX_RATE:
+ if (ic->ic_fixed_rate == -1)
+ wreq.wi_val[0] = 0; /* auto */
+ else
+ wreq.wi_val[0] = htole16(
+ (ic->ic_sup_rates[ic->ic_fixed_rate] &
+ IEEE80211_RATE_VAL) / 2);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_CUR_TX_RATE:
+ wreq.wi_val[0] = htole16(
+ (ic->ic_bss.ni_rates[ic->ic_bss.ni_txrate] &
+ IEEE80211_RATE_VAL) / 2);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_RTS_THRESH:
+ wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: RTS */
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_CREATE_IBSS:
+ wreq.wi_val[0] =
+ htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_MICROWAVE_OVEN:
+ wreq.wi_val[0] = 0; /* no ... not supported */
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_ROAMING_MODE:
+ wreq.wi_val[0] = htole16(1); /* enabled ... not supported */
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_SYSTEM_SCALE:
+ wreq.wi_val[0] = htole16(1); /* low density ... not supp */
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_PM_ENABLED:
+ wreq.wi_val[0] =
+ htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_MAX_SLEEP:
+ wreq.wi_val[0] = htole16(ic->ic_lintval);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_CUR_BEACON_INT:
+ wreq.wi_val[0] = htole16(ic->ic_bss.ni_intval);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_WEP_AVAIL:
+ wreq.wi_val[0] =
+ htole16((ic->ic_flags & IEEE80211_F_HASWEP) ? 1 : 0);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_CNFAUTHMODE:
+ wreq.wi_val[0] = htole16(1); /* TODO: open system only */
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_ENCRYPTION:
+ wreq.wi_val[0] =
+ htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_TX_CRYPT_KEY:
+ wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
+ wreq.wi_len = 1;
+ break;
+ case WI_RID_DEFLT_CRYPT_KEYS:
+ keys = (struct wi_ltv_keys *)&wreq;
+ /* do not show keys to non-root user */
+ error = suser(curthread);
+ if (error) {
+ memset(keys, 0, sizeof(*keys));
+ error = 0;
+ break;
+ }
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ keys->wi_keys[i].wi_keylen =
+ htole16(ic->ic_nw_keys[i].wk_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;
+ 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 = 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;
+ case WI_RID_SCAN_APS:
+ error = EINVAL;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (error == 0) {
+ wreq.wi_len++;
+ error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
+ }
+ return error;
+}
+
+int
+ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ieee80211com *ic = (void *)ifp;
+ int i, j, len, error;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct wi_ltv_keys *keys;
+ struct wi_req wreq;
+ u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
+
+ error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
+ if (error)
+ return error;
+ if (wreq.wi_len-- < 1)
+ return EINVAL;
+ switch (wreq.wi_type) {
+ case WI_RID_SERIALNO:
+ case WI_RID_NODENAME:
+ return EPERM;
+ case WI_RID_CURRENT_SSID:
+ return EPERM;
+ case WI_RID_OWN_SSID:
+ case WI_RID_DESIRED_SSID:
+ len = le16toh(wreq.wi_val[0]);
+ if (wreq.wi_len < (1 + len + 1) / 2)
+ return EINVAL;
+ if (len > IEEE80211_NWID_LEN)
+ return EINVAL;
+ ic->ic_des_esslen = len;
+ memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
+ memcpy(ic->ic_des_essid, &wreq.wi_val[1], len);
+ error = ENETRESET;
+ break;
+ case WI_RID_CURRENT_BSSID:
+ return EPERM;
+ case WI_RID_OWN_CHNL:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ i = le16toh(wreq.wi_val[0]);
+ if (i < 0 ||
+ i > IEEE80211_CHAN_MAX ||
+ isclr(ic->ic_chan_active, i))
+ return EINVAL;
+ ic->ic_ibss_chan = i;
+ if (ic->ic_flags & IEEE80211_F_SIBSS)
+ error = ENETRESET;
+ break;
+ case WI_RID_CURRENT_CHAN:
+ return EPERM;
+ case WI_RID_COMMS_QUALITY:
+ return EPERM;
+ case WI_RID_PROMISC:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (ifp->if_flags & IFF_PROMISC) {
+ if (wreq.wi_val[0] == 0) {
+ ifp->if_flags &= ~IFF_PROMISC;
+ error = ENETRESET;
+ }
+ } else {
+ if (wreq.wi_val[0] != 0) {
+ ifp->if_flags |= IFF_PROMISC;
+ error = ENETRESET;
+ }
+ }
+ break;
+ case WI_RID_PORTTYPE:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ switch (le16toh(wreq.wi_val[0])) {
+ case IEEE80211_M_STA:
+ break;
+ case IEEE80211_M_IBSS:
+ if (!(ic->ic_flags & IEEE80211_F_HASIBSS))
+ return EINVAL;
+ break;
+ case IEEE80211_M_AHDEMO:
+ if (ic->ic_phytype != IEEE80211_T_DS ||
+ !(ic->ic_flags & IEEE80211_F_HASAHDEMO))
+ return EINVAL;
+ break;
+ case IEEE80211_M_HOSTAP:
+ if (!(ic->ic_flags & IEEE80211_F_HASHOSTAP))
+ return EINVAL;
+ break;
+ default:
+ return EINVAL;
+ }
+ if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
+ ic->ic_opmode = le16toh(wreq.wi_val[0]);
+ error = ENETRESET;
+ }
+ break;
+#if 0
+ 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;
+#endif
+ case WI_RID_TX_RATE:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (wreq.wi_val[0] == 0) {
+ /* auto */
+ ic->ic_fixed_rate = -1;
+ break;
+ }
+ for (i = 0; i < IEEE80211_RATE_SIZE; i++) {
+ if (le16toh(wreq.wi_val[0]) ==
+ (ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) / 2)
+ break;
+ }
+ if (i == IEEE80211_RATE_SIZE)
+ return EINVAL;
+ ic->ic_fixed_rate = i;
+ error = ENETRESET;
+ break;
+ case WI_RID_CUR_TX_RATE:
+ return EPERM;
+ break;
+ case WI_RID_RTS_THRESH:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
+ return EINVAL; /* TODO: RTS */
+ break;
+ case WI_RID_CREATE_IBSS:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (wreq.wi_val[0] != 0) {
+ if ((ic->ic_flags & IEEE80211_F_HASIBSS) == 0)
+ return EINVAL;
+ if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
+ ic->ic_flags |= IEEE80211_F_IBSSON;
+ if (ic->ic_opmode == IEEE80211_M_IBSS &&
+ ic->ic_state == IEEE80211_S_SCAN)
+ error = ENETRESET;
+ }
+ } else {
+ if (ic->ic_flags & IEEE80211_F_IBSSON) {
+ ic->ic_flags &= ~IEEE80211_F_IBSSON;
+ if (ic->ic_flags & IEEE80211_F_SIBSS) {
+ ic->ic_flags &= ~IEEE80211_F_SIBSS;
+ error = ENETRESET;
+ }
+ }
+ }
+ break;
+ case WI_RID_MICROWAVE_OVEN:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (wreq.wi_val[0] != 0)
+ return EINVAL; /* not supported */
+ break;
+ case WI_RID_ROAMING_MODE:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (le16toh(wreq.wi_val[0]) != 1)
+ return EINVAL; /* not supported */
+ break;
+ case WI_RID_SYSTEM_SCALE:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (le16toh(wreq.wi_val[0]) != 1)
+ return EINVAL; /* not supported */
+ break;
+ case WI_RID_PM_ENABLED:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (wreq.wi_val[0] != 0) {
+ if ((ic->ic_flags & IEEE80211_F_HASPMGT) == 0)
+ return EINVAL;
+ if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+ ic->ic_flags |= IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ } else {
+ if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ ic->ic_flags &= ~IEEE80211_F_PMGTON;
+ error = ENETRESET;
+ }
+ }
+ break;
+ case WI_RID_MAX_SLEEP:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ ic->ic_lintval = le16toh(wreq.wi_val[0]);
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
+ error = ENETRESET;
+ break;
+ case WI_RID_CUR_BEACON_INT:
+ return EPERM;
+ case WI_RID_WEP_AVAIL:
+ return EPERM;
+ case WI_RID_CNFAUTHMODE:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (le16toh(wreq.wi_val[0]) != 1)
+ return EINVAL; /* TODO: shared key auth */
+ break;
+ case WI_RID_ENCRYPTION:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ if (wreq.wi_val[0] != 0) {
+ if ((ic->ic_flags & IEEE80211_F_HASWEP) == 0)
+ return EINVAL;
+ if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
+ ic->ic_flags |= IEEE80211_F_WEPON;
+ error = ENETRESET;
+ }
+ } else {
+ if (ic->ic_flags & IEEE80211_F_WEPON) {
+ ic->ic_flags &= ~IEEE80211_F_WEPON;
+ error = ENETRESET;
+ }
+ }
+ break;
+ case WI_RID_TX_CRYPT_KEY:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ i = le16toh(wreq.wi_val[0]);
+ if (i >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ ic->ic_wep_txkey = i;
+ break;
+ case WI_RID_DEFLT_CRYPT_KEYS:
+ if (wreq.wi_len != sizeof(struct wi_ltv_keys) / 2)
+ return EINVAL;
+ keys = (struct wi_ltv_keys *)&wreq;
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ len = le16toh(keys->wi_keys[i].wi_keylen);
+ if (len != 0 && len < IEEE80211_WEP_KEYLEN)
+ return EINVAL;
+ if (len > sizeof(ic->ic_nw_keys[i].wk_key))
+ return EINVAL;
+ }
+ memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ len = le16toh(keys->wi_keys[i].wi_keylen);
+ ic->ic_nw_keys[i].wk_len = len;
+ memcpy(ic->ic_nw_keys[i].wk_key,
+ keys->wi_keys[i].wi_keydat, len);
+ }
+ error = ENETRESET;
+ break;
+ case WI_RID_MAX_DATALEN:
+ if (wreq.wi_len != 1)
+ return EINVAL;
+ len = le16toh(wreq.wi_val[0]);
+ if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
+ return EINVAL;
+ if (len != IEEE80211_MAX_LEN)
+ return EINVAL; /* TODO: fragment */
+ break;
+ case WI_RID_IFACE_STATS:
+ error = EPERM;
+ break;
+ case WI_RID_SCAN_APS:
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ break;
+ wreq.wi_len -= 2; /* XXX: tx rate? */
+ /* FALLTHRU */
+ case WI_RID_CHANNEL_LIST:
+ memset(chanlist, 0, sizeof(chanlist));
+ /*
+ * Since channel 0 is not available for DS, channel 1
+ * is assigned to LSB on WaveLAN.
+ */
+ if (ic->ic_phytype == IEEE80211_T_DS)
+ i = 1;
+ else
+ i = 0;
+ for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
+ if (j / 16 >= wreq.wi_len)
+ break;
+ if (isclr((u_int8_t *)wreq.wi_val, j))
+ continue;
+ if (isclr(ic->ic_chan_avail, i)) {
+ if (wreq.wi_type == WI_RID_CHANNEL_LIST)
+ error = EPERM;
+ else
+ continue;
+ }
+ setbit(chanlist, i);
+ }
+ if (error == EPERM && ic->ic_chancheck != NULL)
+ error = (*ic->ic_chancheck)(ic->ic_softc, chanlist);
+ if (error)
+ return error;
+ memcpy(ic->ic_chan_active, chanlist,
+ sizeof(ic->ic_chan_active));
+ if (isclr(chanlist, ic->ic_ibss_chan)) {
+ for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+ if (isset(chanlist, i)) {
+ ic->ic_ibss_chan = i;
+ break;
+ }
+ }
+ if (isclr(chanlist, ic->ic_bss.ni_chan))
+ ic->ic_bss.ni_chan = ic->ic_ibss_chan;
+ if (wreq.wi_type == WI_RID_CHANNEL_LIST)
+ error = ENETRESET;
+ else {
+ ic->ic_flags |= IEEE80211_F_SCANAP;
+ error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+/*
+ * 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);
OpenPOWER on IntegriCloud